Skip to content

Commit 0fba255

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 0fba255

File tree

3 files changed

+75
-31
lines changed

3 files changed

+75
-31
lines changed

lib/ConvexHull.d.ts

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

13+
declare class VertexNode {
14+
point: Vector3;
15+
index: number;
16+
}
17+
1318
declare class ConvexHull {
1419
public faces: Face[];
20+
public vertices: VertexNode[];
1521
setFromObject(mesh: Mesh): this;
22+
collectFaces(): number[][];
23+
collectFacesAndVertices(): { faces: number[][]; vertices: Vector3[]; }
1624
}

lib/ConvexHull.js

+52-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import {
2-
BufferGeometry,
32
Line3,
43
Plane,
54
Triangle,
65
Vector3
76
} from 'three';
7+
88
/**
99
* Ported from: https://github.com/maurizzzio/quickhull3d/ by Mauricio Poppe (https://github.com/maurizzzio)
1010
*/
@@ -42,6 +42,41 @@ var ConvexHull = ( function () {
4242

4343
Object.assign( ConvexHull.prototype, {
4444

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

4782
if ( Array.isArray( points ) !== true ) {
@@ -60,7 +95,7 @@ var ConvexHull = ( function () {
6095

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

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

65100
}
66101

@@ -980,6 +1015,16 @@ var ConvexHull = ( function () {
9801015

9811016
Object.assign( Face.prototype, {
9821017

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

9851030
var edge = this.edge;
@@ -1105,12 +1150,15 @@ var ConvexHull = ( function () {
11051150

11061151
// A vertex as a double linked list node.
11071152

1108-
function VertexNode( point ) {
1153+
function VertexNode( point, index ) {
11091154

11101155
this.point = point;
1156+
// index in the input array
1157+
this.index = index;
11111158
this.prev = null;
11121159
this.next = null;
1113-
this.face = null; // the face that is able to see this vertex
1160+
// the face that is able to see this vertex
1161+
this.face = null;
11141162

11151163
}
11161164

src/index.ts

+15-27
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,22 @@ 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+
// Get 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 v = i * 3;
265+
vertices[v] = verticesArray[i].x;
266+
vertices[v + 1] = verticesArray[i].y;
267+
vertices[v + 2] = verticesArray[i].z;
277268
}
278269

279-
const verticesTypedArray = new Float32Array(vertices.length);
280-
verticesTypedArray.set(vertices);
281-
282270
return {
283271
type: ShapeType.HULL,
284-
params: { vertices: verticesTypedArray, faces },
272+
params: { vertices, faces },
285273
};
286274
}
287275

@@ -373,7 +361,7 @@ function getBoundingSphereParameters (
373361
const geometry = getGeometry(object);
374362
if (!geometry) return null;
375363
geometry.computeBoundingSphere();
376-
364+
377365
return {
378366
type: ShapeType.SPHERE,
379367
params: { radius: geometry.boundingSphere!.radius },

0 commit comments

Comments
 (0)