Clone an Object3D model from a Collada load call - three.js

I'm trying to clone an ThreeJS Object3D model. I've found various code here and on GitHub and nothing is working for me. The code below comes in part from How to clone an object3d in Three.js?
var loader = new THREE.ColladaLoader();
loader.load('../Model.dae', function (result) {
var loadedMesh = // No sure where this comes from
// Create X of these
for ( var i = 0; i < 10; i ++ ) {
var mesh = new THREE.Mesh( loadedMesh.geometry, loadedMesh.material );
mesh.position.set( i * 100, 0, 0 );
scene.add( mesh );
}
}
Can you help be fill in the blanks?

This ended up working:
var loader = new THREE.ColladaLoader();
loader.load('../Turn.dae', function colladaReady(result) {
var piece = result.scene.children[0];
for (var i = 0; i < 10; i++) {
var newPiece = new THREE.Object3D();
for (var j = 0; j < piece.children.length; j++) {
newPiece.add(new THREE.Mesh(piece.children[j].geometry, piece.children[j].material));
}
newPiece.position.set(i * 100, 0, 0);
newPiece.scale.set(30, 30, 30);
scene.add(newPiece);
}
renderer.render(scene, camera);
});
So instead of getting a single mesh, I got a group of meshes. I'm new to this, so I don't know why this is different from what every other answer I've seen. The dae file was exported directly from Sketchup. I'd be interested in knowing if there is a easier/better was to do this.

Related

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;

Collision Detection - Character not falling of platform correct

I am making a platform game with movement in x and y axis. Im using the collision detction from https://stemkoski.github.io/Three.js/Collision-Detection.html, works great and all. Just that Im having a small problem that I do not understand how to solve. Did the reasearch and found nothing that helped me with the problem (probably because I dont get how it works).
To the problem: When Im moving my character left and right I want the character fall down where the ledge is, works on the left side. But on the right side the character starts to fall before where the ledge is picture.
This is how my code looks like now:
function animate() {
requestAnimationFrame( animate );
var originPoint = user.position.clone();
for (var i = 0; i < user.geometry.vertices.length; i++) {
var localVertex = user.geometry.vertices[i].clone();
var globalVertex = localVertex.applyMatrix4(user.matrixWorld);
var directionVector = globalVertex.sub(user.position);
var ray = new THREE.Raycaster(originPoint, directionVector.clone().normalize());
var collisionResult = ray.intersectObjects(objectArray);
if (collisionResult.length > 0 && collisionResult[0].distance < directionVector.length()){
inAir = false;
}
else inAir = true;
};
var userGeometry = new THREE.PlaneGeometry( 50, 100, 10 );
var userMaterial = new THREE.MeshBasicMaterial( {
color: "red"
} );
var user = new THREE.Mesh( userGeometry, userMaterial );
scene.add(user);

How to use geomertryUtils merge in three js?

I'm using old version of three js because some library compatible problem. There's no Geometry.merge function in it, all i could use is GeometryUtils.merge.
What i am trying to do is to have a mesh, and then merge other mesh into it(i mean merge their geometries) later in the animation loop.
The result is funny, the console.log indicates that the vertices have been added into the group geometry. I checked their positions, they seem right.
However, they seemly don't show up in the scene ( i can't see them!)
Notice that i called add() once before the animation function, and it works!
It just does not work in the animation loop. So weird!!!
var group = new THREE.Geometry();
var mmm = new THREE.Mesh(group, new THREE.MeshNormalMaterial());
scene.add(mmm);
var counter = 0;
function add(){
for(var i = counter + 1; i < counter + 2 ; i++){
var geo = new THREE.BoxGeometry(0.1, 0.1, 0.1);
var m = new THREE.Mesh(geo, material);
m.position.set(i, i, 0);
console.log(i)
group.verticesNeedUpdate = true;
THREE.GeometryUtils.merge(group, m);
}
counter ++;
console.log(group.vertices.length);
}
add();
var frameCount = 0;
function animate(){
requestAnimationFrame(animate);
if(frameCount !== 0 && frameCount % 60 === 0){
add();
}
frameCount++;
renderer.render( scene, camera );
}
animate();

Flip (mirror) any object with Three.js

Update 2019
Since r89 three.js will also adjust the faces and normals. To flip/mirror an object simply use:
object3D.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1));
No other adjustments as shown in the original solution are required. Link to the ticket: Support reflection matrices. #12787
Original question
I'm trying to create an utility that would flip any object in Three.js scene. The flipping itself is the easy bit:
object3D.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1));
What is proving difficult is fixing faces and normals after the flip. The result looks quite messed up. Images of the source and flipped object: https://dl.dropboxusercontent.com/u/20925853/Flipped.png
I have seen a number of threads where this and similar issues were discussed but did not find anything usable. Does anybody know what I'm missing there? - Thanks!
Sample code on Js fiddle:
https://jsfiddle.net/7dwh084w/
var renderer;
var scene;
var camera;
function render() {
renderer.render(scene, camera);
};
function load(callback) {
new THREE.ColladaLoader().load("https://dl.dropboxusercontent.com/u/20925853/Kitchen.dae", function (result) {
var mesh = result.scene.children[0].children[0].clone();
if (callback) callback(mesh);
});
}
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000);
camera.position.z = 1000;
var light = new THREE.DirectionalLight(0xffffff);
light.position.set(2, 1, 1).normalize();
scene.add(light);
var ambient = new THREE.AmbientLight(0x777777);
scene.add(ambient);
var controls = new THREE.OrbitControls(camera);
controls.damping = 0.2;
controls.addEventListener('change', render);
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
load(function (mesh) {
flipMesh(mesh);
scene.add(mesh);
render();
});
render();
}
function flipMesh(object3D) {
object3D.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1));
reverseWindingOrder(object3D);
}
function reverseWindingOrder(object3D) {
// TODO: Something is missing, the objects are flipped alright but the light reflection on them is somehow broken
if (object3D.type === "Mesh") {
var geometry = object3D.geometry;
for (var i = 0, l = geometry.faces.length; i < l; i++) {
var face = geometry.faces[i];
var temp = face.a;
face.a = face.c;
face.c = temp;
}
var faceVertexUvs = geometry.faceVertexUvs[0];
for (i = 0, l = faceVertexUvs.length; i < l; i++) {
var vector2 = faceVertexUvs[i][0];
faceVertexUvs[i][0] = faceVertexUvs[i][2];
faceVertexUvs[i][2] = vector2;
}
geometry.computeFaceNormals();
geometry.computeVertexNormals();
}
if (object3D.children) {
for (var j = 0, jl = object3D.children.length; j < jl; j++) {
reverseWindingOrder(object3D.children[j]);
}
}
}
init();
I use this code in my project to flip objects:
const scale = new THREE.Vector3(1, 1, 1);
if (flipX) {
scale.x *= -1;
}
if (flipY) {
scale.z *= -1;
}
object.scale.multiply(scale);
Hope this helps.

