Initial project import
This commit is contained in:
313
node_modules/@react-three/drei/core/Instances.js
generated
vendored
Normal file
313
node_modules/@react-three/drei/core/Instances.js
generated
vendored
Normal file
@@ -0,0 +1,313 @@
|
||||
import _extends from '@babel/runtime/helpers/esm/extends';
|
||||
import * as THREE from 'three';
|
||||
import * as React from 'react';
|
||||
import { extend, useFrame } from '@react-three/fiber';
|
||||
import { setUpdateRange } from '../helpers/deprecated.js';
|
||||
|
||||
function isFunctionChild(value) {
|
||||
return typeof value === 'function';
|
||||
}
|
||||
const _instanceLocalMatrix = /* @__PURE__ */new THREE.Matrix4();
|
||||
const _instanceWorldMatrix = /* @__PURE__ */new THREE.Matrix4();
|
||||
const _instanceIntersects = [];
|
||||
const _mesh = /* @__PURE__ */new THREE.Mesh();
|
||||
class PositionMesh extends THREE.Group {
|
||||
constructor() {
|
||||
super();
|
||||
this.color = new THREE.Color('white');
|
||||
this.instance = {
|
||||
current: undefined
|
||||
};
|
||||
this.instanceKey = {
|
||||
current: undefined
|
||||
};
|
||||
}
|
||||
|
||||
// This will allow the virtual instance have bounds
|
||||
get geometry() {
|
||||
var _this$instance$curren;
|
||||
return (_this$instance$curren = this.instance.current) == null ? void 0 : _this$instance$curren.geometry;
|
||||
}
|
||||
|
||||
// And this will allow the virtual instance to receive events
|
||||
raycast(raycaster, intersects) {
|
||||
const parent = this.instance.current;
|
||||
if (!parent) return;
|
||||
if (!parent.geometry || !parent.material) return;
|
||||
_mesh.geometry = parent.geometry;
|
||||
const matrixWorld = parent.matrixWorld;
|
||||
const instanceId = parent.userData.instances.indexOf(this.instanceKey);
|
||||
// If the instance wasn't found or exceeds the parents draw range, bail out
|
||||
if (instanceId === -1 || instanceId > parent.count) return;
|
||||
// calculate the world matrix for each instance
|
||||
parent.getMatrixAt(instanceId, _instanceLocalMatrix);
|
||||
_instanceWorldMatrix.multiplyMatrices(matrixWorld, _instanceLocalMatrix);
|
||||
// the mesh represents this single instance
|
||||
_mesh.matrixWorld = _instanceWorldMatrix;
|
||||
// raycast side according to instance material
|
||||
if (parent.material instanceof THREE.Material) _mesh.material.side = parent.material.side;else _mesh.material.side = parent.material[0].side;
|
||||
_mesh.raycast(raycaster, _instanceIntersects);
|
||||
// process the result of raycast
|
||||
for (let i = 0, l = _instanceIntersects.length; i < l; i++) {
|
||||
const intersect = _instanceIntersects[i];
|
||||
intersect.instanceId = instanceId;
|
||||
intersect.object = this;
|
||||
intersects.push(intersect);
|
||||
}
|
||||
_instanceIntersects.length = 0;
|
||||
}
|
||||
}
|
||||
const globalContext = /* @__PURE__ */React.createContext(null);
|
||||
const parentMatrix = /* @__PURE__ */new THREE.Matrix4();
|
||||
const instanceMatrix = /* @__PURE__ */new THREE.Matrix4();
|
||||
const tempMatrix = /* @__PURE__ */new THREE.Matrix4();
|
||||
const translation = /* @__PURE__ */new THREE.Vector3();
|
||||
const rotation = /* @__PURE__ */new THREE.Quaternion();
|
||||
const scale = /* @__PURE__ */new THREE.Vector3();
|
||||
const isInstancedBufferAttribute = attr => attr.isInstancedBufferAttribute;
|
||||
const Instance = /* @__PURE__ */React.forwardRef(({
|
||||
context,
|
||||
children,
|
||||
...props
|
||||
}, ref) => {
|
||||
React.useMemo(() => extend({
|
||||
PositionMesh
|
||||
}), []);
|
||||
const group = React.useRef(null);
|
||||
React.useImperativeHandle(ref, () => group.current, []);
|
||||
const {
|
||||
subscribe,
|
||||
getParent
|
||||
} = React.useContext(context || globalContext);
|
||||
React.useLayoutEffect(() => subscribe(group), []);
|
||||
return /*#__PURE__*/React.createElement("positionMesh", _extends({
|
||||
instance: getParent(),
|
||||
instanceKey: group,
|
||||
ref: group
|
||||
}, props), children);
|
||||
});
|
||||
const Instances = /* @__PURE__ */React.forwardRef(({
|
||||
context,
|
||||
children,
|
||||
range,
|
||||
limit = 1000,
|
||||
frames = Infinity,
|
||||
...props
|
||||
}, ref) => {
|
||||
const [{
|
||||
localContext,
|
||||
instance
|
||||
}] = React.useState(() => {
|
||||
const localContext = /*#__PURE__*/React.createContext(null);
|
||||
return {
|
||||
localContext,
|
||||
instance: /*#__PURE__*/React.forwardRef((props, ref) => /*#__PURE__*/React.createElement(Instance, _extends({
|
||||
context: localContext
|
||||
}, props, {
|
||||
ref: ref
|
||||
})))
|
||||
};
|
||||
});
|
||||
const parentRef = React.useRef(null);
|
||||
React.useImperativeHandle(ref, () => parentRef.current, []);
|
||||
const [instances, setInstances] = React.useState([]);
|
||||
const [[matrices, colors]] = React.useState(() => {
|
||||
const mArray = new Float32Array(limit * 16);
|
||||
for (let i = 0; i < limit; i++) tempMatrix.identity().toArray(mArray, i * 16);
|
||||
return [mArray, new Float32Array([...new Array(limit * 3)].map(() => 1))];
|
||||
});
|
||||
React.useEffect(() => {
|
||||
// We might be a frame too late? 🤷♂️
|
||||
parentRef.current.instanceMatrix.needsUpdate = true;
|
||||
});
|
||||
let iterations = 0;
|
||||
let count = 0;
|
||||
const attributes = React.useRef([]);
|
||||
React.useLayoutEffect(() => {
|
||||
attributes.current = Object.entries(parentRef.current.geometry.attributes).filter(([_name, value]) => isInstancedBufferAttribute(value));
|
||||
});
|
||||
useFrame(() => {
|
||||
if (frames === Infinity || iterations < frames) {
|
||||
parentRef.current.updateMatrix();
|
||||
parentRef.current.updateMatrixWorld();
|
||||
parentMatrix.copy(parentRef.current.matrixWorld).invert();
|
||||
count = Math.min(limit, range !== undefined ? range : limit, instances.length);
|
||||
parentRef.current.count = count;
|
||||
setUpdateRange(parentRef.current.instanceMatrix, {
|
||||
start: 0,
|
||||
count: count * 16
|
||||
});
|
||||
setUpdateRange(parentRef.current.instanceColor, {
|
||||
start: 0,
|
||||
count: count * 3
|
||||
});
|
||||
for (let i = 0; i < instances.length; i++) {
|
||||
const instance = instances[i].current;
|
||||
// Multiply the inverse of the InstancedMesh world matrix or else
|
||||
// Instances will be double-transformed if <Instances> isn't at identity
|
||||
instance.matrixWorld.decompose(translation, rotation, scale);
|
||||
instanceMatrix.compose(translation, rotation, scale).premultiply(parentMatrix);
|
||||
instanceMatrix.toArray(matrices, i * 16);
|
||||
parentRef.current.instanceMatrix.needsUpdate = true;
|
||||
instance.color.toArray(colors, i * 3);
|
||||
parentRef.current.instanceColor.needsUpdate = true;
|
||||
}
|
||||
iterations++;
|
||||
}
|
||||
});
|
||||
const api = React.useMemo(() => ({
|
||||
getParent: () => parentRef,
|
||||
subscribe: ref => {
|
||||
setInstances(instances => [...instances, ref]);
|
||||
return () => setInstances(instances => instances.filter(item => item.current !== ref.current));
|
||||
}
|
||||
}), []);
|
||||
return /*#__PURE__*/React.createElement("instancedMesh", _extends({
|
||||
userData: {
|
||||
instances,
|
||||
limit,
|
||||
frames
|
||||
},
|
||||
matrixAutoUpdate: false,
|
||||
ref: parentRef,
|
||||
args: [null, null, 0],
|
||||
raycast: () => null
|
||||
}, props), /*#__PURE__*/React.createElement("instancedBufferAttribute", {
|
||||
attach: "instanceMatrix",
|
||||
args: [matrices, 16],
|
||||
usage: THREE.DynamicDrawUsage
|
||||
}), /*#__PURE__*/React.createElement("instancedBufferAttribute", {
|
||||
attach: "instanceColor",
|
||||
args: [colors, 3],
|
||||
usage: THREE.DynamicDrawUsage
|
||||
}), isFunctionChild(children) ? /*#__PURE__*/React.createElement(localContext.Provider, {
|
||||
value: api
|
||||
}, children(instance)) : context ? /*#__PURE__*/React.createElement(context.Provider, {
|
||||
value: api
|
||||
}, children) : /*#__PURE__*/React.createElement(globalContext.Provider, {
|
||||
value: api
|
||||
}, children));
|
||||
});
|
||||
// TODO: make this non-recursive and type-safe
|
||||
const Merged = /* @__PURE__ */React.forwardRef(function Merged({
|
||||
meshes,
|
||||
children,
|
||||
...props
|
||||
}, ref) {
|
||||
const isArray = Array.isArray(meshes);
|
||||
// Filter out meshes from collections, which may contain non-meshes
|
||||
// @ts-expect-error
|
||||
if (!isArray) for (const key of Object.keys(meshes)) if (!meshes[key].isMesh) delete meshes[key];
|
||||
const render = args => isArray ?
|
||||
// @ts-expect-error
|
||||
children(...args) : children(
|
||||
// @ts-expect-error
|
||||
Object.keys(meshes)
|
||||
// @ts-expect-error
|
||||
.filter(key => meshes[key].isMesh).reduce((acc, key, i) => ({
|
||||
...acc,
|
||||
[key]: args[i]
|
||||
}), {}));
|
||||
|
||||
// @ts-expect-error
|
||||
const components = (isArray ? meshes : Object.values(meshes)).map(({
|
||||
geometry,
|
||||
material
|
||||
}) => /*#__PURE__*/React.createElement(Instances, _extends({
|
||||
key: geometry.uuid,
|
||||
geometry: geometry,
|
||||
material: material
|
||||
}, props)));
|
||||
return /*#__PURE__*/React.createElement("group", {
|
||||
ref: ref
|
||||
}, renderRecursive(render, components));
|
||||
});
|
||||
|
||||
// https://github.com/jamesplease/react-composer
|
||||
function renderRecursive(render, components, results = []) {
|
||||
// Once components is exhausted, we can render out the results array.
|
||||
if (!components[0]) {
|
||||
return render(results);
|
||||
}
|
||||
|
||||
// Continue recursion for remaining items.
|
||||
// results.concat([value]) ensures [...results, value] instead of [...results, ...value]
|
||||
function nextRender(value) {
|
||||
return renderRecursive(render, components.slice(1), results.concat([value]));
|
||||
}
|
||||
|
||||
// Each props.components entry is either an element or function [element factory]
|
||||
return typeof components[0] === 'function' ?
|
||||
// When it is a function, produce an element by invoking it with "render component values".
|
||||
components[0]({
|
||||
results,
|
||||
render: nextRender
|
||||
}) :
|
||||
/*#__PURE__*/
|
||||
// When it is an element, enhance the element's props with the render prop.
|
||||
React.cloneElement(components[0], {
|
||||
children: nextRender
|
||||
});
|
||||
}
|
||||
|
||||
/** Idea and implementation for global instances and instanced attributes by
|
||||
/* Matias Gonzalez Fernandez https://x.com/matiNotFound
|
||||
/* and Paul Henschel https://x.com/0xca0a
|
||||
*/
|
||||
function createInstances() {
|
||||
const context = /*#__PURE__*/React.createContext(null);
|
||||
return [/*#__PURE__*/React.forwardRef((props, fref) => /*#__PURE__*/React.createElement(Instances, _extends({
|
||||
ref: fref,
|
||||
context: context
|
||||
}, props))), /*#__PURE__*/React.forwardRef((props, fref) => /*#__PURE__*/React.createElement(Instance, _extends({
|
||||
ref: fref,
|
||||
context: context
|
||||
}, props)))];
|
||||
}
|
||||
const InstancedAttribute = /*#__PURE__*/React.forwardRef(({
|
||||
name,
|
||||
defaultValue,
|
||||
normalized,
|
||||
usage = THREE.DynamicDrawUsage
|
||||
}, fref) => {
|
||||
const ref = React.useRef(null);
|
||||
React.useImperativeHandle(fref, () => ref.current, []);
|
||||
React.useLayoutEffect(() => {
|
||||
const parent = ref.current.__r3f.parent.object;
|
||||
parent.geometry.attributes[name] = ref.current;
|
||||
const value = Array.isArray(defaultValue) ? defaultValue : [defaultValue];
|
||||
const array = Array.from({
|
||||
length: parent.userData.limit
|
||||
}, () => value).flat();
|
||||
ref.current.array = new Float32Array(array);
|
||||
ref.current.itemSize = value.length;
|
||||
// @ts-expect-error
|
||||
ref.current.count = array.length / ref.current.itemSize;
|
||||
return () => {
|
||||
delete parent.geometry.attributes[name];
|
||||
};
|
||||
}, [name]);
|
||||
let iterations = 0;
|
||||
useFrame(() => {
|
||||
const parent = ref.current.__r3f.parent.object;
|
||||
if (parent.userData.frames === Infinity || iterations < parent.userData.frames) {
|
||||
for (let i = 0; i < parent.userData.instances.length; i++) {
|
||||
const instance = parent.userData.instances[i].current;
|
||||
const value = instance[name];
|
||||
if (value !== undefined) {
|
||||
ref.current.set(Array.isArray(value) ? value : typeof value.toArray === 'function' ? value.toArray() : [value], i * ref.current.itemSize);
|
||||
ref.current.needsUpdate = true;
|
||||
}
|
||||
}
|
||||
iterations++;
|
||||
}
|
||||
});
|
||||
// @ts-expect-error we're abusing three API here by mutating immutable args
|
||||
return /*#__PURE__*/React.createElement("instancedBufferAttribute", {
|
||||
ref: ref,
|
||||
usage: usage,
|
||||
normalized: normalized
|
||||
});
|
||||
});
|
||||
|
||||
export { Instance, InstancedAttribute, Instances, Merged, PositionMesh, createInstances };
|
||||
Reference in New Issue
Block a user