Three JS : Attaching an object to another - three.js

I'm trying to attach a cube to another cube at a specific point. I'm able to get collision and .add() is working fine. However, I want to add child cube exactly at point where collision occurs to the parent cube.
for (var vertexIndex = 0; vertexIndex < MovingCube.geometry.vertices.length; vertexIndex++) {
var localVertex = MovingCube.geometry.vertices[vertexIndex].clone();
var globalVertex = localVertex.applyMatrix4( MovingCube.matrix );
var directionVector = globalVertex.sub( MovingCube.position );
var ray = new THREE.Raycaster( originPoint, directionVector.clone().normalize() );
var collisionResults = ray.intersectObjects( collidableMeshList );
if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() )
console.log(" Hit and collision detected " + SELECTED);
MovingCubeBig.add(SELECTED);
// Is there way to get collision point and attach object at that point?
}

Intersections results should include a point attribute.
https://github.com/mrdoob/three.js/blob/master/src/core/Raycaster.js#L255
Maybe you can use this to attach the child at the right position ?
Also, this example may help http://threejs.org/examples/#webgl_geometry_terrain_raycast

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.

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!

THREEJS: add hole to rendered Shape

I'm trying to inject hole to the shape that is already added to the scene, but some thing going wrong...
so in details: the shape
var well,
vertices = [],
wellShape,
wellMaterial = new THREE.MeshLambertMaterial({color: this.params.wellsColor});
vertices.push(new THREE.Vector2(0,3000));
vertices.push(new THREE.Vector2(4000,3000));
vertices.push(new THREE.Vector2(4000,0));
vertices.push(new THREE.Vector2(0,0));
wellShape = new THREE.Shape(vertices);
well = new THREE.Mesh( new THREE.ShapeGeometry(wellShape), wellMaterial);
scene.add(well);
well.geometry.dynamic = true;
var hole = [
new THREE.Vector3(300,300,0),
new THREE.Vector3(1000,300,0),
new THREE.Vector3(1000,1000,0),
new THREE.Vector3(300,1000,0)
];
well.geometry.vertices = well.geometry.vertices.concat(hole);
well.geometry.faces = [];
var triangles = THREE.Shape.Utils.triangulateShape ( well.geometry.vertices, hole );
for( var i = 0; i < triangles.length; i++ ){
well.geometry.faces.push( new THREE.Face3( triangles[i][0], triangles[i][1], triangles[i][2] ));
well.geometry.faceVertexUvs[ 0 ][i] = THREE.ExtrudeGeometry.WorldUVGenerator.generateTopUV(well.geometry, triangles[i][0], triangles[i][1], triangles[i][2]);
}
but as result i got something strange: in console output "Infinite Loop! Holes left:4, Probably Hole outside Shape!" and on desktop i got https://yadi.sk/i/WHRzH7c2jnaRm
could someone tell me what is wrong in my code?
after few days play around I found what is wrong:
1. THREE.Shape.Utils.triangulateShape expect vertices and holes to be parts of same shape.
2. hole should be not the array of points but array of path.
3. vertices should be concated after triangulation.
so correct result is:
....
var holePoints = [
new THREE.Vector3(300,300,0),
new THREE.Vector3(1000,300,0),
new THREE.Vector3(1000,1000,0),
new THREE.Vector3(300,1000,0)
],
hole = new THREE.Path();
hole.fromPoints(holePoints);
var shape = new THREE.Shape(well.geometry.vertices);
shape.holes = [hole];
var points = shape.extractPoints();
well.geometry.faces = [];
var triangles = THREE.Shape.Utils.triangulateShape ( points.vertices, points.holes );
....
Hope someone will find this info helpful.

ray.interstectObjects not intersecting correctly after geometry is dynamically modified

