@kite3d/engine 0.15.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.
Files changed (74) hide show
  1. package/dist/authoring.d.ts +54 -0
  2. package/dist/authoring.js +165 -0
  3. package/dist/authoring.js.map +1 -0
  4. package/dist/authoringValidation.d.ts +110 -0
  5. package/dist/authoringValidation.js +488 -0
  6. package/dist/authoringValidation.js.map +1 -0
  7. package/dist/defaults.d.ts +2 -0
  8. package/dist/fileTypes.d.ts +5 -0
  9. package/dist/fileTypes.js +51 -0
  10. package/dist/fileTypes.js.map +1 -0
  11. package/dist/importMap.d.ts +15 -0
  12. package/dist/importMap.js +62 -0
  13. package/dist/importMap.js.map +1 -0
  14. package/dist/index.d.ts +15 -0
  15. package/dist/index.js +2756 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/migrations.js +5 -0
  18. package/dist/migrations.js.map +1 -0
  19. package/dist/paths.d.ts +9 -0
  20. package/dist/paths.js +13 -0
  21. package/dist/paths.js.map +1 -0
  22. package/dist/plugins/GeneratorComponent.d.ts +40 -0
  23. package/dist/plugins/HtmlUiComponent.d.ts +52 -0
  24. package/dist/plugins/HtmlUiComponent.example.d.ts +0 -0
  25. package/dist/plugins/cannon/Cannon3DBodyComponent.d.ts +27 -0
  26. package/dist/plugins/cannon/Cannon3DShapeComponent.d.ts +57 -0
  27. package/dist/plugins/cannon/CannonPhysicsPlugin.d.ts +63 -0
  28. package/dist/plugins/cannon/CannonRagdollComponent.d.ts +148 -0
  29. package/dist/plugins/cannon/helper.d.ts +24 -0
  30. package/dist/plugins/cannon/threeToCannon.d.ts +63 -0
  31. package/dist/plugins/cannon/utils.d.ts +9 -0
  32. package/dist/projectFormat.js +108 -0
  33. package/dist/projectFormat.js.map +1 -0
  34. package/dist/runtime/createGame.d.ts +29 -0
  35. package/dist/runtime/index.d.ts +18 -0
  36. package/dist/runtime/migrations.d.ts +5 -0
  37. package/dist/runtime/nestedAssets.d.ts +26 -0
  38. package/dist/runtime/projectFormat.d.ts +83 -0
  39. package/dist/runtime/version.d.ts +1 -0
  40. package/dist/runtime.js +72835 -0
  41. package/dist/runtime.js.map +1 -0
  42. package/dist/sceneSerialization.d.ts +17 -0
  43. package/dist/sceneSerialization.js +174 -0
  44. package/dist/sceneSerialization.js.map +1 -0
  45. package/dist/scripts.d.ts +17 -0
  46. package/dist/version.js +7 -0
  47. package/dist/version.js.map +1 -0
  48. package/package.json +86 -0
  49. package/src/authoring.ts +293 -0
  50. package/src/authoringValidation.ts +815 -0
  51. package/src/defaults.ts +277 -0
  52. package/src/fileTypes.ts +53 -0
  53. package/src/importMap.ts +105 -0
  54. package/src/index.ts +15 -0
  55. package/src/paths.ts +9 -0
  56. package/src/plugins/GeneratorComponent.ts +275 -0
  57. package/src/plugins/HtmlUiComponent.example.ts +202 -0
  58. package/src/plugins/HtmlUiComponent.md +219 -0
  59. package/src/plugins/HtmlUiComponent.ts +320 -0
  60. package/src/plugins/cannon/Cannon3DBodyComponent.ts +227 -0
  61. package/src/plugins/cannon/Cannon3DShapeComponent.ts +258 -0
  62. package/src/plugins/cannon/CannonPhysicsPlugin.ts +409 -0
  63. package/src/plugins/cannon/CannonRagdollComponent.ts +1170 -0
  64. package/src/plugins/cannon/helper.ts +255 -0
  65. package/src/plugins/cannon/threeToCannon.ts +441 -0
  66. package/src/plugins/cannon/utils.ts +185 -0
  67. package/src/runtime/createGame.ts +337 -0
  68. package/src/runtime/index.ts +19 -0
  69. package/src/runtime/migrations.ts +6 -0
  70. package/src/runtime/nestedAssets.ts +226 -0
  71. package/src/runtime/projectFormat.ts +233 -0
  72. package/src/runtime/version.ts +3 -0
  73. package/src/sceneSerialization.ts +289 -0
  74. package/src/scripts.ts +52 -0
