Multiple ColladaLoader loads fail. Is it thread safe? - three.js

I am trying to load multiple pieces simultaneously. Either only one piece shows up, correctly, or both pieces show up, with one correct and one incorrect. Here's my code:
var loader = THREE.ColladaLoader();
loader.load('model.dae', function colladaReady(result) {
var piece = result.scene.children[0];
piece.position.set(-100, 0, 0);
scene.add(piece);
renderer.render(scene, camera);
});
loader.load('model2.dae', function colladaReady2(result2) {
var piece2 = result2.scene.children[0];
piece2.position.set(100, 0, 0);
scene.add(piece2);
renderer.render(scene, camera);
});

The reason is because ColladaLoader is not thread-safe, so multiple load() calls cannot be made on the same object. Simply create a new instance of ColladaLoader like so and all should work
var loader = THREE.ColladaLoader();
loader.load('model.dae', function colladaReady(result) {
var piece = result.scene.children[0];
piece.position.set(-100, 0, 0);
scene.add(piece);
renderer.render(scene, camera);
});
/*** Line added here! ***/
var loader2 = THREE.ColladaLoader();
loader.load('model2.dae', function colladaReady2(result2) {
var piece2 = result2.scene.children[0];
piece2.position.set(100, 0, 0);
scene.add(piece2);
renderer.render(scene, camera);
});

Related

How do i animate my 3D model to spin like a spinning coin in three.js?

been stuck for hours trying to figure out how to make my 3d model spin like a coin spinning. my code is below anything help i really appreciate you guys.
// add a light
const light = new THREE.AmbientLight(0xffffff,5)
scene.add(light);
// controls
// load object
const loader = new THREE.GLTFLoader();
loader.load('yattee.gltf', function (gltf) {
scene.add(gltf.scene);
})
const render = function() {
requestAnimationFrame(render)
// camera.rotation.z += .0010
renderer.render(scene, camera);
}
render();
Try it like so:
let myModel;
const loader = new GLTFLoader();
loader.load( 'model.gltf', function ( gltf ) {
myModel = gltf.scene;
scene.add( myModel );
} );
const render = function() {
requestAnimationFrame(render)
if ( myModel ) {
myModel.rotation.z += 0.01;
}
renderer.render(scene, camera);
}
The problem is, you are defining the model gltf inside a function, which means that it cannot be accessed from render(). In order to spin the model, you need to define the variable gltf prior to loading the model, so it can be stored inside of it:
const loader = new THREE.GLTFLoader();
var gltf; //By defining it prior to loading, it is now in global scope.
loader.load('yattee.gltf', function (gltf) {
scene.add(gltf.scene);
})
Then, you can add the flipping effect in the render() loop. Make sure that you add the given if statement so it doesn't crash before the model even loads!
const render = function() {
requestAnimationFrame(render);
if(gltf) {
gltf.rotation.z += .0010; //Make sure you rotate the gltf, not the camera.
}
renderer.render(scene, camera);
}
And that should do it!
Note: I've taken a look at some of the other answers, and it seems like they either don't work, or they don't explain what they are doing properly. I think that this should help you achieve what you want.

Simply assign a Material to loaded .OBJ - ThreeJS

I've been hitting myself overhead with this one for the good three hours now..
My question is, would someone be kind enough to reorganize my example in such a way, that the object that I load comes with a color, any color
var loader = new THREE.OBJLoader()
loader.load( 'learning/exported1.obj', function ( object )
{
object.traverse( function (child)
{
if ( child instanceof THREE.Mesh )
{
child.material.color.setHex( 0xffffff );
}
}
var OBJBoundingBox = new THREE.Box3().setFromObject(object);
OBJBoundingBox.center(object.position);
object.position.multiplyScalar(-1);
object.position.x = object.position.x;
object.position.y = object.position.y;
object.position.z = object.position.z;
scene.add( object );
}, onProgress, onError );
If I throw the traverse part away the object is loaded successfully but is a simple white color, with it, the object does not appear on my screen...
<script src="../build/three.js"></script>
<script src="js/loaders/OBJLoader.js"></script>
<script src="js/controls/OrbitControls.js"></script>
<script src="js/shaders/CopyShader.js"></script>
<script src="js/shaders/SMAAShader.js"></script>
<script src="js/postprocessing/EffectComposer.js"></script>
<script src="js/postprocessing/SMAAPass.js"></script>
<script src="js/postprocessing/RenderPass.js"></script>
<script src="js/postprocessing/MaskPass.js"></script>
<script src="js/postprocessing/ShaderPass.js"></script>
<script src="js/libs/dat.gui.min.js"></script>
These are my imports in case you think the problem lies in here
Thanks so much in advance, this is a project which I'm developing independently and would love it if I can get it working!
I feel like an idiot but the thing I was missing was
);
at the end of object.traverse function
var loader = new THREE.OBJLoader()
loader.load( 'learning/exported1.obj', function ( object )
{
object.traverse( function (child)
{
if ( child instanceof THREE.Mesh )
{
child.material.color.setHex( 0xffffff );
}
});
// ^This over here^
var OBJBoundingBox = new THREE.Box3().setFromObject(object);
OBJBoundingBox.center(object.position);
object.position.multiplyScalar(-1);
object.position.x = object.position.x;
object.position.y = object.position.y;
object.position.z = object.position.z;
scene.add( object );
}, onProgress, onError );
Assigning a material now works perfectly with this code.

