Skip to content

Commit 063198a

Browse files
committed
Use connected faces from generated convex hull
Motivation: - Generated convex hulls did not have connected faces, which is required for convex-convex collision in cannon-es - Using the connected faces generated by ConvexHull addresses this - Fixes donmccurdy#76
1 parent 144fd6b commit 063198a

File tree

3 files changed

+72
-30
lines changed

3 files changed

+72
-30
lines changed

lib/ConvexHull.d.ts

+6
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@ declare class Face {
1010
normal: Vector3;
1111
}
1212

13+
declare class VertexNode {
14+
point: Vector3;
15+
}
16+
1317
declare class ConvexHull {
1418
public faces: Face[];
19+
public vertices: VertexNode[];
1520
setFromObject(mesh: Mesh): this;
21+
collectFacesAndVertices(): { faces: number[][]; vertices: Vector3[]; }
1622
}

lib/ConvexHull.js

+51-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import {
2-
BufferGeometry,
32
Line3,
43
Plane,
54
Triangle,
@@ -42,6 +41,41 @@ var ConvexHull = ( function () {
4241

4342
Object.assign( ConvexHull.prototype, {
4443

44+
collectFacesAndVertices: function () {
45+
// Get face vertex indices.
46+
// These are the indices of all input vertices, including vertices inside the convex hull.
47+
const faceIndices = this.faces.map((f) => f.collectIndices());
48+
49+
// Get distinct vertex indices referenced by convex hull faces.
50+
const referencedFaceIndices = Array.from(
51+
new Set(faceIndices.flatMap((f) => f)).values()
52+
).sort((a, b) => a - b);
53+
54+
// Create a mapping between original vertex indices to new vertex indices.
55+
const originalToNewIndex = new Map();
56+
for (let i = 0; i < referencedFaceIndices.length; i++) {
57+
originalToNewIndex.set(referencedFaceIndices[i], i);
58+
}
59+
60+
// Get faces with remapped vertex indices.
61+
const faces = [];
62+
for (let i = 0; i < faceIndices.length; i++) {
63+
const indices = faceIndices[i];
64+
const a = originalToNewIndex.get(indices[0]);
65+
const b = originalToNewIndex.get(indices[1]);
66+
const c = originalToNewIndex.get(indices[2]);
67+
faces.push([a, b, c]);
68+
}
69+
70+
// Get vertices referenced by faces.
71+
const vertices = [];
72+
for (let i = 0; i < referencedFaceIndices.length; i++) {
73+
vertices.push(this.vertices[referencedFaceIndices[i]].point);
74+
}
75+
76+
return { faces, vertices };
77+
},
78+
4579
setFromPoints: function ( points ) {
4680

4781
if ( Array.isArray( points ) !== true ) {
@@ -60,7 +94,7 @@ var ConvexHull = ( function () {
6094

6195
for ( var i = 0, l = points.length; i < l; i ++ ) {
6296

63-
this.vertices.push( new VertexNode( points[ i ] ) );
97+
this.vertices.push( new VertexNode( points[ i ], i ) );
6498

6599
}
66100

@@ -980,6 +1014,16 @@ var ConvexHull = ( function () {
9801014

9811015
Object.assign( Face.prototype, {
9821016

1017+
collectIndices: function () {
1018+
const indices = [];
1019+
let edge = this.edge;
1020+
do {
1021+
indices.push(edge.head().index);
1022+
edge = edge.next;
1023+
} while (edge !== this.edge);
1024+
return indices;
1025+
},
1026+
9831027
getEdge: function ( i ) {
9841028

9851029
var edge = this.edge;
@@ -1105,12 +1149,15 @@ var ConvexHull = ( function () {
11051149

11061150
// A vertex as a double linked list node.
11071151

1108-
function VertexNode( point ) {
1152+
function VertexNode( point, index ) {
11091153

11101154
this.point = point;
1155+
// index in the input array
1156+
this.index = index;
11111157
this.prev = null;
11121158
this.next = null;
1113-
this.face = null; // the face that is able to see this vertex
1159+
// the face that is able to see this vertex
1160+
this.face = null;
11141161

11151162
}
11161163

src/index.ts

+15-26
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { Box, Quaternion as CQuaternion, ConvexPolyhedron, Cylinder, Shape, Sphere, Trimesh, Vec3 } from 'cannon-es';
1+
import { Box, ConvexPolyhedron, Cylinder, Quaternion as CQuaternion, Shape, Sphere, Trimesh, Vec3 } from 'cannon-es';
22
import { Box3, BufferGeometry, CylinderGeometry, MathUtils, Mesh, Object3D, SphereGeometry, Vector3 } from 'three';
3-
import { ConvexHull } from '../lib/ConvexHull.js';
3+
import { ConvexHull } from '../lib/ConvexHull';
44
import { getComponent, getGeometry, getVertices } from './utils';
55

66
const PI_2 = Math.PI / 2;
@@ -254,34 +254,23 @@ function getConvexPolyhedronParameters (object: Object3D): ShapeParameters<Shape
254254
);
255255
}
256256

257-
// Compute the 3D convex hull.
258-
const hull = new ConvexHull().setFromObject(new Mesh(geometry));
259-
const hullFaces = hull.faces;
260-
const vertices: number[] = [];
261-
const faces: number[][] = [];
262-
263-
let currentFaceVertex = 0;
264-
for (let i = 0; i < hullFaces.length; i++) {
265-
const hullFace = hullFaces[ i ];
266-
const face: number[] = [];
267-
faces.push(face);
268-
269-
let edge = hullFace.edge;
270-
do {
271-
const point = edge.head().point;
272-
vertices.push(point.x, point.y, point.z);
273-
face.push(currentFaceVertex);
274-
currentFaceVertex++;
275-
edge = edge.next;
276-
} while ( edge !== hullFace.edge );
257+
// Compute the 3D convex hull and collect convex hull vertices and faces.
258+
const { vertices: verticesArray, faces } = new ConvexHull()
259+
.setFromObject(new Mesh(geometry))
260+
.collectFacesAndVertices();
261+
262+
const vertices = new Float32Array(verticesArray.length * 3);
263+
for (let i = 0; i < verticesArray.length; i++) {
264+
const { x, y, z } = verticesArray[i];
265+
const v = i * 3;
266+
vertices[v] = x;
267+
vertices[v + 1] = y;
268+
vertices[v + 2] = z;
277269
}
278270

279-
const verticesTypedArray = new Float32Array(vertices.length);
280-
verticesTypedArray.set(vertices);
281-
282271
return {
283272
type: ShapeType.HULL,
284-
params: { vertices: verticesTypedArray, faces },
273+
params: { vertices, faces },
285274
};
286275
}
287276

0 commit comments

Comments
 (0)