@react-three/rapier 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@ import { useAsset } from 'use-asset';
3
3
  import { useFrame } from '@react-three/fiber';
4
4
  import { ColliderDesc, CoefficientCombineRule, ActiveEvents, EventQueue, ShapeType } from '@dimforge/rapier3d-compat';
5
5
  export { CoefficientCombineRule, Collider as RapierCollider, RigidBody as RapierRigidBody } from '@dimforge/rapier3d-compat';
6
- import { Vector3, Quaternion, Object3D, Euler, CylinderBufferGeometry, BufferGeometry, BufferAttribute, SphereBufferGeometry, BoxBufferGeometry } from 'three';
6
+ import { Quaternion, Euler, Vector3, Object3D, CylinderBufferGeometry, BufferGeometry, BufferAttribute, SphereBufferGeometry, BoxBufferGeometry } from 'three';
7
7
 
8
8
  const vectorArrayToObject = arr => {
9
9
  const [x, y, z] = arr;
@@ -88,7 +88,8 @@ const createColliderFromOptions = (options, world, rigidBody, scale = {
88
88
  const collider = world.createCollider(colliderDesc, rigidBody);
89
89
  return collider;
90
90
  };
91
- const createCollidersFromChildren = (object, rigidBody, type, world, hasCollisionEvents = false) => {
91
+ const createCollidersFromChildren = (object, rigidBody, options, world) => {
92
+ const hasCollisionEvents = !!(options.onCollisionEnter || options.onCollisionExit);
92
93
  const colliders = [];
93
94
  let desc;
94
95
  let offset = new Vector3();
@@ -110,7 +111,7 @@ const createCollidersFromChildren = (object, rigidBody, type, world, hasCollisio
110
111
  } = new Quaternion().setFromEuler(child.rotation);
111
112
  const scale = child.getWorldScale(new Vector3());
112
113
 
113
- switch (type) {
114
+ switch (options.colliders) {
114
115
  case "cuboid":
115
116
  {
116
117
  geometry.computeBoundingBox();
@@ -161,11 +162,9 @@ const createCollidersFromChildren = (object, rigidBody, type, world, hasCollisio
161
162
  z: rz,
162
163
  w: rw
163
164
  });
164
-
165
- if (hasCollisionEvents) {
166
- desc.setActiveEvents(ActiveEvents.COLLISION_EVENTS);
167
- }
168
-
165
+ if (hasCollisionEvents) desc.setActiveEvents(ActiveEvents.COLLISION_EVENTS);
166
+ if (Number.isFinite(options.friction)) desc.setFriction(options.friction);
167
+ if (Number.isFinite(options.restitution)) desc.setRestitution(options.restitution);
169
168
  const collider = world.createCollider(desc, rigidBody);
170
169
  colliders.push(collider);
171
170
  }
@@ -183,6 +182,11 @@ const scaleVertices = (vertices, scale) => {
183
182
 
184
183
  return scaledVerts;
185
184
  };
185
+ const quaternion = new Quaternion();
186
+ const euler = new Euler();
187
+ const vector3ToQuaternion = v => {
188
+ return quaternion.setFromEuler(euler.setFromVector3(v));
189
+ };
186
190
 
187
191
  function _defineProperty(obj, key, value) {
188
192
  if (key in obj) {
@@ -294,12 +298,26 @@ const createRigidBodyApi = ref => {
294
298
  },
295
299
 
296
300
  setAngvel: velocity => ref.current().setAngvel(velocity, true),
297
- setNextKinematicRotation: rotation => ref.current().setNextKinematicRotation(_objectSpread2(_objectSpread2({}, rotation), {}, {
298
- w: 1
299
- })),
301
+ setNextKinematicRotation: ({
302
+ x,
303
+ y,
304
+ z
305
+ }) => {
306
+ const q = vector3ToQuaternion(new Vector3(x, y, z));
307
+ ref.current().setNextKinematicRotation({
308
+ x: q.x,
309
+ y: q.y,
310
+ z: q.z,
311
+ w: q.w
312
+ });
313
+ },
300
314
  setNextKinematicTranslation: translation => ref.current().setNextKinematicTranslation(translation),
301
315
  resetForces: () => ref.current().resetForces(true),
302
- resetTorques: () => ref.current().resetTorques(true)
316
+ resetTorques: () => ref.current().resetTorques(true),
317
+ lockRotations: locked => ref.current().lockRotations(locked, true),
318
+ lockTranslations: locked => ref.current().lockTranslations(locked, true),
319
+ setEnabledRotations: (x, y, z) => ref.current().setEnabledRotations(x, y, z, true),
320
+ setEnabledTranslations: (x, y, z) => ref.current().setEnabledTranslations(x, y, z, true)
303
321
  };
304
322
  }; // TODO: Flesh this out
305
323
 
@@ -324,7 +342,16 @@ const createWorldApi = ref => {
324
342
  removeCollider: collider => ref.current().removeCollider(collider, true),
325
343
  createImpulseJoint: (params, rigidBodyA, rigidBodyB) => ref.current().createImpulseJoint(params, rigidBodyA, rigidBodyB, true),
326
344
  removeImpulseJoint: joint => ref.current().removeImpulseJoint(joint, true),
327
- forEachCollider: callback => ref.current().forEachCollider(callback)
345
+ forEachCollider: callback => ref.current().forEachCollider(callback),
346
+ setGravity: ({
347
+ x,
348
+ y,
349
+ z
350
+ }) => ref.current().gravity = {
351
+ x,
352
+ y,
353
+ z
354
+ }
328
355
  };
329
356
  }; // TODO: Broken currently, waiting for Rapier3D to fix
330
357
 
@@ -352,7 +379,8 @@ const importRapier = async () => {
352
379
  const Physics = ({
353
380
  colliders: _colliders = 'cuboid',
354
381
  gravity: _gravity = [0, -9.81, 0],
355
- children
382
+ children,
383
+ timeStep: _timeStep = 'vary'
356
384
  }) => {
357
385
  const rapier = useAsset(importRapier);
358
386
  const worldRef = useRef();
@@ -377,7 +405,15 @@ const Physics = ({
377
405
  worldRef.current = undefined;
378
406
  }
379
407
  };
380
- }, []);
408
+ }, []); // Update gravity
409
+
410
+ useEffect(() => {
411
+ const world = worldRef.current;
412
+
413
+ if (world) {
414
+ world.gravity = vectorArrayToObject(_gravity);
415
+ }
416
+ }, [_gravity]);
381
417
  const time = useRef(performance.now());
382
418
  useFrame(context => {
383
419
  const world = worldRef.current;
@@ -386,7 +422,13 @@ const Physics = ({
386
422
 
387
423
  const now = performance.now();
388
424
  const delta = Math.min(100, now - time.current);
389
- world.timestep = delta / 1000;
425
+
426
+ if (_timeStep === 'vary') {
427
+ world.timestep = delta / 1000;
428
+ } else {
429
+ world.timestep = _timeStep;
430
+ }
431
+
390
432
  world.step(eventQueue); // Update meshes
391
433
 
392
434
  rigidBodyMeshes.forEach((mesh, handle) => {
@@ -515,7 +557,7 @@ const useRigidBody = (options = {}) => {
515
557
  const rigidBodyRef = useRef();
516
558
  const getRigidBodyRef = useRef(() => {
517
559
  if (!rigidBodyRef.current) {
518
- var _options$linearVeloci, _options$angularVeloc, _options$gravityScale, _options$canSleep, _options$ccd;
560
+ var _options$linearVeloci, _options$angularVeloc, _options$gravityScale, _options$canSleep, _options$ccd, _options$enabledRotat, _options$enabledTrans;
519
561
 
520
562
  const type = rigidBodyTypeFromString((options === null || options === void 0 ? void 0 : options.type) || "dynamic");
521
563
  const [lvx, lvy, lvz] = (_options$linearVeloci = options === null || options === void 0 ? void 0 : options.linearVelocity) !== null && _options$linearVeloci !== void 0 ? _options$linearVeloci : [0, 0, 0];
@@ -523,11 +565,15 @@ const useRigidBody = (options = {}) => {
523
565
  const gravityScale = (_options$gravityScale = options === null || options === void 0 ? void 0 : options.gravityScale) !== null && _options$gravityScale !== void 0 ? _options$gravityScale : 1;
524
566
  const canSleep = (_options$canSleep = options === null || options === void 0 ? void 0 : options.canSleep) !== null && _options$canSleep !== void 0 ? _options$canSleep : true;
525
567
  const ccdEnabled = (_options$ccd = options === null || options === void 0 ? void 0 : options.ccd) !== null && _options$ccd !== void 0 ? _options$ccd : false;
568
+ const [erx, ery, erz] = (_options$enabledRotat = options === null || options === void 0 ? void 0 : options.enabledRotations) !== null && _options$enabledRotat !== void 0 ? _options$enabledRotat : [true, true, true];
569
+ const [etx, ety, etz] = (_options$enabledTrans = options === null || options === void 0 ? void 0 : options.enabledTranslations) !== null && _options$enabledTrans !== void 0 ? _options$enabledTrans : [true, true, true];
526
570
  const desc = new rapier.RigidBodyDesc(type).setLinvel(lvx, lvy, lvz).setAngvel({
527
571
  x: avx,
528
572
  y: avy,
529
573
  z: avz
530
- }).setGravityScale(gravityScale).setCanSleep(canSleep).setCcdEnabled(ccdEnabled);
574
+ }).setGravityScale(gravityScale).setCanSleep(canSleep).setCcdEnabled(ccdEnabled).enabledRotations(erx, ery, erz).enabledTranslations(etx, ety, etz);
575
+ if (options.lockRotations) desc.lockRotations();
576
+ if (options.lockTranslations) desc.lockTranslations();
531
577
  const rigidBody = world.createRigidBody(desc);
532
578
  rigidBodyRef.current = world.getRigidBody(rigidBody.handle);
533
579
  }
@@ -575,9 +621,9 @@ const useRigidBody = (options = {}) => {
575
621
  rigidBody.resetForces(false);
576
622
  rigidBody.resetTorques(false);
577
623
  const colliderSetting = (_ref = (_options$colliders = options === null || options === void 0 ? void 0 : options.colliders) !== null && _options$colliders !== void 0 ? _options$colliders : physicsOptions.colliders) !== null && _ref !== void 0 ? _ref : false;
578
- const hasCollisionEvents = !!(options.onCollisionEnter || options.onCollisionExit);
579
- const autoColliders = colliderSetting !== false ? createCollidersFromChildren(ref.current, rigidBody, colliderSetting, world, hasCollisionEvents) : [];
580
- rigidBody.wakeUp();
624
+ const autoColliders = colliderSetting !== false ? createCollidersFromChildren(ref.current, rigidBody, _objectSpread2(_objectSpread2({}, options), {}, {
625
+ colliders: colliderSetting
626
+ }), world) : [];
581
627
  rigidBodyMeshes.set(rigidBody.handle, ref.current);
582
628
  return () => {
583
629
  world.removeRigidBody(rigidBody);
@@ -624,142 +670,6 @@ const useCollider = (body, options = {}) => {
624
670
  }, []);
625
671
  const api = useMemo(() => createColliderApi(getColliderRef), []);
626
672
  return [objectRef, api];
627
- };
628
- const useRigidBodyWithCollider = (rigidBodyOptions, colliderOptions) => {
629
- const {
630
- world
631
- } = useRapier();
632
- const [ref, rigidBody] = useRigidBody(rigidBodyOptions);
633
- useEffect(() => {
634
- if (!colliderOptions) {
635
- return;
636
- }
637
-
638
- const scale = ref.current.getWorldScale(new Vector3());
639
- const collider = createColliderFromOptions(colliderOptions, world, world.getRigidBody(rigidBody.handle), scale);
640
- return () => {
641
- world.removeCollider(collider);
642
- };
643
- }, []);
644
- return [ref, rigidBody];
645
- };
646
- const useCuboid = (rigidBodyOptions = {}, colliderOptions = {}) => {
647
- var _colliderOptions$args;
648
-
649
- return useRigidBodyWithCollider(rigidBodyOptions, _objectSpread2({
650
- shape: "cuboid",
651
- args: (_colliderOptions$args = colliderOptions.args) !== null && _colliderOptions$args !== void 0 ? _colliderOptions$args : [0.5, 0.5, 0.5]
652
- }, colliderOptions));
653
- };
654
- const useBall = (rigidBodyOptions = {}, colliderOptions = {}) => {
655
- var _colliderOptions$args2;
656
-
657
- return useRigidBodyWithCollider(rigidBodyOptions, _objectSpread2({
658
- shape: "ball",
659
- args: (_colliderOptions$args2 = colliderOptions.args) !== null && _colliderOptions$args2 !== void 0 ? _colliderOptions$args2 : [0.5]
660
- }, colliderOptions));
661
- };
662
- const useCapsule = (rigidBodyOptions = {}, colliderOptions = {}) => {
663
- var _colliderOptions$args3;
664
-
665
- return useRigidBodyWithCollider(rigidBodyOptions, _objectSpread2({
666
- shape: "capsule",
667
- args: (_colliderOptions$args3 = colliderOptions.args) !== null && _colliderOptions$args3 !== void 0 ? _colliderOptions$args3 : [0.5, 0.5]
668
- }, colliderOptions));
669
- };
670
- const useHeightfield = (rigidBodyOptions = {}, colliderOptions = {}) => {
671
- return useRigidBodyWithCollider(rigidBodyOptions, _objectSpread2({
672
- shape: "heightfield"
673
- }, colliderOptions));
674
- };
675
- /**
676
- * Create a trimesh collider and rigid body.
677
- * Note that Trimeshes don't have mass unless provided.
678
- * See https://rapier.rs/docs/user_guides/javascript/rigid_bodies#mass-properties
679
- * for available properties.
680
- */
681
-
682
- const useTrimesh = (rigidBodyOptions = {}, colliderOptions = {}) => {
683
- return useRigidBodyWithCollider(rigidBodyOptions, _objectSpread2({
684
- shape: "trimesh"
685
- }, colliderOptions));
686
- };
687
-
688
- useTrimesh.fromMesh = (mesh, rigidBodyOptions = {}, colliderOptions = {}) => {
689
- var _mesh$geometry, _mesh$geometry$index;
690
-
691
- return useTrimesh(rigidBodyOptions, _objectSpread2({
692
- args: [mesh.geometry.attributes.position.array, ((_mesh$geometry = mesh.geometry) === null || _mesh$geometry === void 0 ? void 0 : (_mesh$geometry$index = _mesh$geometry.index) === null || _mesh$geometry$index === void 0 ? void 0 : _mesh$geometry$index.array) || []]
693
- }, colliderOptions));
694
- };
695
-
696
- const usePolyline = (rigidBodyOptions = {}, colliderOptions = {}) => {
697
- return useRigidBodyWithCollider(rigidBodyOptions, _objectSpread2({
698
- shape: "polyline"
699
- }, colliderOptions));
700
- };
701
- const useRoundCuboid = (rigidBodyOptions = {}, colliderOptions = {}) => {
702
- return useRigidBodyWithCollider(rigidBodyOptions, _objectSpread2({
703
- shape: "roundCuboid"
704
- }, colliderOptions));
705
- };
706
- const useCylinder = (rigidBodyOptions = {}, colliderOptions = {}) => {
707
- return useRigidBodyWithCollider(rigidBodyOptions, _objectSpread2({
708
- shape: "cylinder"
709
- }, colliderOptions));
710
- };
711
- const useRoundCylinder = (rigidBodyOptions = {}, colliderOptions = {}) => {
712
- return useRigidBodyWithCollider(rigidBodyOptions, _objectSpread2({
713
- shape: "roundCylinder"
714
- }, colliderOptions));
715
- };
716
- const useCone = (rigidBodyOptions = {}, colliderOptions = {}) => {
717
- return useRigidBodyWithCollider(rigidBodyOptions, _objectSpread2({
718
- shape: "cone"
719
- }, colliderOptions));
720
- };
721
- const useRoundCone = (rigidBodyOptions = {}, colliderOptions = {}) => {
722
- return useRigidBodyWithCollider(rigidBodyOptions, _objectSpread2({
723
- shape: "roundCone"
724
- }, colliderOptions));
725
- };
726
- const useConvexHull = (rigidBodyOptions = {}, colliderOptions = {}) => {
727
- return useRigidBodyWithCollider(rigidBodyOptions, _objectSpread2({
728
- shape: "convexHull"
729
- }, colliderOptions));
730
- };
731
-
732
- useConvexHull.fromMesh = (mesh, rigidBodyOptions = {}, colliderOptions = {}) => {
733
- var _mesh$geometry2, _mesh$geometry2$attri, _mesh$geometry2$attri2;
734
-
735
- return useConvexHull(rigidBodyOptions, _objectSpread2({
736
- args: [(mesh === null || mesh === void 0 ? void 0 : (_mesh$geometry2 = mesh.geometry) === null || _mesh$geometry2 === void 0 ? void 0 : (_mesh$geometry2$attri = _mesh$geometry2.attributes) === null || _mesh$geometry2$attri === void 0 ? void 0 : (_mesh$geometry2$attri2 = _mesh$geometry2$attri.position) === null || _mesh$geometry2$attri2 === void 0 ? void 0 : _mesh$geometry2$attri2.array) || []]
737
- }, colliderOptions));
738
- };
739
-
740
- const useRoundConvexHull = (rigidBodyOptions = {}, colliderOptions = {}) => {
741
- return useRigidBodyWithCollider(rigidBodyOptions, _objectSpread2({
742
- shape: "roundConvexHull"
743
- }, colliderOptions));
744
- };
745
- const useConvexMesh = (rigidBodyOptions = {}, colliderOptions = {}) => {
746
- return useRigidBodyWithCollider(rigidBodyOptions, _objectSpread2({
747
- shape: "convexMesh"
748
- }, colliderOptions));
749
- };
750
-
751
- useConvexMesh.fromMesh = (mesh, rigidBodyOptions = {}, colliderOptions = {}) => {
752
- var _mesh$geometry3, _mesh$geometry3$attri, _mesh$geometry3$attri2, _mesh$geometry4, _mesh$geometry4$index;
753
-
754
- return useConvexMesh(rigidBodyOptions, _objectSpread2({
755
- args: [mesh === null || mesh === void 0 ? void 0 : (_mesh$geometry3 = mesh.geometry) === null || _mesh$geometry3 === void 0 ? void 0 : (_mesh$geometry3$attri = _mesh$geometry3.attributes) === null || _mesh$geometry3$attri === void 0 ? void 0 : (_mesh$geometry3$attri2 = _mesh$geometry3$attri.position) === null || _mesh$geometry3$attri2 === void 0 ? void 0 : _mesh$geometry3$attri2.array, ((_mesh$geometry4 = mesh.geometry) === null || _mesh$geometry4 === void 0 ? void 0 : (_mesh$geometry4$index = _mesh$geometry4.index) === null || _mesh$geometry4$index === void 0 ? void 0 : _mesh$geometry4$index.array) || []]
756
- }, colliderOptions));
757
- };
758
-
759
- const useRoundConvexMesh = (rigidBodyOptions = {}, colliderOptions = {}) => {
760
- return useRigidBodyWithCollider(rigidBodyOptions, _objectSpread2({
761
- shape: "convexMesh"
762
- }, colliderOptions));
763
673
  }; // Joints
764
674
 
765
675
  const useImpulseJoint = (body1, body2, params) => {
@@ -772,12 +682,6 @@ const useImpulseJoint = (body1, body2, params) => {
772
682
  let rb1;
773
683
  let rb2;
774
684
 
775
- if ('handle' in body1 && 'handle' in body2) {
776
- rb1 = world.getRigidBody(body1.handle);
777
- rb2 = world.getRigidBody(body2.handle);
778
- jointRef.current = world.createImpulseJoint(params, rb1, rb2);
779
- }
780
-
781
685
  if ('current' in body1 && body1.current && 'current' in body2 && body2.current) {
782
686
  rb1 = world.getRigidBody(body1.current.handle);
783
687
  rb2 = world.getRigidBody(body2.current.handle);
@@ -1113,4 +1017,4 @@ const Debug = () => {
1113
1017
  })));
1114
1018
  };
1115
1019
 
1116
- export { BallCollider, CapsuleCollider, ConeCollider, ConvexHullCollider, CuboidCollider, CylinderCollider, Debug, HeightfieldCollider, Physics, RigidBody, RoundCuboidCollider, TrimeshCollider, useBall, useCapsule, useCollider, useCone, useConvexHull, useConvexMesh, useCuboid, useCylinder, useFixedJoint, useHeightfield, useImpulseJoint, usePolyline, usePrismaticJoint, useRapier, useRevoluteJoint, useRigidBody, useRigidBodyWithCollider, useRoundCone, useRoundConvexHull, useRoundConvexMesh, useRoundCuboid, useRoundCylinder, useSphericalJoint, useTrimesh };
1020
+ export { BallCollider, CapsuleCollider, ConeCollider, ConvexHullCollider, CuboidCollider, CylinderCollider, Debug, HeightfieldCollider, Physics, RigidBody, RoundCuboidCollider, TrimeshCollider, useCollider, useFixedJoint, useImpulseJoint, usePrismaticJoint, useRapier, useRevoluteJoint, useRigidBody, useSphericalJoint };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-three/rapier",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "source": "src/index.ts",
5
5
  "main": "dist/react-three-rapier.cjs.js",
6
6
  "module": "dist/react-three-rapier.esm.js",
@@ -23,7 +23,7 @@
23
23
  "three": "^0.139.2"
24
24
  },
25
25
  "dependencies": {
26
- "@dimforge/rapier3d-compat": "0.8.1",
26
+ "@dimforge/rapier3d-compat": "0.9.0",
27
27
  "use-asset": "^1.0.4"
28
28
  },
29
29
  "repository": "https://github.com/pmndrs/react-three-rapier/tree/master/packages/react-three-rapier"
package/readme.md CHANGED
@@ -5,18 +5,24 @@
5
5
  ## Usage
6
6
 
7
7
  ```tsx
8
- import { Box } from "@react-three/drei";
8
+ import { Box, Torus } from "@react-three/drei";
9
9
  import { Canvas } from "@react-three/fiber";
10
10
  import { Physics, RigidBody } from "@react-three/rapier";
11
11
 
12
12
  const App = () => {
13
13
  return (
14
14
  <Canvas>
15
- <Physics>
16
- <RigidBody>
17
- <Box />
18
- </RigidBody>
19
- </Physics>
15
+ <Suspense>
16
+ <Physics>
17
+ <RigidBody colliders={"hull"} restitution={2}>
18
+ <Torus />
19
+ </RigidBody>
20
+
21
+ <RigidBody position={[0, -2, 0]} type="kinematicPosition">
22
+ <Box args={[20, 0.5, 20]} />
23
+ </RigidBody>
24
+ </Physics>
25
+ </Suspense>
20
26
  </Canvas>
21
27
  );
22
28
  };
@@ -128,7 +134,7 @@ return (
128
134
  colliders="hull"
129
135
  onSleep={() => setIsAsleep(true)}
130
136
  onWake={() => setIsAsleep(false)}
131
- onCollision={({manifold}) => {
137
+ onCollisionEnter={({manifold}) => {
132
138
  console.log('Collision at world position ', manifold.solverContactPoint(0))
133
139
  }}
134
140
  >
@@ -140,24 +146,9 @@ return (
140
146
  }
141
147
  ```
142
148
 
143
- ## Hooks
144
-
145
- You can also use hooks to generate rigid bodies and colliders, but it's not encouraged.
146
-
147
- ```tsx
148
- import { Box } from "@react-three/drei";
149
- import { useCuboid } from "@react-three/rapier";
149
+ ## Joints
150
150
 
151
- const RigidBox = () => {
152
- // Generates a RigidBody and attaches a BoxCollider to it, returns a ref
153
- const [box, rigidBody, collider] = useCuboid(
154
- { position: [1, 1, 1] },
155
- { args: [0.5, 0.5, 0.5] }
156
- );
157
-
158
- return <Box ref={box} />;
159
- };
160
- ```
151
+ WIP
161
152
 
162
153
  ## Roadmap?
163
154
 
@@ -168,8 +159,9 @@ In order, but also not necessarily:
168
159
  - [x] Nested objects retain world transforms
169
160
  - [x] Nested objects retain correct collider scale
170
161
  - [x] Automatic colliders based on rigidbody children
171
- - [ ] Translation and rotational constraints
162
+ - [x] Translation and rotational constraints
172
163
  - [x] Collision events
164
+ - [ ] Colliders outside RigidBodies
173
165
  - [ ] InstancedMesh support
174
166
  - [ ] Docs
175
167
  - [ ] CodeSandbox examples