Three.js - Updating texture on plane

I'm trying to make a terrain editor. So I have a plane with multiple materials on it, which I initialize like so:
var materials = [];
var grasstexture = THREE.ImageUtils.loadTexture('Img/grass.png', {}, function() { });
var rocktexture = THREE.ImageUtils.loadTexture('Img/rock.png', {}, function() { });
materials.push(new THREE.MeshPhongMaterial({color: 0xffffff, map: grasstexture}));
materials.push(new THREE.MeshPhongMaterial({color: 0xffffff, map: rocktexture}));
// Plane
this.geometry.materials = materials;
for(var i = 0; i < this.geometry.faces.length; i++)
{
this.geometry.faces[i].materialIndex = 0;
}
this.geometry.dynamic = true;
this.mesh = new THREE.Mesh( this.geometry, new THREE.MeshFaceMaterial( ) );
this.mesh.receiveShadow = true;
The result is a square plane stripped with the two textures.
Now, I can't seem to update the materialIndex for each vertex during the runtime. I have tried:
face.materialIndex = 1;
for(var i = 0; i < geo.materials.length; i++){
geo.materials[i].needsUpdate = true;
}
for(var i = 0; i < this.geometry.faces.length; i++)
{
geo.faces[i].materialIndex = 0;
}
for(var i = 0; i < geo.materials.length; i++){
geo.materials[i].needsUpdate = true;
}
this.mesh.needsUpdate = true;
this.mesh.material.needsUpdate = true;
geo.verticesNeedUpdate = true;
geo.normalsNeedUpdate = true;
geo.colorsNeedUpdate = true;
geo.computeFaceNormals();
geo.computeVertexNormals();
geo.computeCentroids();
geo.computeTangents() ;
And every other 'materialIndex' and 'needsUpdate' variable in Three.js I was able to find, but still nothing happens. How do I force an update on the material indices?
You can't. materialIndex is only used in the first render to partition the geometry into chunks, with each chunk having the same material. Also, you cannot re-partition the chunks.
What you can do, however, is change a material in the materials array.
materials[ 0 ] = new THREE.MeshBasicMaterial();
You do not need to set any needsUpdate flags to true after doing so, either.
An alternate thing you can do is give each face it's own material from the start, and then, for example, just change a material's texture.
mesh.geometry.materials[ 0 ].map = texture;
texture.needsUpdate = true;

Resources