How can i call the BufferGeometryUtils from threejs?

I tried to merge two geometries pre-loaded from two PLY files, the console of the browser told me should be use the BufferGeometryUtils.mergeBufferGeometries() for merge de two geometries into one, but I some problems for call this from the class.
Here is my code:
var scene = new THREE.Scene();
var singleGeometry, material, mesh;
var loader = new THREE.PLYLoader();
function loadPLY(path) {
return new Promise(resolve => {
loader.load(path, result => {
console.log('Success');
resolve(result);
});
});
}
init();
Promise.all([
loadPLY("ply/Paso_1_mandible.ply"),
loadPLY("ply/maxila.ply")
])
.then(geometries => {
geometries.forEach(geometry => geometry.computeVertexNormals());
singleGeometry = new THREE.BufferGeometry();
singleGeometry = THREE.BufferGeometryUtils.mergeBufferGeometries(geometries);
material = new THREE.MeshPhongMaterial({color: 0x0055ff });
mesh = new THREE.Mesh(singleGeometry, material);
mesh.position.y = 1;
mesh.position.z = 1;
mesh.position.x = 1;
mesh.scale.multiplyScalar( 0.0001 );
mesh.castShadow = true;
mesh.receiveShadow = true;
scene.add(mesh);
})
.then(() => {
animate();
})
.catch(err => {console.error(err);});
but I got this error:
TypeError: "THREE.BufferGeometryUtils is undefined"
here is the link to the documentation:
class BufferGeometryUtils
It's in examples/js/BufferGeometryUtils.js in the THREE master distro.
https://github.com/mrdoob/three.js/archive/master.zip
The error TypeError: "THREE.BufferGeometryUtils is undefined" is quite clear.
You need to include the BufferGeometryUtils class / file in your code.
As it is in the examples folder, it is not included in the main Three.js library.

Jasmine and Three.js asynchronous loading of Geometry

I try to use Jasmine to automatically test some functionality. The issue I have is that I want to test a class and for that need to load a geometry for all related tests. However due to asynchronous loading at time of execution of the tests the geometry has not been loaded and is undefined and thus test cases fail. Any idea how to make sure the geometry is loaded at time of test execution? Here the snipet to load the geometry
describe("Model Class", function() {
var geometry;
beforeAll(function() {
var loader = new THREE.STLLoader();
loader.load( '../tests/testdata/cube_big.stl', function ( geo ) {
geometry = geo;
});
});
I believe the beforeAll() function takes one argument that you can call when async operations are complete:
beforeAll(function(done) {
var loader = new THREE.STLLoader();
loader.load( '../tests/testdata/cube_big.stl', function ( geo ) {
geometry = geo;
done();
});
});

Cancel Animation in Three.js

I'd like to stop a Three.js animation properly in order to execute a function afterwards.
This is the code I have :
var anim;
function animate() {
anim = requestAnimationFrame(animate);
checkRotation();
renderer.render(scene, camera);
setTimeout(cancel, 10000);
}
function cancel() {
cancelAnimationFrame(anim);
finalCoords();
}
animate();
Being put like this, the animation checkRotation() indeed stops but the function finalCoords() keeps looping as if it was trapped in some kind of recursion.
I have also tried this :
var anim;
function animate(anima) {
anima = requestAnimationFrame(animate);
checkRotation();
renderer.render(scene, camera);
}
function cancel(anima) {
cancelAnimationFrame(anima);
finalCoords();
}
animate(anim);
setTimeout(cancel(anim), 10000);
Now the loop stops but the function finalCoords() doesn't return proper results and the animation doesn't stop.
What did I do wrong ? and how can I fix it ?
Thanks!
1) The problem in the first example is that in each animation frame you install another new timeout. Accordingly, for example, if you have 60 frames per second for 10,000 seconds you will set 600 million new timeouts.
2) The problem is the second example that you just are not transmitted in time-out function, but once it is called. Plus you are confused with the names of the variables in which to store the identifier of the frame of animation.
3) An improved version of the second example:
var stopAnimate = false;
function animate(time) {
if (!stopAnimate) {
checkRotation(time);
requestAnimationFrame(animate);
} else {
finalCoords('final');
}
}
animate();
setTimeout( function() {
stopAnimate = true;
}, 1000);
[ https://jsfiddle.net/02d37pxs/ ]

Resources