So you need to put the collider in an Entity child of your object, so you can move it. But that won’t help with physics if you are doing real physics - no compound colliders have been implemented last time I looked. (Ended up writing my own collision detection, interpenetration prevention and raycasting solution for this and performance reasons).
I’ve got a quick hull implementation from that if you need it. That will output what I need for a collision surface (but might not be enough to make a PC mesh collider without work).
/*
* QuickHull function to produce convex hulls and faces
*/
export default (function () {
/*
QuickHull
---------
The MIT License
Copyright © 2010-2014 three.js authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@author mark lundin / http://mark-lundin.com
This is a 3D implementation of the Quick Hull algorithm.
It is a fast way of computing a convex hull with average complexity
of O(n log(n)).
It uses depends on three.js and is supposed to create THREE.Geometry.
It's also very messy
*/
var w = new pc.Vec3();
var faces = [],
faceStack = [],
i, NUM_POINTS, extremes,
max = 0,
dCur, current, j, v0, v1, v2, v3,
N = new pc.Vec3(), D;
var ab, ac, ax,
subA, subB, normal,
diff, subAA, subBB, subC;
function reset() {
ab = new pc.Vec3();
ac = new pc.Vec3();
ax = new pc.Vec3();
subA = new pc.Vec3();
subB = new pc.Vec3();
normal = new pc.Vec3();
diff = new pc.Vec3();
subAA = new pc.Vec3();
subBB = new pc.Vec3();
subC = new pc.Vec3();
}
//temporary vectors
function process(points) {
// Iterate through all the faces and remove
while (faceStack.length > 0) {
cull(faceStack.shift(), points);
}
}
var ca = new pc.Vec3();
var ba = new pc.Vec3();
var norm = function () {
return function (a, b, c) {
ca.copy(c).sub(a);
ba.copy(b).sub(a);
N.cross(ca, ba);
return N.normalize();
}
}();
function getNormal(face, points) {
if (face.normal !== undefined) return face.normal;
var p0 = points[face[0]],
p1 = points[face[1]],
p2 = points[face[2]];
ab.copy(p1).sub(p0);
ac.copy(p2).sub(p0);
normal.cross(ac, ab);
normal.normalize();
return face.normal = normal.clone();
}
function assignPoints(face, pointset, points) {
var p0 = points[face[0]],
dots = [], apex,
norm = getNormal(face, points);
pointset.sort(function (aItem, bItem) {
dots[aItem.x / 3] =
dots[aItem.x / 3] !== undefined ? dots[aItem.x / 3] : norm.dot(subA.copy(aItem).sub(p0));
dots[bItem.x / 3] =
dots[bItem.x / 3] !== undefined ? dots[bItem.x / 3] : norm.dot(subB.copy(bItem).sub(p0));
return dots[aItem.x / 3] - dots[bItem.x / 3];
});
var index = pointset.length;
if (index === 1) dots[pointset[0].x / 3] = norm.dot(subA.copy(pointset[0]).sub(p0));
while (index-- > 0 && dots[pointset[index].x / 3] > 0) {
var point;
}
if (index + 1 < pointset.length && dots[pointset[index + 1].x / 3] > 0) {
face.visiblePoints = pointset.splice(index + 1);
}
}
function cull(face, points) {
var i = faces.length,
dot, visibleFace, currentFace,
visibleFaces = [face];
var apex = points.indexOf(face.visiblePoints.pop());
// Iterate through all other faces...
while (i-- > 0) {
currentFace = faces[i];
if (currentFace !== face) {
// ...and check if they're pointing in the same direction
dot = getNormal(currentFace, points).dot(diff.copy(points[apex]).sub(points[currentFace[0]]));
if (dot > 0) {
visibleFaces.push(currentFace);
}
}
}
var index, neighbouringIndex, vertex;
var j = i = visibleFaces.length;
var isDistinct = false,
hasOneVisibleFace = i === 1,
cull = [],
perimeter = [],
edgeIndex = 0, compareFace, nextIndex,
a, b;
var allPoints = [];
var originFace = [
visibleFaces[0][0],
visibleFaces[0][1],
visibleFaces[0][1],
visibleFaces[0][2],
visibleFaces[0][2],
visibleFaces[0][0]
];
if (visibleFaces.length === 1) {
currentFace = visibleFaces[0];
perimeter =
[currentFace[0], currentFace[1], currentFace[1], currentFace[2], currentFace[2], currentFace[0]];
// remove visible face from list of faces
if (faceStack.indexOf(currentFace) > -1) {
faceStack.splice(faceStack.indexOf(currentFace), 1);
}
if (currentFace.visiblePoints) allPoints = allPoints.concat(currentFace.visiblePoints);
faces.splice(faces.indexOf(currentFace), 1);
} else {
while (i-- > 0) { // for each visible face
currentFace = visibleFaces[i];
// remove visible face from list of faces
if (faceStack.indexOf(currentFace) > -1) {
faceStack.splice(faceStack.indexOf(currentFace), 1);
}
if (currentFace.visiblePoints) allPoints = allPoints.concat(currentFace.visiblePoints);
faces.splice(faces.indexOf(currentFace), 1);
var isSharedEdge;
var cEdgeIndex = 0;
while (cEdgeIndex < 3) { // Iterate through it's edges
isSharedEdge = false;
j = visibleFaces.length;
a = currentFace[cEdgeIndex];
b = currentFace[(cEdgeIndex + 1) % 3];
while (j-- > 0 && !isSharedEdge) { // find another visible faces
compareFace = visibleFaces[j];
edgeIndex = 0;
// isSharedEdge = compareFace == currentFace;
if (compareFace !== currentFace) {
while (edgeIndex < 3 && !isSharedEdge) { //Check all it's indices
nextIndex = ( edgeIndex + 1 );
isSharedEdge =
( compareFace[edgeIndex] === a && compareFace[nextIndex % 3] === b ) ||
( compareFace[edgeIndex] === b && compareFace[nextIndex % 3] === a );
edgeIndex++;
}
}
}
if (!isSharedEdge || hasOneVisibleFace) {
perimeter.push(a);
perimeter.push(b);
}
cEdgeIndex++;
}
}
}
// create new face for all pairs around edge
i = 0;
var l = perimeter.length / 2;
var f;
while (i < l) {
f = [perimeter[i * 2 + 1], apex, perimeter[i * 2]];
assignPoints(f, allPoints, points);
faces.push(f);
if (f.visiblePoints !== undefined)faceStack.push(f);
i++;
}
}
var distSqPointSegment = function () {
var ab = new pc.Vec3(),
ac = new pc.Vec3(),
bc = new pc.Vec3();
return function (a, b, c) {
ab.copy(b).sub(a);
ac.copy(c).sub(a);
bc.copy(c).sub(b);
var e = ac.dot(ab);
if (e < 0.0) return ac.dot(ac);
var f = ab.dot(ab);
if (e >= f) return bc.dot(bc);
return ac.dot(ac) - e * e / f;
}
}();
return function (points) {
reset();
faces = [];
faceStack = [];
i = NUM_POINTS = points.length;
extremes = points.slice(0, 6);
max = 0;
/*
* FIND EXTREMETIES
*/
while (i-- > 0) {
if (points[i].x < extremes[0].x) extremes[0] = points[i];
if (points[i].x > extremes[1].x) extremes[1] = points[i];
if (points[i].y < extremes[2].y) extremes[2] = points[i];
if (points[i].y < extremes[3].y) extremes[3] = points[i];
if (points[i].z < extremes[4].z) extremes[4] = points[i];
if (points[i].z < extremes[5].z) extremes[5] = points[i];
}
/*
* Find the longest line between the extremeties
*/
j = i = 6;
while (i-- > 0) {
j = i - 1;
while (j-- > 0) {
if (max < (dCur = w.copy(extremes[i]).sub(extremes[j]).lengthSq())) {
max = dCur;
v0 = extremes[i];
v1 = extremes[j];
}
}
}
// 3. Find the most distant point to the line segment, this creates a plane
i = 6;
max = 0;
while (i-- > 0) {
dCur = distSqPointSegment(v0, v1, extremes[i]);
if (max < dCur) {
max = dCur;
v2 = extremes[i];
}
}
// 4. Find the most distant point to the plane.
N = norm(v0, v1, v2);
D = N.dot(v0);
max = 0;
i = NUM_POINTS;
while (i-- > 0) {
dCur = Math.abs(points[i].dot(N) - D);
if (max < dCur) {
max = dCur;
v3 = points[i];
}
}
var v0Index = points.indexOf(v0),
v1Index = points.indexOf(v1),
v2Index = points.indexOf(v2),
v3Index = points.indexOf(v3);
// We now have a tetrahedron as the base geometry.
// Now we must subdivide the
var tetrahedron = [
[v2Index, v1Index, v0Index],
[v1Index, v3Index, v0Index],
[v2Index, v3Index, v1Index],
[v0Index, v3Index, v2Index]
];
subAA.copy(v1).sub(v0).normalize();
subBB.copy(v2).sub(v0).normalize();
subC.copy(v3).sub(v0).normalize();
var sign = subC.dot(new pc.Vec3().cross(subBB, subAA));
// Reverse the winding if negative sign
if (sign < 0) {
tetrahedron[0].reverse();
tetrahedron[1].reverse();
tetrahedron[2].reverse();
tetrahedron[3].reverse();
}
//One for each face of the pyramid
var pointsCloned = points.slice();
pointsCloned.splice(pointsCloned.indexOf(v0), 1);
pointsCloned.splice(pointsCloned.indexOf(v1), 1);
pointsCloned.splice(pointsCloned.indexOf(v2), 1);
pointsCloned.splice(pointsCloned.indexOf(v3), 1);
var i = tetrahedron.length;
while (i-- > 0) {
assignPoints(tetrahedron[i], pointsCloned, points);
if (tetrahedron[i].visiblePoints !== undefined) {
faceStack.push(tetrahedron[i]);
}
faces.push(tetrahedron[i]);
}
process(points);
// Assign to our geometry object
var ll = faces.length;
var output = [];
var dedupe = {};
var id = 0;
//Build vertex connectivity
for (i = 0; i < ll; i++) {
var face = faces[i];
var v = dedupe[face[0]] || (dedupe[face[0]] = {
id: id++,
connections: [],
directions: [],
connected: {},
point: points[face[0]]
});
v.connected[face[0]] = true;
if (!v.connected[face[1]]) v.connections.push(face[1]);
if (!v.connected[face[2]]) v.connections.push(face[2]);
v.connected[face[1]] = true;
v.connected[face[2]] = true;
v = dedupe[face[1]] || (dedupe[face[1]] = {
id: id++,
connections: [],
directions: [],
connected: {},
point: points[face[1]]
});
v.connected[face[1]] = true;
if (!v.connected[face[0]]) v.connections.push(face[0]);
if (!v.connected[face[2]]) v.connections.push(face[2]);
v.connected[face[0]] = true;
v.connected[face[2]] = true;
v = dedupe[face[2]] || (dedupe[face[2]] = {
id: id++,
connections: [],
directions: [],
connected: {},
point: points[face[2]]
});
v.connected[face[2]] = true;
if (!v.connected[face[1]]) v.connections.push(face[1]);
if (!v.connected[face[0]]) v.connections.push(face[0]);
v.connected[face[1]] = true;
v.connected[face[0]] = true;
}
for (i = 0; i < ll; i++) {
var face = faces[i];
for (var j = 0; j < 3; j++) {
face[j] = dedupe[face[j]].id;
}
}
for (i = 0; i < id; i++) {
output.push(null);
}
//Create hill climb data
for (var k in dedupe) {
var vertex = dedupe[k];
vertex.length = vertex.point.length();
vertex.inverseLength = 1 / vertex.length;
for (var c = 0, l = vertex.connections.length; c < l; c++) {
var other = dedupe[vertex.connections[c]];
vertex.directions.push(V(other.point).sub(vertex.point).normalize().clone());
vertex.connections[c] = other.id;
}
output[vertex.id] = vertex;
}
return {
points: output,
faces: faces
};
}
}());
Actually that code needs my “V” and “Q” functions that allocate temporary pc.Vec3 and pc.Quat without garbage