Asked 1 year ago by SaturnianCollector099
How can I obtain accurate triangle normals for ground detection using three-mesh-bvh?
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
Asked 1 year ago by SaturnianCollector099
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
I'm using the three-mesh-bvh game example for collision detection. The code below shows how intersections are checked to adjust a capsule collider so it doesn't intersect with geometry:
JAVASCRIPTcollider.geometry.boundsTree.shapecast( { intersectsBounds: box => box.intersectsBox( tempBox ), intersectsTriangle: tri => { // check if the triangle is intersecting the capsule and adjust the // capsule position if it is. const triPoint = tempVector; const capsulePoint = tempVector2; const distance = tri.closestPointToSegment( tempSegment, triPoint, capsulePoint ); if ( distance < capsuleInfo.radius ) { const depth = capsuleInfo.radius - distance; const direction = capsulePoint.sub( triPoint ).normalize(); tempSegment.start.addScaledVector( direction, depth ); tempSegment.end.addScaledVector( direction, depth ); } } } ); // get the adjusted position of the capsule collider in world space after checking // triangle collisions and moving it. capsuleInfo.segment.start is assumed to be // the origin of the player model. const newPosition = tempVector; newPosition.copy( tempSegment.start ).applyMatrix4( collider.matrixWorld ); // check how much the collider was moved const deltaVector = tempVector2; deltaVector.subVectors( newPosition, player.position ); // if the player was primarily adjusted vertically we assume it's on something we should consider ground playerIsOnGround = deltaVector.y > Math.abs( delta * playerVelocity.y * 0.25 );
For ground detection, the current approach checks if the vertical offset (y value) is positive. However, with a velocity-based character controller, falling next to a wall may be mistakenly treated as grounded, which then impedes proper falling behavior.
I attempted to log the normal of the intersected triangle as shown:
JAVASCRIPTcollider.geometry.boundsTree.shapecast( { intersectsBounds: box => box.intersectsBox( tempBox ), intersectsTriangle: tri => { // ... if ( distance < capsuleInfo.radius ) { // ... console.log(tri.plane.normal); } } } );
However, the console always logs (1,0,0) regardless of the actual triangle orientation. Is there a way to directly obtain and use the correct triangle normals during intersection testing for reliable ground detection, or must I resort to raycasting?
Comments 0
•Answers 1
•Views 908
Answer by TychoAI Agent • 9 days ago
Based on your code and issue description, it appears you're trying to access the normal of triangles during collision detection with the three-mesh-bvh library to improve ground detection.
The issue you're encountering where tri.plane.normal
always returns (1,0,0)
suggests that the triangle object provided by the shapecast function doesn't have a properly initialized plane property with the correct normal.
Instead of trying to access tri.plane.normal
, you can calculate the normal of the triangle directly. The tri
object in the intersectsTriangle
callback represents a Triangle object from Three.js, which has three vertices (a, b, c).
Here's how you can calculate the normal:
JAVASCRIPTcollider.geometry.boundsTree.shapecast({ intersectsBounds: box => box.intersectsBox(tempBox), intersectsTriangle: tri => { // check if the triangle is intersecting the capsule and adjust the // capsule position if it is. const triPoint = tempVector; const capsulePoint = tempVector2; const distance = tri.closestPointToSegment(tempSegment, triPoint, capsulePoint); if (distance < capsuleInfo.radius) { const depth = capsuleInfo.radius - distance; const direction = capsulePoint.sub(triPoint).normalize(); // Calculate the normal of the triangle const triangleNormal = new THREE.Vector3(); tri.getNormal(triangleNormal); console.log("Triangle normal:", triangleNormal); // You can now use this normal for ground detection // For example, if the normal's y component is above a threshold, it's likely ground const isGround = triangleNormal.y > 0.7; // Adjust threshold as needed tempSegment.start.addScaledVector(direction, depth); tempSegment.end.addScaledVector(direction, depth); } } });
With the triangle normal, you can implement a more reliable ground detection:
JAVASCRIPTlet potentialGroundNormal = null; let minDistance = Infinity; collider.geometry.boundsTree.shapecast({ intersectsBounds: box => box.intersectsBox(tempBox), intersectsTriangle: tri => { const triPoint = tempVector; const capsulePoint = tempVector2; const distance = tri.closestPointToSegment(tempSegment, triPoint, capsulePoint); if (distance < capsuleInfo.radius) { const depth = capsuleInfo.radius - distance; const direction = capsulePoint.sub(triPoint).normalize(); // Calculate the normal of the triangle const triangleNormal = new THREE.Vector3(); tri.getNormal(triangleNormal); // Store the normal of the closest triangle that could be ground if (triangleNormal.y > 0.7 && distance < minDistance) { minDistance = distance; potentialGroundNormal = triangleNormal.clone(); } tempSegment.start.addScaledVector(direction, depth); tempSegment.end.addScaledVector(direction, depth); } } }); // After shapecast, check if we found ground playerIsOnGround = potentialGroundNormal !== null;
This approach uses the triangle normal's Y component to determine if the surface is facing upward (like ground) rather than sideways (like a wall). You can adjust the threshold (0.7 in this example) based on your needs.
If you need more precise ground detection, you might still want to combine this with your existing approach or use additional raycasting for confirmation.
Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?
No comments yet.
No comments yet.