My ray.intersectObjects works really well with the objects in my scene until I dynamically modify the geometry of the object. Although the renderer is showing the objects as being modified (vertices moved and faces changed), when an intersect is tried on the modified object, it produces strange results. I need the intersect to work even on the modified geometry!
To help debug and track how the intersect is working in my scene, I've added a function: makeMiniSphere(). This makes a new sphere object in the scene at the point where the intersection occurs. Using this, you can see that after the cube is modified, sometimes the intersect hits the cube and sometimes it goes right through (mostly the faces that have been modified). It isn't a random problem, but the more you click around the modified cube, the more you can see a pattern develop. It is almost as if the renderer for the visuals of the scene know which direction the cube was modified, but the ray.intersectObjects thinks that it has been modified in a different direction!
Here is a link to the test website: http://www.littledrop.net/html/cadiverse/HelloWorld.html
Directions to show problem:
Left click on cube to show intersect points. Mini spheres will be created wherever Three.js sees an intersect. The color of the selected object will change to yellow if not already selected.
Click on any face of the cube. This will A. Turn it yellow if it isn't already yellow. B. It will select the face of the cube, although the selected face won't look any different from the rest of the cube.
Press the "Right" arrow key to move the selected face to the right. This will dynamically change the geometry of the cube.
Now try to click on the cube--especially in the area that it has been modified. Again the Mini spheres will show where the software thinks the intersects are occurring.
Here is the intersect code:
function onDocumentMouseDown (event)
{
// the following line would stop any other event handler from firing
// (such as the mouse's TrackballControls)
//event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
document.getElementById('message1').innerHTML = window.innerHeight;
var isinworkingarea = window.innerHeight-menubarh;
if (event.clientY<=isinworkingarea)
{
var vector = new THREE.Vector3( mouse.x, mouse.y, 1 );
projector.unprojectVector( vector, camera );
//var ray = new THREE.ReusableRay();
var ray = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
// create an array containing all objects in the scene with which the ray intersects
// use this to select anything in the scene:
var intersects = ray.intersectObjects( scene.children );
if ( intersects.length > 0 )
{
if (cadjectNow)
cadjects[cadjectNow].material.color.setHex(cadjects[cadjectNow].oldColor);
if (intersects[0].object.cadNum)
cadjectNow = intersects[0].object.cadNum;
SELECTEDface=intersects[0].face;
if (cadjectNow)
cadjects[cadjectNow].material.color.setHex( selectColor );
document.getElementById('message1').innerHTML = cadjects[cadjectNum].cadNum;
///// Information about selected /////
var facestring = intersects[0].face.a + " " + intersects[0].face.b + " " + intersects[0].face.c;
if(intersects[0].face instanceof THREE.Face4)
{
facestring=facestring + " " + intersects[0].face.d;
}
copyGeometry=cadjects[cadjectNow].geometry;
//makeCopy(copyGeometry,cadjects[cadjectNow].position.x,cadjects[cadjectNow].position.y,cadjects[cadjectNow].position.z);
makeMiniSphere(intersects[0].point.x, intersects[0].point.y, intersects[0].point.z);
document.getElementById('message1').innerHTML = facestring;
//document.getElementById('message2').innerHTML = cadjects[cadjectNow].geometry.vertices[SELECTEDface.a].x + " " + cadjects[cadjectNow].geometry.vertices[intersects[0].face.a].y + " " + cadjects[cadjectNow].geometry.vertices[intersects[0].face.a].z;
document.getElementById('message2').innerHTML = intersects[0].point.x + " " + intersects[0].point.y + " " + intersects[0].point.z;
}
}
}
Here is the modify code:
if ( keyboard.pressed("right"))
{
document.getElementById('message1').innerHTML = mouseMode;
cadjects[cadjectNow].geometry.vertices[SELECTEDface.a].x+=10;
cadjects[cadjectNow].geometry.vertices[SELECTEDface.b].x+=10;
cadjects[cadjectNow].geometry.vertices[SELECTEDface.c].x+=10;
if(SELECTEDface instanceof THREE.Face4)
{
cadjects[cadjectNow].geometry.vertices[SELECTEDface.d].x+=10;
}
cadjects[cadjectNow].geometry.verticesNeedUpdate = true;
cadjects[cadjectNow].geometry.elementsNeedUpdate = true;
cadjects[cadjectNow].geometry.normalsNeedUpdate = true;
}
Thank you to everyone who has posted past questions and given the answers. By perusing past questions, I've been able to get this far---so you guys have already been a great help. Thanks in advance for help on this one. (As this is my first question to post here, any suggestions on how to better present a question are also more than welcome.)
Update (3/21/13)--I've migrated to r57 as suggested and the updated code is shown above. I've also debugged it so that it is working at least as well as it was before. So now the geometry is still visually being changed dynamically, but the intersect is not detecting the change properly. Thanks #WestLangley for the encouraging posts so far.
Now working correctly. Here is how I did it (thanks to Westlangley for guidance).
Upgrade Three.js to latest revision (r57 from r49).
Migrate my present code to work with r57.
Remove all former code attempting to update the object.
Added the following code to "modify object" section:
cadjects[cadjectNow].geometry.verticesNeedUpdate = true;
cadjects[cadjectNow].geometry.normalsNeedUpdate = true;
cadjects[cadjectNow].geometry.computeFaceNormals();
cadjects[cadjectNow].geometry.computeVertexNormals();
cadjects[cadjectNow].geometry.computeBoundingSphere();
I was having a similar issue with a modified BufferGeometry.
I got raycasting working by calling geometry.computeBoundingSphere() and geometry.computeBoundingBox() after modifying the array with vertex positions.
Setting verticesNeedUpdate was not needed.
Running r112.

How to dynamically add mesh from Object3D to new Object in 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?

Resources