How to dynamically add mesh from Object3D to new Object in THREE.js - three.js

I am a complete newbie to 3d programming and have been working with three.js just over a week now. I have managed to load multiple collada and obj files, and managed to get perspectives, trackballs, everything working from examples. However, now I am stuck and need some help. For reference you can see the file i'm posting about at the following url:
http://shaman-labz.appspot.com/webgl_loader_obj_mtl.html
This page is basically straight from the examples, except that i am loading up an obj file, which has all these objects as meshes. what i am working on now is after loading the obj, i am going to iterate through all the geometries in the object and extract them so that i can drop them in the scene one a time when i need them, or have them float around like bubbles or something. I was thinking to maybe try to use the fresnel example, but this is where i'm hitting up against the boundaries of my understanding, and some terminology escapes me.
My question is, when this runs, the object that returns and is added to the scene after load has all these gems in it together.
So instead of the following lines:
var loader = new THREE.OBJMTLLoader();
loader.addEventListener( 'load', function ( event ) {
var object = event.content;
object.position.y = - 100;
scene.add( object );
});
loader.load( 'obj/gems/24.obj', 'obj/gems/24.mtl' );
So what i'm doing is when the object returns, i look at the internals in debug/break mode, and i see that it's an object3D and object.children is an array of 25 meshes...and each of those meshes would be one of my 'gems' that i want to work with individually.
So here's where i get lost...when i grab the 'mesh' do i need to strip out the underlying geometry and create a new mesh?
on this page, you can see what i tried to accomplish:
http://shaman-labz.appspot.com/webgl_loader_obj_mtl2.html
the only major difference is in this section of code:
var loader = new THREE.OBJMTLLoader();
loader.addEventListener( 'load', function ( event ) {
var object = event.content;
var pos=0;
for(var i=0; i< object.children.length; i++){
var m = object.children[i];
var gem = new THREE.Object3D();
gem.name=m.name;
gem.add(m);
gem.position.x = -10;
gem.position.y = -10;
gem.position.z = pos;
scene.add( gem );
pos = pos - 10;
}
});
loader.load( 'obj/gems/24.obj', 'obj/gems/24.mtl' );
notice that only 13 of the 25 gems show up from the collection, and also note how they are scattered, indicating that they are somehow still linked to some higher-order relationships that i am unable to set with position properly (it's like each mesh is somehow offset relative to it's original positioning in the original object loaded...thinking this has to do with world matrix?

Related

Three.js - repositioning vertices in a 'particle' mesh

I have a basic three.js game working and I'd like to add particles. I've been searching online, including multiple questions here, and the closest I've come to getting a 'particle system' working is using a THREE.BufferGeometry, a THREE.BufferAttribute and a THREE.Points mesh. I set it up like this:
const particleMaterial = new THREE.PointsMaterial( { size: 10, map: particleTexture, blending: THREE.AdditiveBlending, transparent: true } );
const particlesGeometry = new THREE.BufferGeometry;
const particlesCount = 300;
const posArray = new Float32Array(particlesCount * 3);
for (let i = 0; i < particlesCount; i++) {
posArray[i] = Math.random() * 10;
}
const particleBufferAttribute = new THREE.BufferAttribute(posArray, 3);
particlesGeometry.setAttribute( 'position', particleBufferAttribute );
const particlesMesh = new THREE.Points(particlesGeometry, particleMaterial);
particlesMesh.counter = 0;
scene.add(particlesMesh);
This part works and displays the particles fine, at their initial positions, but of course I'd like to move them.
I have tried all manner of things, in my 'animate' function, but I am not happening upon the right combination. I'd like to move particles, ideally one vertex per frame.
The current thing I'm doing in the animate function - which does not work! - is this:
particleBufferAttribute.setXYZ( particlesMesh.counter, objects[0].position.x, objects[0].position.y, objects[0].position.z );
particlesGeometry.setAttribute( 'position', particleBufferAttribute );
//posArray[particlesMesh.counter] = objects[0].position;
particlesMesh.counter ++;
if (particlesMesh.counter > particlesCount) {
particlesMesh.counter = 0;
}
If anyone has any pointers about how to move Points mesh vertices, that would be great.
Alternatively, if this is not at all the right approach, please let me know.
I did find Stemkoski's ShaderParticleEngine, but I could not find any information about how to make it work (the docs are very minimal and do not seem to include examples).
You don't need to re-set the attribute, but you do need to tell the renderer that the attribute has changed.
particleBufferAttribute.setXYZ( particlesMesh.counter, objects[0].position.x, objects[0].position.y, objects[0].position.z );
particleBufferAttribute.needsUpdate = true; // This is the kicker!
By setting needsUpdate to true, the renderer knows to re-upload that attribute to the GPU.
This might not be concern for you, but just know that moving particles in this way is expensive, because you re-upload the position attribute every single frame, which includes all the position data for every particle you aren't moving.

Problem in Mesh Rotation around another Mesh of imported FBX model in Three.js

For the learning purposes, I downloaded a layalty-free FBX model from a website, which happens to be a helicopter. I want to emulate the rotation of the helicopter blades programmatically in Three.js. I imported the moded successfully by means of FBXLoader, without any problem. I checked its meshes in Blender, and it has more than fifty meshes. I pinpointed the blades' meshes and wrote this in the load() function:
pivotPoint = new THREE.Object3D();
const loader = new THREE.FBXLoader();
group = new THREE.Object3D();
loader.load(
'Apache.fbx',
object => {
scene.add(object);
const twentyFive = scene.getObjectByName('Mesh25'); //This is the shaft which the blades should rotate around
console.log(twentyFive); //x: 685.594482421875, y: 136.4067840576172, z: -501.9534606933594
twentyFive.add(pivotPoint);
const twentyEight = scene.getObjectByName('Mesh28');//These four are the blades
const twentyNine = scene.getObjectByName('Mesh29');
const twentySeven = scene.getObjectByName('Mesh27');
const twentySix = scene.getObjectByName('Mesh26');
group.add(twentyEight);
group.add(twentyNine);
group.add(twentySeven);
group.add(twentySix);
pivotPoint.add(group);
scene.add(pivotPoint);
scene.add(twentyFive);
},
progress => ...,
error => ...
);
and the following in the loop render function:
pivotPoint.rotation.y += 0.01;
However, either the four blades disappear once I add the nesting Object3Ds or upon changing the code into the above version with numerous mutations, the four blades would strangely rotate around some other point in sky, apart from the fuselage, while the awe-stricken pilot watches the catastrophe and amazed by the aforementioned code, as if the helicopter is about to crash any second!
I tried many changes to the code. Basically I had once used the Object3D parenting for some light sources on another scene, but have no idea what's the issue now. Besides, the rotation of the blades around Mesh25 (my wished pivot) is around a big circle with no contacts with the fuselage, although all four are beautifully revolve around their center of mass.
I really appreciate any help, as I really need to learn to wrestle with similar imported models.
Use attach instead of add in the appropriate places.
const twentyFive = scene.getObjectByName('Mesh25');
// add the pivot and group first so they are in the scene
pivotPoint.add(group);
twentyFive.add(pivotPoint);
const twentyEight = scene.getObjectByName('Mesh28');
const twentyNine = scene.getObjectByName('Mesh29');
const twentySeven = scene.getObjectByName('Mesh27');
const twentySix = scene.getObjectByName('Mesh26');
// use attach to move something in the scene hierarchy without
// changing its position
group.attach(twentyEight);
group.attach(twentyNine);
group.attach(twentySeven);
group.attach(twentySix);
This assumes the model is created correctly in the first place and that the the shaft's position Mesh25 is in the center of the shaft.
Note: If the shaft's origin is in the correct position and the blades are already children of the shaft you can just rotate the shaft.

calling object3D children for unique styles / animations

I'm wondering how I would go about calling the individual children of cube (mesh0, mesh1) so I'm able to set different styles / animations to them both.
AFRAME.registerComponent('multi_box', {
schema: {},
update: function() {
for (var i = 0; i < 2; i++) {
var material = new THREE.MeshBasicMaterial({color: "blue"});
var geometry = new THREE.BoxGeometry(1, 1, 1);
var cube = new THREE.Mesh(geometry, material);
cube.position.x = i == 0 ? -1 : 1;
cube.position.y = 0.5;
cube.position.z = -5;
this.el.setObject3D("mesh"+i, cube); //unique name for each object
}
console.log(this.el.object3DMap)
}
});
Codepen link: https://codepen.io/ubermario/pen/wrwjVG
I can console.log them both and see that they are unique objects to each other but I'm having trouble calling them:
var meshtest = this.el.getObject3D('mesh0')
console.log(meshtest.position)
I'v tried this method but with no luck: aframe get object3d children
Any help is appreciated :)
Instancing
In your for cycle, you create
a new geometry instance
a new material instance
a new mesh that connect the previous two
Each new keyword creates a unique instance that is independent of the others. In your case mesh0 and mesh1 are fully independent of each other. If you change any property of an instance, like for example material color or position, the other(s) will not be affected by that. In fact you do that by assigning a different x position to each cube.
Storage
Your component holds a map of 3D objects. Each is identified by a unique name. You generate this name by concatenating the prefix mesh with the iteration number (value of i).
You can later access the cubes just the same way you created them. Either by name
this.el.getObject3D('mesh0')
this.el.getObject3D('mesh1')
//etc.
or by index
this.el.object3D.children[0]
this.el.object3D.children[1]
and you can manipulate them further. For example you can put his on Ln19 in your Codepen:
this.el.getObject3D('mesh0').position.y = 2;
this.el.object3D.children[1].position.z = -3;
Just for completeness: If you would omit the +i at the end, you would overwrite the same key again and again, so mesh would reference just the last cube and others would get lost.
Changing properties
Three.js has a nice API, but in javascript you always need to think what happens behind the scenes. You will see that while learning new stuff. For example:
this.el.object3D.children[1].position.z = -3;
this.el.object3D.children[1].material.color.set('#ffff00');
this.el.object3D.children[1].material.color = [1, 1, 0];
As you can see the position can be changed directly, but color needs a setter sometimes. Things get more complicated with vectors where you need to watch which methods change the current instance and which produce a new one. You could easily forget to clone it. Say you had:
var pos = new THREE.Vector3(-1, 0.5, -5)
for (var i = 0; i < 2; i++) {
var material = new THREE.MeshBasicMaterial({color: "blue"});
var geometry = new THREE.BoxGeometry(1, 1, 1);
var cube = new THREE.Mesh(geometry, material);
//wrong, since it will assign the object reference,
//i.e. always the same object.
//Thus, the cubes will be at one position in the end
cube.position = pos;
//right
cube.pos = pos.clone();
pos.x += 1;
this.el.setObject3D("mesh"+i, cube); //unique name for each object
}
The difference is a bit more moderate topic on reference and value types. You will find many tutorials for that, for example this small gist I just found.
I hope this was helpful and wish you good progress in learning!

