@react-three/csg
Version:
Constructive solid geometry for React
153 lines (140 loc) • 5.01 kB
JavaScript
import _extends from '@babel/runtime/helpers/esm/extends';
import * as React from 'react';
import { extend } from '@react-three/fiber';
import { Evaluator, ADDITION, Brush, SUBTRACTION, REVERSE_SUBTRACTION, DIFFERENCE, INTERSECTION } from 'three-bvh-csg';
const TYPES = {
subtraction: SUBTRACTION,
reverseSubtraction: REVERSE_SUBTRACTION,
addition: ADDITION,
difference: DIFFERENCE,
intersection: INTERSECTION
};
function dispose(geometry) {
geometry.dispose();
geometry.attributes = {};
geometry.groups = [];
geometry.boundsTree = geometry.index = geometry.boundingBox = geometry.boundingSphere = null;
geometry.drawRange = {
start: 0,
count: Infinity
};
}
function resolve(op) {
let currentOp = null;
if (op instanceof Brush) {
op.updateMatrixWorld();
currentOp = op;
} else {
op.traverse(obj => {
obj.updateMatrixWorld();
if (!currentOp && obj instanceof Brush) currentOp = obj;
});
}
return currentOp;
}
const csgContext = /*#__PURE__*/React.createContext(null);
const Geometry = /*#__PURE__*/React.forwardRef(({
children,
computeVertexNormals = false,
useGroups = false,
consolidateGroups = false,
showOperations = false
}, fref) => {
const geo = React.useRef(null);
const operations = React.useRef(null);
const ev = React.useMemo(() => Object.assign(new Evaluator(), {
useGroups,
consolidateGroups
}), [useGroups, consolidateGroups]);
const update = React.useCallback(() => {
try {
const ops = operations.current.children.slice();
if (ops.length > 0) {
// Dispose old geometry
dispose(geo.current); // Set the ops groups matrix to identiy
operations.current.matrixWorld.identity();
let root = resolve(ops.shift());
if (root) {
var _geo$current, _geo$current$__r3f, _geo$current$__r3f$pa, _geo$current$__r3f$pa2;
while (ops.length) {
const op = resolve(ops.shift());
if (op) root = ev.evaluate(root, op, TYPES[op.operator] || ADDITION);
}
;
geo.current.boundsTree = root.geometry.boundsTree;
geo.current.index = root.geometry.index;
geo.current.attributes = root.geometry.attributes;
geo.current.groups = root.geometry.groups;
geo.current.drawRange = root.geometry.drawRange;
if (ev.useGroups && (_geo$current = geo.current) != null && (_geo$current$__r3f = _geo$current.__r3f) != null && (_geo$current$__r3f$pa = _geo$current$__r3f.parent) != null && (_geo$current$__r3f$pa2 = _geo$current$__r3f$pa.object) != null && _geo$current$__r3f$pa2.material) geo.current.__r3f.parent.object.material = root.material;
if (computeVertexNormals) geo.current.computeVertexNormals();
}
}
} catch (e) {
console.log(e);
}
}, [computeVertexNormals, ev]);
const api = React.useMemo(() => ({
computeVertexNormals,
showOperations,
useGroups,
consolidateGroups,
update
}), [computeVertexNormals, showOperations, useGroups, consolidateGroups]);
React.useLayoutEffect(() => void update());
React.useImperativeHandle(fref, () => ({
geometry: geo.current,
operations: operations.current,
...api
}), [api]);
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("group", {
matrixAutoUpdate: false,
ref: operations
}, /*#__PURE__*/React.createElement(csgContext.Provider, {
value: api
}, children)), /*#__PURE__*/React.createElement("bufferGeometry", {
ref: geo
}));
});
const Base = /*#__PURE__*/React.forwardRef(({
showOperation = false,
operator = 'addition',
...props
}, fref) => {
extend({
Brush: Brush
});
const {
showOperations
} = React.useContext(csgContext);
return /*#__PURE__*/React.createElement("brush", _extends({
operator: operator,
raycast: () => null,
visible: showOperation || showOperations,
ref: fref
}, props));
});
const Addition = /*#__PURE__*/React.forwardRef((props, fref) => /*#__PURE__*/React.createElement(Base, _extends({
ref: fref,
operator: "addition"
}, props)));
const Subtraction = /*#__PURE__*/React.forwardRef((props, fref) => /*#__PURE__*/React.createElement(Base, _extends({
ref: fref,
operator: "subtraction"
}, props)));
const ReverseSubtraction = /*#__PURE__*/React.forwardRef((props, fref) => /*#__PURE__*/React.createElement(Base, _extends({
ref: fref,
operator: "reverseSubtraction"
}, props)));
const Difference = /*#__PURE__*/React.forwardRef((props, fref) => /*#__PURE__*/React.createElement(Base, _extends({
ref: fref,
operator: "difference"
}, props)));
const Intersection = /*#__PURE__*/React.forwardRef((props, fref) => /*#__PURE__*/React.createElement(Base, _extends({
ref: fref,
operator: "intersection"
}, props)));
function useCSG() {
return React.useContext(csgContext);
}
export { Addition, Base, Difference, Geometry, Intersection, ReverseSubtraction, Subtraction, useCSG };