Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support ray intersects mesh instances #989

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/framework/components/camera/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,21 @@ pc.extend(pc, function () {
this.cameras.sort(function (a, b) {
return a.priority - b.priority;
});
},

raycastMeshInstances: function (ray, meshInstances) {
var i = 0;
var intersects = [];

for (i = 0; i < meshInstances.length; i++) {
meshInstances[i].intersectsRay(ray, intersects);
}

if (intersects.length === 0) {
return null;
}

return intersects.sort(function (a, b) { return a.distance - b.distance; });
}
});

Expand Down
170 changes: 170 additions & 0 deletions src/scene/mesh.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,176 @@ pc.extend(pc, function () {
this.parameters = {};
};

MeshInstance.prototype.intersectsRay = (function() {
var localRay = new pc.Ray();
var distance = new pc.Vec3();
var aabb = new pc.BoundingBox();

var points = [new pc.Vec3(), new pc.Vec3(), new pc.Vec3()];

var edge1 = new pc.Vec3();
var edge2 = new pc.Vec3();
var normal = new pc.Vec3();

var localTransform = new pc.Mat4();
var worldTransform = new pc.Mat4();

var localCoord = new pc.Vec3();
var worldCoord = new pc.Vec3();

var red = new pc.Color(1, 0, 0, 1);

function checkIntersection(meshInstance, i, worldRay, a, b, c, point) {
var backfaceCulling = (
meshInstance.material.cull === pc.CULLFACE_BACK ||
meshInstance.material.cull === pc.CULLFACE_FRONTANDBACK
);

var intersect;

if (meshInstance.skinInstance) {
intersect = worldRay.intersectTriangle(a, b, c, backfaceCulling, point);
} else {
intersect = localRay.intersectTriangle(a, b, c, backfaceCulling, point);
}

if (intersect === null) return null;

edge1.sub2(b, a);
edge2.sub2(c, a);
normal.cross(edge1, edge2).normalize();

localCoord.copy(intersect);
worldCoord.copy(intersect);

if (meshInstance.skinInstance) {
localTransform.transformPoint(localCoord, localCoord);
} else {
worldTransform.transformPoint(worldCoord, worldCoord);
worldTransform.transformPoint(a, a);
worldTransform.transformPoint(b, b);
worldTransform.transformPoint(c, c);
worldTransform.transformVector(normal, normal);
}

distance.sub2(worldCoord, worldRay.origin);

return {
index: i,
distance: distance.length(),
point: worldCoord.clone(),
localPoint: localCoord.clone(),
normal: normal.clone(),
vertices: [a.clone(), b.clone(), c.clone()],
meshInstance: meshInstance
};
}

return function intersectsRay(worldRay, intersects) {
aabb.copy(this.aabb);

if (aabb.intersectsRay(worldRay) === false) return null;

var vertexBuffer = this.mesh.vertexBuffer;
var indexBuffer = this.mesh.indexBuffer[0];
var base = this.mesh.primitive[0].base;
var count = this.mesh.primitive[0].count;
var dataF = new Float32Array(vertexBuffer.lock());
var data8 = new Uint8Array(vertexBuffer.lock());
var indices = indexBuffer.bytesPerIndex === 2 ? new Uint16Array(indexBuffer.lock()) : new Uint32Array(indexBuffer.lock());
var elems = vertexBuffer.format.elements;
var numVerts = vertexBuffer.numVertices;
var vertSize = vertexBuffer.format.size;
var i, j, k, index;

var offsetP = 0;
var offsetI = 0;
var offsetW = 0;;
var intersect = null;

for (i = 0; i < elems.length; i++) {
if (elems[i].name === pc.SEMANTIC_POSITION) {
offsetP = elems[i].offset;
} else if (elems[i].name === pc.SEMANTIC_BLENDINDICES) {
offsetI = elems[i].offset;
} else if (elems[i].name === pc.SEMANTIC_BLENDWEIGHT) {
offsetW = elems[i].offset;
}
}

var offsetPF = offsetP / 4;
var offsetWF = offsetW / 4;
var vertSizeF = vertSize / 4;

intersects = (intersects === undefined) ? [] : intersects;

localRay.origin.copy(worldRay.origin);
localRay.direction.copy(worldRay.direction);

worldTransform.copy(this.node.getWorldTransform());
localTransform.copy(worldTransform).invert();

localTransform.transformPoint(localRay.origin, localRay.origin);
localTransform.transformVector(localRay.direction, localRay.direction);


if (this.skinInstance) {
var boneIndices = [0, 0, 0, 0];
var boneWeights = [0, 0, 0, 0];
var boneMatrices = this.skinInstance.matrices;
var boneWeightVertices = [new pc.Vec3(), new pc.Vec3(), new pc.Vec3(), new pc.Vec3()];

for (i = base; i < base + count; i += 3) {
for (j = 0; j < 3; j++) {

index = indices[i + j];

for (k = 0; k < 4; k++) {
boneIndices[k] = data8[index * vertSize + offsetI + k];
boneWeights[k] = dataF[index * vertSizeF + offsetPF + offsetWF + k];
}

index = index * vertSizeF + offsetPF;
points[j].set(dataF[index], dataF[index + 1], dataF[index + 2]);

for (k = 0; k < 4; k++) {
boneMatrices[boneIndices[k]].transformPoint(points[j], boneWeightVertices[k]);
boneWeightVertices[k].scale(boneWeights[k]);
}

points[j].copy(boneWeightVertices[0]).add(boneWeightVertices[1]).add(boneWeightVertices[2]).add(boneWeightVertices[3]);
}

intersect = checkIntersection(this, i, worldRay, points[0], points[1], points[2]);

if (intersect) {
intersects.push(intersect);
}
}

} else {
for (i = base; i < base + count; i += 3) {

for (j = 0; j < 3; j++) {
index = indices[i + j] * vertSizeF + offsetPF;
points[j].set(dataF[index], dataF[index + 1], dataF[index + 2]);
}

intersect = checkIntersection(this, i, worldRay, points[0], points[1], points[2]);

if (intersect) {
intersects.push(intersect);
}
}
}

vertexBuffer.unlock();
indexBuffer.unlock();
Comment on lines +302 to +303
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to unlock, as we read only from it .. otherwise gpu version is updated


return intersects.length > 0 ? intersects : null;
}
})();

Object.defineProperty(MeshInstance.prototype, 'mesh', {
get: function () {
return this._mesh;
Expand Down
46 changes: 45 additions & 1 deletion src/shape/ray.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pc.extend(pc, function () {
pc.extend(pc, function() {
/**
* @name pc.Ray
* @class An infinite ray
Expand All @@ -17,6 +17,50 @@ pc.extend(pc, function () {
this.direction = direction || new pc.Vec3(0, 0, -1);
};

Ray.prototype.intersectTriangle = (function() {
var diff = new pc.Vec3();
var edge1 = new pc.Vec3();
var edge2 = new pc.Vec3();
var normal = new pc.Vec3();

return function intersectTriangle(a, b, c, backfaceCulling, res) {
res = (res === undefined) ? new pc.Vec3() : res;
edge1.sub2(b, a);
edge2.sub2(c, a);
normal.cross(edge1, edge2);

var DdN = this.direction.dot(normal);
var sign;

if (DdN > 0) {
if (backfaceCulling) return null;
sign = 1;
} else if (DdN < 0) {
sign = -1;
DdN = -DdN;
} else {
return null;
}

diff.sub2(this.origin, a);

var DdQxE2 = sign * this.direction.dot(edge2.cross(diff, edge2));

if (DdQxE2 < 0) return null;

var DdE1xQ = sign * this.direction.dot(edge1.cross(edge1, diff));

if (DdE1xQ < 0) return null;
if (DdQxE2 + DdE1xQ > DdN) return null;

var QdN = -sign * diff.dot(normal);

if (QdN < 0) return null;

return res.copy(this.direction).scale(QdN / DdN).add(this.origin);
};
})();

return {
Ray: Ray
};
Expand Down