Rigid body (shape) in bullet/ammo.js from a mesh in 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.

THREE.js Reusing geometry does not seem to work efficiently

I am loading several models into scene using the same geometry like so (pseudo code):
var geoCache = [];
function parseJSONGeometry(json_geo){
// this code is the three.js model parser from the jsonloader
return geometry;
}
function loadCachedGeo(data){
if( !geoCache[data.id] ){
geoCache[json.id] = parseJSONGeometry(data);
}
return geoCache[json.id];
}
function loadObjects(json){
var mats = [];
combined = new THREE.Geometry();
for(i=0<i<json.geometries.length;i++){
data = json.geometries[i];
geo = loadCachedGeo(data.id);
mats.push(new THREE.MeshBasicMaterial(map:THREE.imageUtils.loadTexture(data.src)));
mesh = new THREE.Mesh(geo);
mesh.position.set(data.x,data.y,data.z);
combined = THREE.GeometryUtils.mergeGeometry(combined,mesh);
}
mesh = new THREE.Mesh(combined,new THREE.MeshFaceMaterial(mats));
scene.add(mesh);
}
I also cache the textures, however I omitted that for the sake of simplicity.
When I call:
renderer.info.render.faces
renderer.info.memory.textures
renderer.info.memory.programs
renderer.info.memory.geometries;
renderer.info.render.calls
I notice when one object is on the screen the poly count is say 1000, textures: 1, calls: 1, shaders: 1 and geometries: 1. When two objects are on the screen 2000 faces are reported, 1 texture, 1 shader, 2 calls, and 2 geometries.
I thought that reusing geometry in this fashion only loads the geometry once into the gpu. Am I missing something, can someone PLEASE explain this behavior?
Three.js r59
You need to inspect
renderer.info.memory.geometries
There is also
renderer.info.memory.textures
renderer.info.memory.programs
three.js r.59

Resources