@threecyborgs/wasm-box3d-three 0.1.0 → 0.1.2

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.
package/dist/index.d.ts CHANGED
@@ -8,10 +8,10 @@ export type ThreeBodyMeshManagerOptions = {
8
8
  };
9
9
 
10
10
  export type ThreeBodyMeshManager = {
11
- meshes: ThreeNamespace.Mesh[];
11
+ readonly meshes: ThreeNamespace.Object3D[];
12
+ arenaMeshes: ThreeNamespace.Mesh[];
12
13
  sync(physics: Box3DDemo): void;
13
14
  dispose(): void;
14
15
  };
15
16
 
16
17
  export declare function createThreeBodyMeshManager(options: ThreeBodyMeshManagerOptions): ThreeBodyMeshManager;
17
-
package/dist/index.js CHANGED
@@ -9,83 +9,216 @@ export function createThreeBodyMeshManager({ THREE, scene, materialFactory } = {
9
9
  }
10
10
 
11
11
  const boxGeometry = new THREE.BoxGeometry(1, 1, 1);
12
- const sphereGeometry = new THREE.SphereGeometry(0.5, 32, 18);
13
- const meshes = [];
12
+ const sphereGeometry = new THREE.SphereGeometry(0.5, 12, 8);
13
+ const arenaMeshes = [];
14
+ const instanceMatrix = new THREE.Matrix4();
15
+ const instancePosition = new THREE.Vector3();
16
+ const instanceQuaternion = new THREE.Quaternion();
17
+ const instanceScale = new THREE.Vector3();
18
+ const instanceColor = new THREE.Color();
14
19
 
15
- function makeMaterial(record) {
20
+ let boxInstances;
21
+ let sphereInstances;
22
+ let boxCapacity = 0;
23
+ let sphereCapacity = 0;
24
+ let lastBoxColorCount = -1;
25
+ let lastSphereColorCount = -1;
26
+
27
+ function getRecordAt(bodyData, offset) {
28
+ return {
29
+ position: { x: bodyData[offset], y: bodyData[offset + 1], z: bodyData[offset + 2] },
30
+ quaternion: { x: bodyData[offset + 3], y: bodyData[offset + 4], z: bodyData[offset + 5], w: bodyData[offset + 6] },
31
+ size: { x: bodyData[offset + 7], y: bodyData[offset + 8], z: bodyData[offset + 9] },
32
+ shapeType: bodyData[offset + 10],
33
+ color: { r: bodyData[offset + 11], g: bodyData[offset + 12], b: bodyData[offset + 13] },
34
+ };
35
+ }
36
+
37
+ function isArenaBoundAt(index, bodyData, offset) {
38
+ return (
39
+ index < 5 &&
40
+ bodyData[offset + 10] === RenderShapeType.box &&
41
+ (bodyData[offset + 7] > 10 || bodyData[offset + 8] > 5 || bodyData[offset + 9] > 10)
42
+ );
43
+ }
44
+
45
+ function makeArenaMaterial(record, index) {
16
46
  if (materialFactory) {
17
47
  return materialFactory(record);
18
48
  }
19
49
 
20
50
  return new THREE.MeshStandardMaterial({
21
51
  color: new THREE.Color(record.color.r, record.color.g, record.color.b),
22
- roughness: record.shapeType === RenderShapeType.sphere ? 0.42 : 0.58,
23
- metalness: 0.03,
52
+ roughness: 0.82,
53
+ metalness: 0.0,
54
+ transparent: true,
55
+ opacity: index === 0 ? 0.22 : 0.34,
56
+ depthWrite: false,
57
+ side: THREE.DoubleSide,
58
+ wireframe: index !== 0,
24
59
  });
25
60
  }
26
61
 
27
- function getRecord(bodyData, index, stride) {
28
- const offset = index * stride;
29
- return {
30
- position: { x: bodyData[offset], y: bodyData[offset + 1], z: bodyData[offset + 2] },
31
- quaternion: { x: bodyData[offset + 3], y: bodyData[offset + 4], z: bodyData[offset + 5], w: bodyData[offset + 6] },
32
- size: { x: bodyData[offset + 7], y: bodyData[offset + 8], z: bodyData[offset + 9] },
33
- shapeType: bodyData[offset + 10],
34
- color: { r: bodyData[offset + 11], g: bodyData[offset + 12], b: bodyData[offset + 13] },
35
- };
62
+ function makeInstanceMaterial(shapeType) {
63
+ return new THREE.MeshStandardMaterial({
64
+ color: 0xffffff,
65
+ roughness: shapeType === RenderShapeType.sphere ? 0.42 : 0.58,
66
+ metalness: 0.03,
67
+ vertexColors: true,
68
+ });
36
69
  }
37
70
 
38
- function ensureMesh(index, record) {
39
- let mesh = meshes[index];
40
- const geometry = record.shapeType === RenderShapeType.sphere ? sphereGeometry : boxGeometry;
71
+ function hideArenaMeshes(fromIndex) {
72
+ for (let i = fromIndex; i < arenaMeshes.length; i += 1) {
73
+ arenaMeshes[i].visible = false;
74
+ }
75
+ }
41
76
 
77
+ function syncArenaMesh(arenaIndex, bodyIndex, record) {
78
+ let mesh = arenaMeshes[arenaIndex];
42
79
  if (!mesh) {
43
- mesh = new THREE.Mesh(geometry, makeMaterial(record));
44
- mesh.castShadow = true;
45
- mesh.receiveShadow = true;
46
- meshes[index] = mesh;
80
+ mesh = new THREE.Mesh(boxGeometry, makeArenaMaterial(record, bodyIndex));
81
+ mesh.castShadow = false;
82
+ mesh.receiveShadow = false;
83
+ arenaMeshes[arenaIndex] = mesh;
47
84
  scene.add(mesh);
48
- return mesh;
49
85
  }
50
86
 
51
- if (mesh.geometry !== geometry) {
52
- mesh.geometry = geometry;
53
- mesh.material.dispose();
54
- mesh.material = makeMaterial(record);
87
+ mesh.visible = true;
88
+ mesh.position.set(record.position.x, record.position.y, record.position.z);
89
+ mesh.quaternion.set(record.quaternion.x, record.quaternion.y, record.quaternion.z, record.quaternion.w);
90
+ mesh.scale.set(record.size.x, record.size.y, record.size.z);
91
+ }
92
+
93
+ function disposeInstancedMesh(mesh) {
94
+ if (!mesh) {
95
+ return;
96
+ }
97
+ scene.remove(mesh);
98
+ mesh.material.dispose();
99
+ }
100
+
101
+ function ensureInstances(kind, required) {
102
+ if (kind === RenderShapeType.sphere) {
103
+ if (sphereInstances && sphereCapacity >= required) {
104
+ return sphereInstances;
105
+ }
106
+ disposeInstancedMesh(sphereInstances);
107
+ sphereCapacity = Math.max(1, required);
108
+ lastSphereColorCount = -1;
109
+ sphereInstances = new THREE.InstancedMesh(sphereGeometry, makeInstanceMaterial(RenderShapeType.sphere), sphereCapacity);
110
+ sphereInstances.castShadow = false;
111
+ sphereInstances.receiveShadow = false;
112
+ sphereInstances.frustumCulled = false;
113
+ scene.add(sphereInstances);
114
+ return sphereInstances;
55
115
  }
56
116
 
57
- return mesh;
117
+ if (boxInstances && boxCapacity >= required) {
118
+ return boxInstances;
119
+ }
120
+ disposeInstancedMesh(boxInstances);
121
+ boxCapacity = Math.max(1, required);
122
+ lastBoxColorCount = -1;
123
+ boxInstances = new THREE.InstancedMesh(boxGeometry, makeInstanceMaterial(RenderShapeType.box), boxCapacity);
124
+ boxInstances.castShadow = false;
125
+ boxInstances.receiveShadow = false;
126
+ boxInstances.frustumCulled = false;
127
+ scene.add(boxInstances);
128
+ return boxInstances;
129
+ }
130
+
131
+ function writeInstance(mesh, instanceIndex, bodyData, offset, writeColor) {
132
+ instancePosition.set(bodyData[offset], bodyData[offset + 1], bodyData[offset + 2]);
133
+ instanceQuaternion.set(bodyData[offset + 3], bodyData[offset + 4], bodyData[offset + 5], bodyData[offset + 6]);
134
+ instanceScale.set(bodyData[offset + 7], bodyData[offset + 8], bodyData[offset + 9]);
135
+ instanceMatrix.compose(instancePosition, instanceQuaternion, instanceScale);
136
+ mesh.setMatrixAt(instanceIndex, instanceMatrix);
137
+
138
+ if (writeColor) {
139
+ instanceColor.setRGB(bodyData[offset + 11], bodyData[offset + 12], bodyData[offset + 13]);
140
+ mesh.setColorAt(instanceIndex, instanceColor);
141
+ }
58
142
  }
59
143
 
60
144
  return {
61
- meshes,
145
+ arenaMeshes,
146
+ get meshes() {
147
+ return [...arenaMeshes, boxInstances, sphereInstances].filter(Boolean);
148
+ },
62
149
  sync(physics) {
63
150
  const count = physics.getBodyCount();
64
151
  const stride = physics.getBodyStride();
65
152
  const bodyData = physics.getBodyData();
153
+ let arenaCount = 0;
154
+ let boxCount = 0;
155
+ let sphereCount = 0;
66
156
 
67
157
  for (let i = 0; i < count; i += 1) {
68
- const record = getRecord(bodyData, i, stride);
69
- const mesh = ensureMesh(i, record);
70
- mesh.visible = true;
71
- mesh.position.set(record.position.x, record.position.y, record.position.z);
72
- mesh.quaternion.set(record.quaternion.x, record.quaternion.y, record.quaternion.z, record.quaternion.w);
73
- mesh.scale.set(record.size.x, record.size.y, record.size.z);
158
+ const offset = i * stride;
159
+ if (isArenaBoundAt(i, bodyData, offset)) {
160
+ syncArenaMesh(arenaCount, i, getRecordAt(bodyData, offset));
161
+ arenaCount += 1;
162
+ } else if (bodyData[offset + 10] === RenderShapeType.sphere) {
163
+ sphereCount += 1;
164
+ } else {
165
+ boxCount += 1;
166
+ }
74
167
  }
75
168
 
76
- for (let i = count; i < meshes.length; i += 1) {
77
- meshes[i].visible = false;
169
+ hideArenaMeshes(arenaCount);
170
+
171
+ const boxes = ensureInstances(RenderShapeType.box, boxCount);
172
+ const spheres = ensureInstances(RenderShapeType.sphere, sphereCount);
173
+ let boxIndex = 0;
174
+ let sphereIndex = 0;
175
+ const writeBoxColors = boxCount !== lastBoxColorCount;
176
+ const writeSphereColors = sphereCount !== lastSphereColorCount;
177
+
178
+ for (let i = 0; i < count; i += 1) {
179
+ const offset = i * stride;
180
+ if (isArenaBoundAt(i, bodyData, offset)) {
181
+ continue;
182
+ }
183
+
184
+ if (bodyData[offset + 10] === RenderShapeType.sphere) {
185
+ writeInstance(spheres, sphereIndex, bodyData, offset, writeSphereColors);
186
+ sphereIndex += 1;
187
+ } else {
188
+ writeInstance(boxes, boxIndex, bodyData, offset, writeBoxColors);
189
+ boxIndex += 1;
190
+ }
191
+ }
192
+
193
+ boxes.count = boxIndex;
194
+ spheres.count = sphereIndex;
195
+ boxes.instanceMatrix.needsUpdate = true;
196
+ spheres.instanceMatrix.needsUpdate = true;
197
+ if (writeBoxColors && boxes.instanceColor) {
198
+ boxes.instanceColor.needsUpdate = true;
199
+ lastBoxColorCount = boxIndex;
200
+ }
201
+ if (writeSphereColors && spheres.instanceColor) {
202
+ spheres.instanceColor.needsUpdate = true;
203
+ lastSphereColorCount = sphereIndex;
78
204
  }
79
205
  },
80
206
  dispose() {
81
- for (const mesh of meshes) {
207
+ for (const mesh of arenaMeshes) {
82
208
  scene.remove(mesh);
83
209
  mesh.material.dispose();
84
210
  }
211
+ disposeInstancedMesh(boxInstances);
212
+ disposeInstancedMesh(sphereInstances);
85
213
  boxGeometry.dispose();
86
214
  sphereGeometry.dispose();
87
- meshes.length = 0;
215
+ arenaMeshes.length = 0;
216
+ boxInstances = undefined;
217
+ sphereInstances = undefined;
218
+ boxCapacity = 0;
219
+ sphereCapacity = 0;
220
+ lastBoxColorCount = -1;
221
+ lastSphereColorCount = -1;
88
222
  },
89
223
  };
90
224
  }
91
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threecyborgs/wasm-box3d-three",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Three.js helpers for rendering @threecyborgs/wasm-box3d bodies.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -29,7 +29,7 @@
29
29
  "prepack": "npm run build"
30
30
  },
31
31
  "dependencies": {
32
- "@threecyborgs/wasm-box3d": "0.1.0"
32
+ "@threecyborgs/wasm-box3d": "0.1.2"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "three": ">=0.160.0"
@@ -42,4 +42,3 @@
42
42
  "physics"
43
43
  ]
44
44
  }
45
-