Rigid body (shape) in bullet/ammo.js from a mesh in three.js - three.js

I am using bullet/ammo.js with three.js. I have a 3d mesh and I want to use the exact shape for collision detection with a soft body. Is there a way I can create a 3d rigid body (in bullet) from a mesh (in three.js)?
Here is an example:
http://kidzinski.com/miamisura/lazy3d/ (please wait a second for the 3d model to download). I have a cloth falling on a 3d body and I need to simulate collision of this cloth with the body.
I am new to these frameworks sorry if I fundamentally misunderstood something.

It looks like you can do some work to turn an arbitrary Three.js mesh into a Bullet concave mesh. This is supported by Physi.js, which is a plug and play solution to link Three.js directly to ammo.js. I personally wouldn't recommend using the project (Physi.js) but you can look at the source code to see how they implement concave meshes.
First they loop over the geometry to create a custom list of "triangle" data objects on these lines of physi.js
for ( i = 0; i < geometry.faces.length; i++ ) {
face = geometry.faces[i];
if ( face instanceof THREE.Face3) {
triangles.push([
...
Then these triangles are passed off to Ammo.js to make a new Ammo.btBvhTriangleMeshShape on these lines:
for ( i = 0; i < description.triangles.length; i++ ) {
...
triangle_mesh.addTriangle( _vec3_1, _vec3_2, _vec3_3, true );
}
...
shape = new Ammo.btBvhTriangleMeshShape( triangle_mesh, true, true );
This should be a good starting point for building your own Ammo.js custom mesh.

There are lots of threads around the web, that Physijs Concave mesh does not work with collission. It seems, that btBvhTriangleMeshShape is not intended to work with collission in ammo.js, as I found out searching for that topic in bullet related forums.
What worked for me, is btConvexHullShape:
var triangle, triangle_mesh = new Ammo.btTriangleMesh;
var btConvexHullShape = new Ammo.btConvexHullShape();
var _vec3_1 = new Ammo.btVector3(0,0,0);
var _vec3_2 = new Ammo.btVector3(0,0,0);
var _vec3_3 = new Ammo.btVector3(0,0,0);
for ( i = 0; i < triangles.length; i++ ) {
triangle = triangles[i];
_vec3_1.setX(triangle[0].x);
_vec3_1.setY(triangle[0].y);
_vec3_1.setZ(triangle[0].z);
btConvexHullShape.addPoint(_vec3_1,true);
_vec3_2.setX(triangle[1].x);
_vec3_2.setY(triangle[1].y);
_vec3_2.setZ(triangle[1].z);
btConvexHullShape.addPoint(_vec3_2,true);
_vec3_3.setX(triangle[2].x);
_vec3_3.setY(triangle[2].y);
_vec3_3.setZ(triangle[2].z);
btConvexHullShape.addPoint(_vec3_3,true);
triangle_mesh.addTriangle(
_vec3_1,
_vec3_2,
_vec3_3,
true
);
}
return btConvexHullShape;
In the process of learning physic based 3d with threejs, I also want to mention the following best practice: when using complex models, create a low poly model that you can push to that converter function instead of the original model, or you will encounter a stack overflow.

Related

Proper method to move plane vertices locations in React-Three-Fiber

I want to create a plane and set the x,y,z locations for the plane individually. So, in three.js it would be something like this ...
var plane = new THREE.PlaneGeometry(planeSize, planeSize, planeDefinition, planeDefinition);
var plane = new THREE.Mesh(plane, new THREE.MeshBasicMaterial({
color: meshColor,
wireframe: true
}));
for (var i = 0; i < plane.vertices.length; i++) {
plane.vertices[i].z += Math.random() * vertexHeight - vertexHeight;
plane.vertices[i]._myZ = plane.vertices[i].z
}
I don't need to update these vertices locations, so I don't need to use a hook of any kind. Is there a parameter to feed vertices locations directly into the react component? Is there a better way to do this? I tried creating the vertices and then feeding them into the vertices parameter with a primitive, but that requires you to make the faces as well and I could not get that to work.
Thanks
You can't feed vertices directly to the component, you'd have to set that imperatively like in Three.js. You could either get a ref to the geometry component and modify it in useLayoutEffect, or create the geometry inside of something like useMemo or useState and assign it to the geometry prop of the <mesh>. Note that PlaneGeometry is now a BufferGeometry in Three.js r125 so you can't set vertices like you've shown anymore.

Aframe create shape from vectors

I'm doing something like this, How to create a custom square in a-frame but with custom shapes (i.e. drawing around an image to make a hotspot to make part of that image interactive)
I've got the line working and I'm now trying to convert this into a fill.
this._mesh = make('a-plane', {
geometry:"buffer: false"
}, this.el)
this._mesh.addEventListener('loaded', e => {
this._mesh.getObject3D('mesh').geometry.vertices = this._points
this._mesh.getObject3D('mesh').geometry.computeFaceNormals()
this._mesh.getObject3D('mesh').geometry.computeVertexNormals()
})
I'm getting close but it's only showing one triangle i.e. something like this
How do i get the shape to fill the whole area? I have done this before with a ConvexGeometry and Quick hull but it seems cumbersome.
I got the idea for updating the vertices of a plane from the above post.
If you create an array with Vector2 objects representing the contour of your shape in CCW order, you can use an instance of Shape and ShapeBufferGeometry to achieve the intended result. Just pass the array of points to the ctor of Shape. The following official three.js example demonstrates this approach:
https://threejs.org/examples/webgl_geometry_shapes
BTW: Instead of defining the contour by an array of points, you can also use the API of Shape to define shapes. A simple triangle would look like so:
var triangleShape = new THREE.Shape()
.moveTo( 80, 20 )
.lineTo( 40, 80 )
.lineTo( 120, 80 )
.lineTo( 80, 20 );
var geometry = new THREE.ShapeBufferGeometry( shape );
var mesh = new THREE.Mesh( geometry, material );
three.js R113

Raycasting in threeJs, when target is over object

I'm fairly new to three.js and trying to get a better understanding of ray casting. I have used it so far in a game to show when an object collides with another on the page which works perfectly. In the game I'm building this is to take health from the hero as it crashes into walls.
I am now trying to implement a target which when hovered over some objects it will auto shoot. However the target only registers a target hit once the object passes through the target mesh rather than when its ray is cast through it.
to further detail this, the ray is cast from the camera through the target, if the target is on a mesh (stored as an object array) then I want it to trigger a function.
within my update function I have this:
var ray = new THREE.Raycaster();
var crossHairClone = crossHair.position.clone();
var coards = {};
coards.x = crossHairClone.x
coards.y = crossHairClone.y
ray.setFromCamera(coards, camera);
var collisionResults = ray.intersectObjects( collidableMeshList );
if ( collisionResults.length > 0 ) {
console.log('Target Hit!', collisionResults)
}
The console log is only triggered when the collidableMeshList actually touches the target mesh rather than when it is aiming at it.
How do I extend the ray to pass through the target (which I thought it was already doing) so that if anything hits the ray then it triggers my console log.
Edit
I've added a URL to the game in progress. There are other wider issues with the game, my current focus is just the target ray casting.
Game Link
Many thanks to everyone who helped me along the way on this one but I finally solved it, only not through the means that I had expected to. Upon reading into the setFromCamera() function it looks like it is mainly used around mouse coards which in my instance was not what I wanted. I wanted a target on the page and the ray to pass through this. For some reason my Ray was shooting vertically up from the centre of the whole scene.
In the end I tried a different approach and set the Rays position and direction directly rather rely on setting it from the cameras position.
var vector = new THREE.Vector3(crossHair.position.x, crossHair.position.y, crossHair.position.z);
var targetRay = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
var enemyHit = targetRay.intersectObject( enemy );
if ( enemyHit.length > 0 ) {
console.log('Target Hit!', targetRay)
}
I'll leave this up for a while before accepting this answer in case anyone has a better approach than this or has any corrections over what I have said in regards to setFromCamera().

Is there a way I can create a Path or Curve to use for TubeGeomety(path,...) from an existing geometry's points/vertices array?

I'm very new to both three.js & to js in general.
1st I select a polyHedron geometry with a dat.gui checkbox
which renders say a tetrahedron. these selections work.
I also have a dat.gui checkbox to either phongfill or wireframe render.
I initially wanted just a wireframe type mesh but not with all of the internal triangles. I found the edgesgeometry() function which draws pretty much what I want(hard edges only). there is however a known issue with linewidth not working in windows anymore. all lines drawn as strokeweight/width 1.
I'd like to use tubeGeometry() to draw tubes of whatever radius as opposed to 1weight lines. I know I'll have to draw something such as a sphere at/over the connection vertices for it to not look ridiculous.
geo = new THREE.TetrahedronBufferGeometry(controls0.Radius,controls0.Detail);
...
egeo = new THREE.EdgesGeometry( geo );
lmat = new THREE.LineBasicMaterial({ color: 0x0099ff, linewidth: 4 });
ph = new THREE.LineSegments( egeo, lmat );
scene.add(ph);
....
playing around in the console I found some geometry/bufferGeomery arrays that are likely the vertices/indices of my selected X-hedron as their sizes change with type(tetra/icosa etc) selection & detail increase/decrease:
//p = dome.geometry.attributes.uv.array;
p = egeo.attributes.position.array
//p = geo.attributes.uv.array
...
var path = new THREE.Curve();
path.getPoint = function (t) {
// trace the arc as t ranges from 0 to 1
var segment = (0 - Math.PI*2) *t;
return new THREE.Vector3( Math.cos(segment), Math.sin(segment), 0);
};
var geomet = new THREE.TubeBufferGeometry( path, 10, 0.2, 12, false );
var mesh = new THREE.Mesh( geomet, mat );
scene.add( mesh );
from above the tubeGeometry() draws fine separately as well but with the "path" made by that curve example. How can I use the vertices from my tetrahedron for example to create that "path" to pass to tubegeometry() ?
maybe a function that creates "segment vectors" from the vertices ?
I think it needs other properties of curve/path as well ?
I'm quite stuck at this point.
ANY Help, suggestions or examples would be greatly appreciated !
thanks.
You can try to create a TubeGeometry for each edge. Generate a LineCurve3 as the input path. Use the vertices of the edge as the start and end vector for the line.
Consider to use something like "triangulated lines" as an alternative in order to visualize the wireframe of a mesh with a linewidth greater than 1. With the next release of three.js(R91) there are new line primitives for this. Demo:
https://rawgit.com/mrdoob/three.js/dev/examples/webgl_lines_fat.html
This approach is much more performant than drawing a bunch of meshes with a TubeGeometry.

THREE.raycaster : Possible to use it for enemy Ai?

I've just started learning THREE and have been messing about with the three.js example of controllable MD2 characters to try and fashion it into a 3rd person shooter kind of game. I've been trying to write a simple algorithm for the enemy characters and I'm pretty sure that ray-casting would be ideal.The whole idea is that the enemies should stop rotating once they're facing the player. But Here's the problem that's giving me sleepless nights! :
Let's say, the enemy object is the origin for the ray caster ray. No matter what direction I set for the direction of that ray ( even, for example (1,0,0) - the positive x-axis), the ray's direction is always pointing towards the center of the scene!!!
Please help! haven't been able to find any Example online for this kind of use for the ray caster (apart from collision detection which I really don't need at the moment).
If all you want is for enemies to stop rotating when they are looking at the player, I would consider just checking the direction between them, as it's a lot faster than casting a ray to see if it intersects:
// Assuming `enemy` is a THREE.Mesh
var targetDir = enemy.position.clone().sub(player.position).normalize();
var currentDir = (new THREE.Vector3()).applyMatrix4(enemy.matrixWorld).sub(enemy.position).normalize();
var amountToRotate = currentDir.sub(targetDir);
var offset = amountToRotate.length();
Then rotate each axis no more than the value for that axis in amountToRotate if offset is greater than some threshold.
That said, here is how you use a Raycaster, given the variables above:
var raycaster = new THREE.Raycaster(enemy.position, targetDir);
var intersections = raycaster.intersectObject(player);
Note that if you are running any of the above code in an animation loop, it will create a lot of garbage collection churn because you are constantly creating a bunch of new objects and then immediately throwing them away. A better pattern, which is used a lot in the library itself, is to initialize objects once, copy values to them if you need to, and then use those copies for computation. For example, you could create a function to do your raycasting for you like this:
var isEnemyLookingAtPlayer = (function() {
var raycaster = new THREE.Raycaster();
var pos = new THREE.Vector3();
return function(enemy) {
raycaster.ray.origin.copy(enemy.position);
raycaster.ray.direction.copy(pos.copy(enemy.position).sub(player.position).normalize());
return !!raycaster.intersectObject(player).length;
};
})();

Resources