How to perform child displacement on 3D object, Three.js? - three.js

Here I have a simple example of an object being loaded using MTLLoader() and OBJLoader() in Three.js
var loader = new THREE.MTLLoader();
loader.load(<?php echo "'".$mPart."'"; ?>, function (materials) {
materials.preload();
var objLoaderOfficeChair = new THREE.OBJLoader();
objLoaderOfficeChair.setMaterials(materials);
objLoaderOfficeChair.load(<?php echo "'".$oPart."'"; ?>, function (object) {
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ){
x=document.getElementsByClassName("popup");
for(var i = 0; i < x.length; i++){
x[i].innerHTML += "<li class='mi_child'>"+child.name+"</li>";
var timer = performance.now();
geometry = child.geometry;
geometry.dynamic = true;
material = child.material;
mesh = new THREE.Mesh(geometry, material);
model = object.children[i].geometry;
model2 = object;
scene.add(mesh);
}
Now later in the anim() code, I call another action which will move the children the object loaded. Now I would like these items to be moved in all different directions.
var center = new THREE.Vector3(0, 0, 0);
var distanceToMove = 0.1;
if(params.explodeEnabled) {
for (var i = 0; i < model2.children.length; i++) {
model2.children[i].position.x = explodeValue + outlinePass.Explode * i;
model2.children[i].position.y = explodeValue + outlinePass.Explode * i - 0.02;
model2.children[i].position.z = explodeValue + outlinePass.Explode * i + 0.06;
}
}
But in this example all the items move together in the same direction as seen here.
More Info

You need to move each part along a different path from the center of the object. Save the old position of each part, and calculate its direction away from the center.
var explosionCenter = new THREE.Vector3(0, 0, 0);
for (var i = 0; i < model2.children.length; i++) {
var child = model2.children[i];
if (!child.geometry.boundingSphere) child.computeBoundingSphere();
child.oldPosition = child.oldPosition || geometry.boundingSphere.center.clone();
var direction = child.oldPosition.clone().sub(explosionCenter).normalize();
child.position.x = child.oldPosition.x + explodeValue + outlinePass.Explode * direction.x;
child.position.y = child.oldPosition.y + explodeValue + outlinePass.Explode * direction.y;
child.position.z = child.oldPosition.z + explodeValue + outlinePass.Explode * direction.z;
}
This way, each component will move away from the center depending on its initial position. You should use custom direction vectors for some of the big components to make sure they do not cover the small ones:
if (i == <index of big component>) {
direction = new THREE.Vector3(1, 1, 0); // Custom direction for a component
}

Related

Three.js + Tween.js tweening vertices of multiple objects

I am using three.js to create multiple BoxGeometries and tween.js to animate all vertices of the geometry.
At first all cubes should randomly distort untill i call stopTweens(). Then all vertices should go back to their default posiiton.
My problem is, that only the last cube gets reseted visually. The others are also tweened, but it seems like verticesNeedUpdate is not set to true and so nothing is happening on the screen.
Here is my Code:
var scene_03_TweenArr = [];
Function to create Cubes:
function createCubes(){
for (i = 0; i < 10; i++) {
var CubeGeometry = new THREE.BoxGeometry( 4, 4, 4, 5, 5, 5 );
var CubeMaterial = new THREE.MeshPhongMaterial( {color: 0xffffff} );
var cube = new THREE.Mesh( CubeGeometry, CubeMaterial );
scene_03.add( cube );
cube.position.x = -40+5*i;
tt_animate_vertices(cube);
}
}
Function to animate Cubes randomly:
function tt_animate_vertices(object) {
for( j = 0; j < object.geometry.vertices.length; j++ ){
var previousX = object.geometry.vertices[j].x;
var previousY = object.geometry.vertices[j].y;
var previousZ = object.geometry.vertices[j].z;
var tween = new TWEEN.Tween(object.geometry.vertices[j]);
tween.to({ x: (0.25*Math.random()+1)*previousX, y: (0.25*Math.random()+1)*previousY, z: (0.25*Math.random()+1)*previousZ }, 5000 + Math.random()*5000);
var tween2 = new TWEEN.Tween(object.geometry.vertices[j]);
tween2.to({ x: previousX, y: previousY, z: previousZ }, 5000 + Math.random()*5000);
tween.onUpdate(function(){
object.geometry.verticesNeedUpdate = true;
});
tween2.onUpdate(function(){
object.geometry.verticesNeedUpdate = true;
});
tween.chain(tween2);
tween2.chain(tween);
tween.start();
var verticeTween = [object, tween, tween2, j, previousX, previousY, previousZ ];
scene_03_TweenArr.push(verticeTween);
}
}
Function to stop tweens and reset cubevertices to default:
function stopTweens(){
for (let k = 0; k < scene_03_TweenArr.length; k++) {
scene_03_TweenArr[k][1].stop();
scene_03_TweenArr[k][2].stop();
var object = scene_03_TweenArr[k][0];
var index = scene_03_TweenArr[k][3];
var prevX = scene_03_TweenArr[k][4];
var prevY = scene_03_TweenArr[k][5];
var prevZ = scene_03_TweenArr[k][6];
var tween = new TWEEN.Tween(object.geometry.vertices[index]);
tween.to({x: prevX, y: prevY, z: prevZ }, 300)
tween.onUpdate(function(){
object.geometry.verticesNeedUpdate = true;
});
tween.start();
object.geometry.verticesNeedUpdate = true;
}
}
Here I managed to make it run in JSFiddle with the same behaviour:
http://jsfiddle.net/gfhyvou3/25/
From my experience, it's better to use let keyword inside loops, when you work with arrays.
function stopTweens(){
for (let k = 0; k < scene_03_TweenArr.length; k++) {
scene_03_TweenArr[k][1].stop();
scene_03_TweenArr[k][2].stop();
let object = scene_03_TweenArr[k][0];
let index = scene_03_TweenArr[k][3];
let prevX = scene_03_TweenArr[k][4];
let prevY = scene_03_TweenArr[k][5];
let prevZ = scene_03_TweenArr[k][6];
var tween = new TWEEN.Tween(object.geometry.vertices[index]);
tween.to({x: prevX, y: prevY, z: prevZ }, 300)
tween.onUpdate(function(){
object.geometry.verticesNeedUpdate = true;
});
tween.start();
object.geometry.verticesNeedUpdate = true;
}
}

Animating Three.js vertices

I'm trying to animate individual vertices from a collada model.
The vertices ARE animating fine once but then they don't animate anymore.
1: Load my collada
// name obj
var orgVerts = false;
var desVerts = false;
var material = new THREE.MeshBasicMaterial({ color:0x1e5679, wireframe:true });
var loader = new THREE.ColladaLoader();
var nameModel = false;
loader.options.convertUpAxis = true;
loader.load( '3d/name.dae', function ( collada ) {
nameModel = collada.scene.children[0].children[0];
nameModel.material = material;
nameModel.geometry.dynamic = true;
nameModel.position.set(0,0,0);//x,z,y- if you think in blender dimensions ;)
nameModel.scale.x = .0035;
nameModel.scale.y = .0035;
nameModel.scale.z = .0035;
scene.add(nameModel);
orgVerts = nameModel.geometry.vertices; // make a backup of all verts
genVerts(); // create a new array of random verts
});
2: in my render function
var render = function () {
requestAnimationFrame(render);
// do stuff
if(nameModel){
if(window.globalCurrentSlide != 0){
for(var r=0; r < nameModel.geometry.vertices.length; r++){
var vert = desVerts[r]; // loop through all the destination verts
nameModel.geometry.vertices[r].x = nameModel.geometry.vertices[r].x - (nameModel.geometry.vertices[r].x - vert.x)/20;
nameModel.geometry.vertices[r].y = nameModel.geometry.vertices[r].y - (nameModel.geometry.vertices[r].y - vert.y)/20;
nameModel.geometry.vertices[r].z = nameModel.geometry.vertices[r].z - (nameModel.geometry.vertices[r].z - vert.z)/20;
}
}else{
for(var t=0; t < nameModel.geometry.vertices.length; t++){
var vert2 = orgVerts[t]; // loop through all the original verts
nameModel.geometry.vertices[t].x = nameModel.geometry.vertices[t].x - (nameModel.geometry.vertices[t].x - vert2.x)/20;
nameModel.geometry.vertices[t].y = nameModel.geometry.vertices[t].y - (nameModel.geometry.vertices[t].y - vert2.y)/20;
nameModel.geometry.vertices[t].z = nameModel.geometry.vertices[t].z - (nameModel.geometry.vertices[t].z - vert2.z)/20;
}
}
nameModel.geometry.verticesNeedUpdate = true;
nameModel.rotation.y += .005;
}
renderer.render( scene, camera );
}
window.globalCurrentSlide is set to 0 to start with and everything is fine. if I change window.globalCurrentSlide to 1, all the vertices animate correctly... BUT when I change window.globalCurrentSlide back to 0 the vertices don't animate back to their original positions. I've debugged heaps and can 100% say that BOTH desVerts and orgVerts don't change and they are correct. Any ideas? It's driving me nuts.
PS: I know the code could be condensed, I'm just playing ATM
The answer was that my orgVerts was just a reference NOT a clone. use:
var geometry = nameModel.geometry.clone();
orgVerts = geometry.vertices;

Texture mapping from one Face4 to two Face3 on Three.js

I am playing with Three.js. I am working on a project that uses an old version of Three.js. I'm trying to replace the old library with the new one (r71). Everything is going well except the texture mapping.
In the old project we use the Face4 which is deprecated and have been removed in the newest version of Three.js. So I have created two Face3 instead of just one Face4. Now the question is: how can I make one texture to cover both Face3 as they were like a single Face4?
Old pseudo-code using Face4
numberOfFaces = 10;
function createMyGeometry(){
var geometry = new THREE.Geometry();
...
for (var i = 0; i < numberOfFaces; i++) {
var face = new THREE.Face4(v1.index, v2.index, v3.index, v4.index, [v1.clone(), v2.clone(), v3.clone(), v4.clone()]);
face.centroid.add(v1).add(v2).add(v3).add(v4).divideScalar(4);
face.normal = face.centroid.clone().normalize();
face.materialIndex = matIdx;
geometry.faces.push(face);
geometry.faceVertexUvs[0].push([uv1.clone(), uv2.clone(), uv3.clone(), uv4.clone()]);
}
...
return geometry;
}
for (var i = 0; i < numberOfFaces; i++) {
var texture = THREE.ImageUtils.loadTexture("MyImage"+ i + '.jpg');
var material = new THREE.MeshBasicMaterial();
material.map = texture;
material.side = THREE.FrontSide;
materials.push(material);
}
materials = new THREE.MeshFaceMaterial(materials);
myGeometry = new THREE.Mesh(createMyGeometry(), materials);
scene.add(myGeometry);
Pseudo-code using Face3
numberOfFaces = 10;
function createMyGeometry(){
var geometry = new THREE.Geometry();
...
for (var i = 0; i < numberOfFaces; i++) {
var face1 = new THREE.Face3(v1.index, v2.index, v3.index, [v1.clone(), v2.clone(), v3.clone()]);
var face2 = new THREE.Face3(v1.index, v3.index, v4.index, [v1.clone(), v3.clone(), v4.clone()]);
face1.materialIndex = matIdx;
face2.materialIndex = matIdx;
geometry.faces.push(face1);
geometry.faces.push(face2);
geometry.faceVertexUvs[0].push([uv1.clone(), uv2.clone(), uv3.clone(), uv4.clone()]);
}
...
return geometry;
}
for (var i = 0; i < numberOfFaces; i++) {
var texture = THREE.ImageUtils.loadTexture("MyImage"+ i + '.jpg');
var material = new THREE.MeshBasicMaterial();
material.map = texture;
material.side = THREE.FrontSide;
materials.push(material);
}
materials = new THREE.MeshFaceMaterial(materials);
myGeometry = new THREE.Mesh(createMyGeometry(), materials);
scene.add(myGeometry);
You should just do the same thing with vertex uvs as with vertices.
For example:
geometry.faceVertexUvs[0].push([uv1.clone(), uv2.clone(), uv3.clone()]);
geometry.faceVertexUvs[0].push([uv1.clone(), uv3.clone(), uv4.clone()]);

Accessing single particles in THREE.js Particle System

I really tried every example, searched the web for hours but I can't seem to get it working!
So I simply tried to implement a little particle system simulating falling snow, just like this: http://www.aerotwist.com/tutorials/creating-particles-with-three-js/
But I only can access it in whole. Meaning I can rotate it as such but as soon as I try to iterate over it's vertices, the whole animation is getting the hiccups! I would really appreciate some help here!
-
Here are the key parts:
-> Setting up the particle system:
var partikelGeo = new THREE.Geometry();
var partikelMaterial = new THREE.ParticleBasicMaterial({
color:0xffffff,
size: 10,
map: THREE.ImageUtils.loadTexture('snowflake2.png'),
blending: THREE.AdditiveBlending,
transparent:true
});
var partikelAnzahl = 3500;
for (var p = 0; p < partikelAnzahl; p++) {
var pX = Math.random() * 1000 -500;
var pY = Math.random() * 1000 -500;
var pZ = Math.random() * 1000 -500;
var partikel = new THREE.Vertex(new THREE.Vector3(pX,pY,pZ));
partikel.velocity = new THREE.Vector3(0,-Math.random(),0);
partikelGeo.vertices.push(partikel);
}
var partikelSystem = new THREE.ParticleSystem(partikelGeo, partikelMaterial);
partikelSystem.sortParticles = true;
scene.add(partikelSystem);
-> Rendering & Animation on mouseclick
var frame = 0;
function animate(){
// request new frame
requestAnimationFrame(function(){
animate();
});
// render
render();
}
animate();
var check = 0;
onmousedown = function(){
if (check) {
check = 0;
}else{
check = 1;
}
}
function render() {
if (check) {
clickDo();
}
camera.lookAt(new THREE.Vector3(0,0,0));
renderer.render(scene,camera);
}
function clickDo() {
frame++;
partikelSystem.rotation.y += 0.01;
var pCount = partikelAnzahl;
while(pCount--) {
// get the particle
var particle =
partikelGeo.vertices[pCount];
// check if we need to reset
if(particle.position.y < -200) {
particle.position.y = 200;
particle.velocity.y = 0;
}
// update the velocity with
// a splat of randomniz
particle.velocity.y -=
Math.random() * .1;
// and the position
particle.position.addSelf(
particle.velocity);
}
// flag to the particle system
// that we've changed its vertices.
partikelSystem.
geometry.
__dirtyVertices = true;
}
Rah
Your code looks good to me. I would just suggest to try not sorting your particles as you use an additive blending:
partikelSystem.sortParticles = false;

Three.js SceneUtils has no traverseHierarchy!!! where is it?

I had not updated the three.js library in my system. Today, I updated the three.js file. But I encountered this error: "Uncaught TypeError: Object # has no method 'traverseHierarchy' ".
I had used it for detecting intersection:
Online code is here: https://dl.dropbox.com/u/44791710/test/simple2.html
function Intersect(event) {
event.preventDefault();
var mousex = (event.clientX / window.innerWidth) * 2 - 1;
var mousey = -(event.clientY / window.innerHeight) * 2 + 1;
var vector = new THREE.Vector3(mousex, mousey, 1);
var toIntersect = [];
THREE.SceneUtils.traverseHierarchy(scene, function (child) {
if (child instanceof THREE.Mesh) {
toIntersect.push(child);
}
});
var projector = new THREE.Projector();
projector.unprojectVector(vector, camera);
var ray = new THREE.Raycaster(camera.position,vector.sub(camera.position).normalize());
var intersects = ray.intersectObjects(toIntersect);
if (intersects.length > 0) {
for (var j = 0; j < intersects.length ; j++){
target = intersects[j].object;
console.log('Intersects at ' + mouse.x + '/' + mouse.y + ':');
for(var i = 0, m = intersects.length; i<m; i++){
console.log(intersects[i].object.id, intersects[i]);
}
}
}
}
what kind of changes I need to do?
Thanks.
You should replace scene.traverse(scene, function (child) { by scene.traverse(function (child) { (I could not add a comment, so I created an answer instead).
As the Migration guide states, in r51 -> r52:
Replaced SceneUtils.traverseHierarchy with object.traverse.
object meaning any THREE.Object3D instance, e.g. THREE.Mesh or THREE.Scene. So instead of THREE.SceneUtils.traverseHierarchy, try scene.traverse.

Resources