@@ -0,0 +1,255 @@
1
+ // https://github.com/pmndrs/cannon-es-debugger/blob/master/src/cannon-es-debugger.ts
2
+
3
+ import {
4
+ Box,
5
+ ConvexPolyhedron,
6
+ Cylinder,
7
+ Heightfield,
8
+ Quaternion as CannonQuaternion,
9
+ Shape,
10
+ Sphere,
11
+ Trimesh,
12
+ Vec3 as CannonVector3,
13
+ } from 'cannon-es'
14
+ import {
15
+ BoxGeometry,
16
+ BufferGeometry,
17
+ CylinderGeometry,
18
+ Float32BufferAttribute,
19
+ LineMaterial2,
20
+ Mesh,
21
+ PlaneGeometry,
22
+ SphereGeometry,
23
+ Vector3 as ThreeVector3,
24
+ Wireframe,
25
+ WireframeGeometry3
26
+ } from "threepipe";
27
+
28
+ type ComplexShape = Shape & {geometryId?: number}
29
+
30
+ export function cannonDebugger() {
31
+ const scale = 1
32
+
33
+ const _tempVec0 = new CannonVector3()
34
+ const _tempVec1 = new CannonVector3()
35
+ const _tempVec2 = new CannonVector3()
36
+ const _tempQuat0 = new CannonQuaternion()
37
+ const _sphereGeometry = new SphereGeometry(1)
38
+ const _boxGeometry = new BoxGeometry(1, 1, 1)
39
+ const _planeGeometry = new PlaneGeometry(10, 10, 10, 10)
40
+ // const _material = new MeshBasicMaterial({color: color ?? 0x00ff00, wireframe: true})
41
+ const _material = new LineMaterial2({
42
+ color: 0x00ff00,
43
+ linewidth: 3, // in world units with size attenuation, pixels otherwise
44
+ vertexColors: false,
45
+ worldUnits: false,
46
+
47
+ dashed: false,
48
+ alphaToCoverage: true,
49
+
50
+ toneMapped: false,
51
+ transparent: true,
52
+ depthTest: true,
53
+ depthWrite: false,
54
+
55
+ allowOverride: false,
56
+ })
57
+ _material.userData.renderToGBuffer = false
58
+ _material.userData.renderToDepth = false
59
+
60
+ // Move the planeGeometry forward a little bit to prevent z-fighting
61
+ _planeGeometry.translate(0, 0, 0.0001)
62
+
63
+ function createConvexPolyhedronGeometry(shape: ConvexPolyhedron): BufferGeometry {
64
+ const geometry = new BufferGeometry()
65
+
66
+ // Add vertices
67
+ const positions = []
68
+ for (let i = 0; i < shape.vertices.length; i++) {
69
+ const vertex = shape.vertices[i]
70
+ positions.push(vertex.x, vertex.y, vertex.z)
71
+ }
72
+ geometry.setAttribute('position', new Float32BufferAttribute(positions, 3))
73
+
74
+ // Add faces
75
+ const indices = []
76
+ for (let i = 0; i < shape.faces.length; i++) {
77
+ const face = shape.faces[i]
78
+ const a = face[0]
79
+ for (let j = 1; j < face.length - 1; j++) {
80
+ const b = face[j]
81
+ const c = face[j + 1]
82
+ indices.push(a, b, c)
83
+ }
84
+ }
85
+
86
+ geometry.setIndex(indices)
87
+ geometry.computeBoundingSphere()
88
+ geometry.computeVertexNormals()
89
+ return geometry
90
+ }
91
+
92
+ function createTrimeshGeometry(shape: Trimesh): BufferGeometry {
93
+ const geometry = new BufferGeometry()
94
+ const positions = []
95
+ const v0 = _tempVec0
96
+ const v1 = _tempVec1
97
+ const v2 = _tempVec2
98
+
99
+ for (let i = 0; i < shape.indices.length / 3; i++) {
100
+ shape.getTriangleVertices(i, v0, v1, v2)
101
+ positions.push(v0.x, v0.y, v0.z)
102
+ positions.push(v1.x, v1.y, v1.z)
103
+ positions.push(v2.x, v2.y, v2.z)
104
+ }
105
+
106
+ geometry.setAttribute('position', new Float32BufferAttribute(positions, 3))
107
+ geometry.computeBoundingSphere()
108
+ geometry.computeVertexNormals()
109
+ return geometry
110
+ }
111
+
112
+ function createHeightfieldGeometry(shape: Heightfield): BufferGeometry {
113
+ const geometry = new BufferGeometry()
114
+ const s = shape.elementSize || 1 // assumes square heightfield, else i*x, j*y
115
+ const positions = shape.data.flatMap((row, i) => row.flatMap((z, j) => [i * s, j * s, z]))
116
+ const indices = []
117
+
118
+ for (let xi = 0; xi < shape.data.length - 1; xi++) {
119
+ for (let yi = 0; yi < shape.data[xi].length - 1; yi++) {
120
+ const stride = shape.data[xi].length
121
+ const index = xi * stride + yi
122
+ indices.push(index + 1, index + stride, index + stride + 1)
123
+ indices.push(index + stride, index + 1, index)
124
+ }
125
+ }
126
+
127
+ geometry.setIndex(indices)
128
+ geometry.setAttribute('position', new Float32BufferAttribute(positions, 3))
129
+ geometry.computeBoundingSphere()
130
+ geometry.computeVertexNormals()
131
+ return geometry
132
+ }
133
+
134
+ function createMesh(shape: Shape): Mesh {
135
+ let mesh = new Mesh()
136
+ const {SPHERE, BOX, PLANE, CYLINDER, CONVEXPOLYHEDRON, TRIMESH, HEIGHTFIELD} = Shape.types
137
+
138
+ switch (shape.type) {
139
+ case SPHERE: {
140
+ mesh = new Wireframe(new WireframeGeometry3(_sphereGeometry), _material)
141
+ break
142
+ }
143
+ case BOX: {
144
+ mesh = new Wireframe(new WireframeGeometry3(_boxGeometry), _material)
145
+ break
146
+ }
147
+ case PLANE: {
148
+ mesh = new Wireframe(new WireframeGeometry3(_planeGeometry), _material)
149
+ break
150
+ }
151
+ case CYLINDER: {
152
+ const geometry = new CylinderGeometry(
153
+ (shape as Cylinder).radiusTop,
154
+ (shape as Cylinder).radiusBottom,
155
+ (shape as Cylinder).height,
156
+ (shape as Cylinder).numSegments
157
+ )
158
+ mesh = new Wireframe(new WireframeGeometry3(geometry), _material)
159
+ ;(shape as ComplexShape).geometryId = geometry.id
160
+ break
161
+ }
162
+ case CONVEXPOLYHEDRON: {
163
+ const geometry = createConvexPolyhedronGeometry(shape as ConvexPolyhedron)
164
+ mesh = new Wireframe(new WireframeGeometry3(geometry), _material)
165
+ ;(shape as ComplexShape).geometryId = geometry.id
166
+ break
167
+ }
168
+ case TRIMESH: {
169
+ const geometry = createTrimeshGeometry(shape as Trimesh)
170
+ mesh = new Wireframe(new WireframeGeometry3(geometry), _material)
171
+ ;(shape as ComplexShape).geometryId = geometry.id
172
+ break
173
+ }
174
+ case HEIGHTFIELD: {
175
+ const geometry = createHeightfieldGeometry(shape as Heightfield)
176
+ mesh = new Wireframe(new WireframeGeometry3(geometry), _material)
177
+ ;(shape as ComplexShape).geometryId = geometry.id
178
+ break
179
+ }
180
+ }
181
+ return mesh
182
+ }
183
+
184
+ function scaleMesh(mesh: Mesh, shape: Shape | ComplexShape): void {
185
+ const {SPHERE, BOX, PLANE, CYLINDER, CONVEXPOLYHEDRON, TRIMESH, HEIGHTFIELD} = Shape.types
186
+ switch (shape.type) {
187
+ case SPHERE: {
188
+ const {radius} = shape as Sphere
189
+ mesh.scale.set(radius * scale, radius * scale, radius * scale)
190
+ break
191
+ }
192
+ case BOX: {
193
+ mesh.scale.copy((shape as Box).halfExtents as unknown as ThreeVector3)
194
+ mesh.scale.multiplyScalar(2 * scale)
195
+ break
196
+ }
197
+ case PLANE: {
198
+ break
199
+ }
200
+ case CYLINDER: {
201
+ mesh.scale.set(1 * scale, 1 * scale, 1 * scale)
202
+ break
203
+ }
204
+ case CONVEXPOLYHEDRON: {
205
+ mesh.scale.set(1 * scale, 1 * scale, 1 * scale)
206
+ break
207
+ }
208
+ case TRIMESH: {
209
+ mesh.scale.copy((shape as Trimesh).scale as unknown as ThreeVector3).multiplyScalar(scale)
210
+ break
211
+ }
212
+ case HEIGHTFIELD: {
213
+ mesh.scale.set(1 * scale, 1 * scale, 1 * scale)
214
+ break
215
+ }
216
+ }
217
+ }
218
+
219
+ function typeMatch(mesh: Mesh|undefined, shape: Shape | ComplexShape): boolean {
220
+ if (!mesh) return false
221
+ const {geometry} = mesh
222
+ return (
223
+ geometry instanceof SphereGeometry && shape.type === Shape.types.SPHERE ||
224
+ geometry instanceof BoxGeometry && shape.type === Shape.types.BOX ||
225
+ geometry instanceof PlaneGeometry && shape.type === Shape.types.PLANE ||
226
+ // geometry.id === (shape as ComplexShape).geometryId && shape.type === Shape.types.BOX ||
227
+ // geometry.id === (shape as ComplexShape).geometryId && shape.type === Shape.types.SPHERE ||
228
+ // geometry.id === (shape as ComplexShape).geometryId && shape.type === Shape.types.PLANE ||
229
+ geometry.id === (shape as ComplexShape).geometryId && shape.type === Shape.types.CONVEXPOLYHEDRON ||
230
+ geometry.id === (shape as ComplexShape).geometryId && shape.type === Shape.types.TRIMESH ||
231
+ geometry.id === (shape as ComplexShape).geometryId && shape.type === Shape.types.HEIGHTFIELD
232
+ )
233
+ }
234
+
235
+ function updateMesh(mesh: Mesh|undefined, shape: Shape | ComplexShape) {
236
+ if (!typeMatch(mesh, shape)) {
237
+ mesh = createMesh(shape)
238
+ }
239
+ mesh && scaleMesh(mesh, shape)
240
+ return mesh
241
+ }
242
+
243
+
244
+ return {
245
+ createConvexPolyhedronGeometry,
246
+ createTrimeshGeometry,
247
+ createHeightfieldGeometry,
248
+ createMesh,
249
+ scaleMesh,
250
+ typeMatch,
251
+ updateMesh,
252
+ }
253
+ }
254
+
255
+ export const CannonDebugger = cannonDebugger();
@@ -0,0 +1,441 @@
1
+ // https://github.com/donmccurdy/three-to-cannon/blob/main/src/index.ts
2
+ import {Box, ConvexPolyhedron, Cylinder, Quaternion as CQuaternion, Shape, Sphere, Trimesh, Vec3} from 'cannon-es';
3
+ import {ConvexHull, Box3, BufferGeometry, CylinderGeometry, MathUtils, Mesh, Object3D, SphereGeometry, Vector3} from 'threepipe';
4
+ import {getComponent, getGeometry, getVertices} from './utils';
5
+
6
+ const PI_2 = Math.PI / 2;
7
+
8
+ export type BoxParameters = { x: number, y: number, z: number };
9
+
10
+ export type CylinderParameters = { radiusTop: number, radiusBottom: number, height: number, segments: number };
11
+
12
+ export type SphereParameters = { radius: number };
13
+
14
+ export type ConvexPolyhedronParameters = { vertices: Float32Array, faces: number[][] };
15
+
16
+ export type TrimeshParameters = { vertices: Float32Array, indices: Uint32Array };
17
+
18
+ type ShapeTypeToShapeParameters = {
19
+ Box: BoxParameters,
20
+ Cylinder: CylinderParameters,
21
+ Sphere: SphereParameters,
22
+ ConvexPolyhedron: ConvexPolyhedronParameters,
23
+ Trimesh: TrimeshParameters,
24
+ };
25
+
26
+ export enum ShapeType {
27
+ BOX = 'Box',
28
+ CYLINDER = 'Cylinder',
29
+ SPHERE = 'Sphere',
30
+ HULL = 'ConvexPolyhedron',
31
+ MESH = 'Trimesh',
32
+ }
33
+
34
+ export interface ShapeOptions {
35
+ type?: ShapeType,
36
+ cylinderAxis?: 'x' | 'y' | 'z',
37
+ sphereRadius?: number,
38
+ }
39
+
40
+ export interface ShapeParameters<T extends ShapeType = ShapeType> {
41
+ type: T,
42
+ params: ShapeTypeToShapeParameters[T],
43
+ offset?: Vec3,
44
+ orientation?: CQuaternion,
45
+ }
46
+
47
+ export interface ShapeResult<T extends Shape = Shape> {
48
+ shape: T,
49
+ offset?: Vec3,
50
+ orientation?: CQuaternion,
51
+ }
52
+
53
+ /**
54
+ * Given a THREE.Object3D instance, creates parameters for a CANNON shape.
55
+ */
56
+ export const getShapeParameters = function (object: Object3D, options: ShapeOptions = {}): ShapeParameters | null {
57
+ let geometry: BufferGeometry | null;
58
+
59
+ if (options.type === ShapeType.BOX) {
60
+ return getBoundingBoxParameters(object);
61
+ } else if (options.type === ShapeType.CYLINDER) {
62
+ return getBoundingCylinderParameters(object, options);
63
+ } else if (options.type === ShapeType.SPHERE) {
64
+ return getBoundingSphereParameters(object, options);
65
+ } else if (options.type === ShapeType.HULL) {
66
+ return getConvexPolyhedronParameters(object);
67
+ } else if (options.type === ShapeType.MESH) {
68
+ geometry = getGeometry(object);
69
+ return geometry ? getTrimeshParameters(geometry) : null;
70
+ } else if (options.type) {
71
+ throw new Error(`[CANNON.getShapeParameters] Invalid type "${options.type}".`);
72
+ }
73
+
74
+ geometry = getGeometry(object);
75
+ if (!geometry) return null;
76
+
77
+ switch (geometry.type) {
78
+ case 'BoxGeometry':
79
+ case 'BoxBufferGeometry':
80
+ return getBoxParameters(geometry);
81
+ case 'CylinderGeometry':
82
+ case 'CylinderBufferGeometry':
83
+ return getCylinderParameters(geometry as CylinderGeometry);
84
+ case 'PlaneGeometry':
85
+ case 'PlaneBufferGeometry':
86
+ return getPlaneParameters(geometry);
87
+ case 'SphereGeometry':
88
+ case 'SphereBufferGeometry':
89
+ return getSphereParameters(geometry as SphereGeometry);
90
+ case 'TubeGeometry':
91
+ case 'BufferGeometry':
92
+ return getBoundingBoxParameters(object);
93
+ default:
94
+ console.warn(
95
+ 'Unrecognized geometry: "%s". Using bounding box as shape.', geometry.type
96
+ );
97
+ return getBoxParameters(geometry);
98
+ }
99
+ };
100
+
101
+ /**
102
+ * Given a THREE.Object3D instance, creates a corresponding CANNON shape.
103
+ */
104
+ export const threeToCannon = function (object: Object3D, options: ShapeOptions = {}, shapeParameters?: ShapeParameters|null): ShapeResult | null {
105
+ shapeParameters = shapeParameters || getShapeParameters(object, options);
106
+ if (!shapeParameters) {
107
+ return null;
108
+ }
109
+
110
+ const { type, params, offset, orientation } = shapeParameters;
111
+
112
+ let shape: Shape;
113
+ if (type === ShapeType.BOX) {
114
+ shape = createBox(params as BoxParameters);
115
+ } else if (type === ShapeType.CYLINDER) {
116
+ shape = createCylinder(params as CylinderParameters);
117
+ } else if (type === ShapeType.SPHERE) {
118
+ shape = createSphere(params as SphereParameters);
119
+ } else if (type === ShapeType.HULL) {
120
+ shape = createConvexPolyhedron(params as ConvexPolyhedronParameters);
121
+ } else {
122
+ shape = createTrimesh(params as TrimeshParameters);
123
+ }
124
+
125
+ return {
126
+ shape,
127
+ offset,
128
+ orientation,
129
+ };
130
+ };
131
+
132
+ /******************************************************************************
133
+ * Shape construction
134
+ */
135
+
136
+ function createBox (params: BoxParameters): Box {
137
+ const { x, y, z } = params;
138
+ const shape = new Box(new Vec3(x || 0.025, y || 0.025, z || 0.025));
139
+ return shape;
140
+ }
141
+
142
+ function createCylinder (params: CylinderParameters): Cylinder {
143
+ const { radiusTop, radiusBottom, height, segments } = params;
144
+
145
+ const shape = new Cylinder(radiusTop, radiusBottom, height, segments);
146
+
147
+ // Include metadata for serialization.
148
+ // TODO(cleanup): Is this still necessary?
149
+ shape.radiusTop = radiusBottom;
150
+ shape.radiusBottom = radiusBottom;
151
+ shape.height = height;
152
+ shape.numSegments = segments;
153
+
154
+ return shape;
155
+ }
156
+
157
+ function createSphere (params: SphereParameters): Sphere {
158
+ const shape = new Sphere(params.radius);
159
+
160
+ return shape;
161
+ }
162
+
163
+ function createConvexPolyhedron (params: ConvexPolyhedronParameters): ConvexPolyhedron {
164
+ const { faces, vertices: verticesArray } = params;
165
+
166
+ const vertices: Vec3[] = [];
167
+ for (let i = 0; i < verticesArray.length; i += 3) {
168
+ vertices.push(new Vec3(
169
+ verticesArray[i],
170
+ verticesArray[i + 1],
171
+ verticesArray[i + 2]
172
+ ));
173
+ }
174
+
175
+ const shape = new ConvexPolyhedron({
176
+ faces,
177
+ vertices
178
+ });
179
+
180
+ return shape;
181
+ }
182
+
183
+ function createTrimesh (params: TrimeshParameters): Trimesh {
184
+ const { vertices, indices } = params
185
+ const shape = new Trimesh(
186
+ vertices as unknown as number[],
187
+ indices as unknown as number[],
188
+ );
189
+
190
+ return shape;
191
+ }
192
+
193
+ /******************************************************************************
194
+ * Shape parameters
195
+ */
196
+
197
+ function getBoxParameters (geometry: BufferGeometry): ShapeParameters<ShapeType.BOX> | null {
198
+ const vertices = getVertices(geometry);
199
+
200
+ if (!vertices.length) return null;
201
+
202
+ geometry.computeBoundingBox();
203
+ const box = geometry.boundingBox!;
204
+
205
+ return {
206
+ type: ShapeType.BOX,
207
+ params: {
208
+ x: (box.max.x - box.min.x) / 2,
209
+ y: (box.max.y - box.min.y) / 2,
210
+ z: (box.max.z - box.min.z) / 2,
211
+ },
212
+ };
213
+ }
214
+
215
+ /** Bounding box needs to be computed with the entire subtree, not just geometry. */
216
+ function getBoundingBoxParameters (object: Object3D): ShapeParameters<ShapeType.BOX> | null {
217
+ const clone = object.clone();
218
+ clone.quaternion.set(0, 0, 0, 1);
219
+ clone.updateMatrixWorld();
220
+
221
+ const box = new Box3().setFromObject(clone);
222
+
223
+ if (!isFinite(box.min.lengthSq())) return null;
224
+
225
+ const localPosition = box.translate(clone.position.negate()).getCenter(new Vector3());
226
+
227
+ return {
228
+ type: ShapeType.BOX,
229
+ params: {
230
+ x: (box.max.x - box.min.x) / 2,
231
+ y: (box.max.y - box.min.y) / 2,
232
+ z: (box.max.z - box.min.z) / 2,
233
+ },
234
+ offset: localPosition.lengthSq()
235
+ ? new Vec3(localPosition.x, localPosition.y, localPosition.z)
236
+ : undefined,
237
+ };
238
+ }
239
+
240
+ /** Computes 3D convex hull as a CANNON.ConvexPolyhedron. */
241
+ function getConvexPolyhedronParameters (object: Object3D): ShapeParameters<ShapeType.HULL> | null {
242
+ const geometry = getGeometry(object);
243
+
244
+ if (!geometry) return null;
245
+
246
+ // Perturb.
247
+ const eps = 1e-4;
248
+ for (let i = 0; i < geometry.attributes.position.count; i++) {
249
+ geometry.attributes.position.setXYZ(
250
+ i,
251
+ geometry.attributes.position.getX(i) + (Math.random() - 0.5) * eps,
252
+ geometry.attributes.position.getY(i) + (Math.random() - 0.5) * eps,
253
+ geometry.attributes.position.getZ(i) + (Math.random() - 0.5) * eps,
254
+ );
255
+ }
256
+
257
+ // Compute the 3D convex hull and collect convex hull vertices and faces.
258
+ const [ positions, indices ] = convexHullToJSON(new ConvexHull().setFromObject(new Mesh(geometry)));
259
+
260
+ return {
261
+ type: ShapeType.HULL,
262
+ params: {
263
+ vertices: new Float32Array(positions),
264
+ faces: indices,
265
+ },
266
+ };
267
+ }
268
+
269
+ function getCylinderParameters (
270
+ geometry: CylinderGeometry
271
+ ): ShapeParameters<ShapeType.CYLINDER> | null {
272
+ const params = geometry.parameters;
273
+
274
+ return {
275
+ type: ShapeType.CYLINDER,
276
+ params: {
277
+ radiusTop: params.radiusTop,
278
+ radiusBottom: params.radiusBottom,
279
+ height: params.height,
280
+ segments: params.radialSegments,
281
+ },
282
+ orientation: new CQuaternion()
283
+ .setFromEuler(MathUtils.degToRad(-90), 0, 0, 'XYZ')
284
+ .normalize(),
285
+ }
286
+ }
287
+
288
+ function getBoundingCylinderParameters (
289
+ object: Object3D,
290
+ options: ShapeOptions
291
+ ): ShapeParameters<ShapeType.CYLINDER> | null {
292
+ const axes = ['x', 'y', 'z'];
293
+ const majorAxis = options.cylinderAxis || 'y';
294
+ const minorAxes = axes.splice(axes.indexOf(majorAxis), 1) && axes;
295
+ const box = new Box3().setFromObject(object);
296
+
297
+ if (!isFinite(box.min.lengthSq())) return null;
298
+
299
+ // Compute cylinder dimensions.
300
+ const height = box.max[majorAxis] - box.min[majorAxis];
301
+ const radius = 0.5 * Math.max(
302
+ getComponent(box.max, minorAxes[0]) - getComponent(box.min, minorAxes[0]),
303
+ getComponent(box.max, minorAxes[1]) - getComponent(box.min, minorAxes[1]),
304
+ );
305
+
306
+ const eulerX = majorAxis === 'y' ? PI_2 : 0;
307
+ const eulerY = majorAxis === 'z' ? PI_2 : 0;
308
+
309
+ return {
310
+ type: ShapeType.CYLINDER,
311
+ params: {
312
+ radiusTop: radius,
313
+ radiusBottom: radius,
314
+ height,
315
+ segments: 12,
316
+ },
317
+ orientation: new CQuaternion()
318
+ .setFromEuler(eulerX, eulerY, 0, 'XYZ')
319
+ .normalize(),
320
+ offset: new Vec3(
321
+ (box.max.x + box.min.x) / 2,
322
+ (box.max.y + box.min.y) / 2,
323
+ (box.max.z + box.min.z) / 2,
324
+ ),
325
+ };
326
+ }
327
+
328
+ function getPlaneParameters (geometry: BufferGeometry): ShapeParameters<ShapeType.BOX> | null {
329
+ geometry.computeBoundingBox();
330
+ const box = geometry.boundingBox!;
331
+
332
+ return {
333
+ type: ShapeType.BOX,
334
+ params: {
335
+ x: (box.max.x - box.min.x) / 2 || 0.1,
336
+ y: (box.max.y - box.min.y) / 2 || 0.1,
337
+ z: (box.max.z - box.min.z) / 2 || 0.1,
338
+ },
339
+ };
340
+ }
341
+
342
+ function getSphereParameters (geometry: SphereGeometry): ShapeParameters<ShapeType.SPHERE> | null {
343
+ return {
344
+ type: ShapeType.SPHERE,
345
+ params: { radius: geometry.parameters.radius },
346
+ };
347
+ }
348
+
349
+ function getBoundingSphereParameters (
350
+ object: Object3D,
351
+ options: ShapeOptions
352
+ ): ShapeParameters<ShapeType.SPHERE> | null {
353
+ if (options.sphereRadius) {
354
+ return {
355
+ type: ShapeType.SPHERE,
356
+ params: { radius: options.sphereRadius },
357
+ };
358
+ }
359
+ const geometry = getGeometry(object);
360
+ if (!geometry) return null;
361
+ geometry.computeBoundingSphere();
362
+ const sphere = geometry.boundingSphere;
363
+ if(!sphere) return null;
364
+ if(!isFinite(sphere.radius)) return null;
365
+
366
+ // const localPosition = sphere.center.clone().sub(object.position);
367
+
368
+ return {
369
+ type: ShapeType.SPHERE,
370
+ params: { radius: geometry.boundingSphere!.radius },
371
+ // offset: localPosition.lengthSq()
372
+ // ? new Vec3(localPosition.x, localPosition.y, localPosition.z)
373
+ // : undefined,
374
+ };
375
+ }
376
+
377
+ function getTrimeshParameters (geometry: BufferGeometry): ShapeParameters<ShapeType.MESH> | null {
378
+ const vertices = getVertices(geometry);
379
+
380
+ if (!vertices.length) return null;
381
+
382
+ const indices = new Uint32Array(vertices.length);
383
+ for (let i = 0; i < vertices.length; i++) {
384
+ indices[i] = i;
385
+ }
386
+
387
+ return {
388
+ type: ShapeType.MESH,
389
+ params: {
390
+ vertices,
391
+ indices,
392
+ },
393
+ };
394
+ }
395
+
396
+ function convexHullToJSON(hull: ConvexHull) {
397
+ // Original ('src') indices do not include interior vertices,
398
+ // but 'hull.vertices' (the list they index) does. Output ('dst')
399
+ // arrays have interior vertices omitted.
400
+
401
+ const srcIndices = hull.faces.map((f) => {
402
+ const indices = [];
403
+ let edge = f.edge;
404
+ do {
405
+ const vertex = edge.head()
406
+ const index = hull.vertices.indexOf(vertex)
407
+ if(index >=0) indices.push(index);
408
+ edge = edge.next;
409
+ } while (edge !== f.edge);
410
+ return indices;
411
+ });
412
+ const uniqueSrcIndices = Array.from(new Set(srcIndices.flat())).sort();
413
+
414
+ // Output vertex positions, omitting interior vertices.
415
+ const dstPositions = [];
416
+ for (let i = 0; i < uniqueSrcIndices.length; i++) {
417
+ dstPositions.push(
418
+ hull.vertices[uniqueSrcIndices[i]].point.x,
419
+ hull.vertices[uniqueSrcIndices[i]].point.y,
420
+ hull.vertices[uniqueSrcIndices[i]].point.z,
421
+ );
422
+ }
423
+
424
+ // Mapping from 'src' (hull.vertices) to 'dst' (dstPositions) indices.
425
+ const srcToDstIndexMap = new Map();
426
+ for (let i = 0; i < uniqueSrcIndices.length; i++) {
427
+ srcToDstIndexMap.set(uniqueSrcIndices[i], i);
428
+ }
429
+
430
+ // Output triangles, as indices on dstPositions.
431
+ const dstIndices = [];
432
+ for (let i = 0; i < srcIndices.length; i++) {
433
+ dstIndices.push([
434
+ srcToDstIndexMap.get(srcIndices[i][0]),
435
+ srcToDstIndexMap.get(srcIndices[i][1]),
436
+ srcToDstIndexMap.get(srcIndices[i][2]),
437
+ ]);
438
+ }
439
+
440
+ return [dstPositions, dstIndices] as const;
441
+ }