ThreeJS assign array materials - javascript

If I assign framemat to createScene(ID,geometry,1,framemat) it works OK, if I do createScene( ID, geometry, 1, materials[ID] ) it doesn't work.
var jsonLoader = new THREE.JSONLoader(),
paths = [
"obj/jgd/GALD-JGD-frame.json",
"obj/jgd/GALD-JGD-logo.json",
"obj/jgd/GALD-JGD-light.json"
],
materials = [
"framemat",
"logomat",
"lightmat"
],
objNum = paths.length; // Cache obects.length
for ( var id = 0; id < paths.length; id++ ) {
(function(){ // IIFE start
jsonLoader.load( paths[id], function( geometry, materials ) {
createScene( id, geometry, 1, materials[id] )
}
);
})(); // IIFE end
}
CreateScene fun here:
function createScene( id, geometry, scale, material ) {
geometry.computeTangents();
objects[id]= new THREE.Mesh( geometry, material );
objects[id].scale.x = objects[id].scale.y = objects[id].scale.z = scale;
scene.add( objects[id] );
}

Your code is ambiguous : first you define an array called materials then you use the second argument of the JSONLoader and also name it materials to catch the textures in the json file, finally you use materials[id] to pass the material at the mesh creation. Which one is it ? Whereas the first array is still filled with strings, the json can have an array of materials instead of only one, worse of all the rank [id] may not exist in this one.
So you have two solutions :
If you want to create the materials independently, only keep the first array and add the missing function that transforms your strings to materials. Just remove the second argument of the JSONLoader.
If you want to use the material provided in the JSON File, the array materials is useless and can lead to other mistakes. There are two options then :
if you have one material for the whole object, write it this way : createScene(...,..., materials);
if you have several materials for the object, this way : createScene(....,....,new THREE.MeshFaceMaterial(materials);

Related

Three.js - How to replace the model of an Object3D?

I'm trying to replace an object3D with a character model, with another one, while keeping all it's properties, such as the position
var object = new THREE.Object3D( );
object = models.character[0].clone( );
object.position.set( 100, 230, 15 );
scene.add( object.scene );
const clip = THREE.AnimationClip.findByName( object.animations, 'idle' );
var action = mixer.clipAction( clip );
action.play();
//...later on, replace the model, while keeping it's position
object = models.character[1].clone( );
How do I replace the model of an THREE.Object3D( )?
An Object3D doesn't have a model because it doesn't have geometry or materials. This line of code doesn't do anything because you're immediately replacing the Object3D with something else:
var object = new THREE.Object3D( );
object = models.character[0].clone( );
I think you should be using a Mesh, which is a subclass of Object3D. Now, if you want to change the "model" of a Mesh, you can access its .geometry property, and assign it a new one:
// This is the original geometry
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(100, 230, 15);
// Here we assign a new geometry to the Mesh
mesh.geometry = someOtherGeometry;

Three.js - dynamic skeleton animation with BVH and JSON-model (not baked)

Brief description: How can a JSON-character-model be dynamically animated using Three.js and a BVH-motion capture-file? As in: The animation is not baked into the JSON-file but rather loaded and parsed from the BVH-file using BVHLoader.js
I got as far as loading the BVH-file, attaching its data to the skeletonHelper, extracting the bone-positions, animating some boxes based on their position (for testing pursposes) and loading a (hopefully?) rigged JSON-model. See the demo here: https://www.patrik-huebner.com/repo/JSON-BVH/
See a GIF of the animation here: https://www.patrik-huebner.com/repo/JSON-BVH/demo.gif (sadly, I can't yet post images)
I did quite some extensive research on this topic and while there are quite a lot of helpful tips[1] on stackoverflow on exporting a mesh from Blender to JSON using the Three.js-exporter and baking in animations, I was not able to figure out how to
a) set up a JSON-file / rigged model / it's skeleton/bones so that I can dynamically animate it (I used a rigged, low-poly from blendswap.com/blends/view/21948). Are there any recommended settings? In order to be able to load the file at all I have to check the "export scene"-option which I'm not quite certain why that should be necessary.
b) map the BVH-data to the JSON-model
If you have any advice and / or code-samples I can use - or at least some resources or keywords that I can research, I'd be most thankful !
Using the supplied BVHLoader.js I am able to load the animation and attach it to the skeletonHelper
var loader = new THREE.BVHLoader();
loader.load( "models/pirouette.bvh", function( result ) {
skeletonHelper = new THREE.SkeletonHelper( result.skeleton.bones[0] );
skeletonHelper.skeleton = result.skeleton; // allow animation mixer to bind to SkeletonHelper directly
boneContainer = new THREE.Group();
boneContainer.add( result.skeleton.bones[ 0 ] );
scene.add( skeletonHelper );
scene.add( boneContainer );
mixer = new THREE.AnimationMixer( skeletonHelper );
mixer.clipAction( result.clip ).setEffectiveWeight( 1.0 ).play();
} );
Though I am rather certain this is not how its done: I can access the Vector3-data of the skeletonHelper and animate some test-cubes based on those positions (compare code-sample above and my supplied GIF-animation)
for (var i = 0; i < skeletonHelper.bones.length; i++) {
var myPos = skeletonHelper.bones[i].getWorldPosition();
cubeGroup.children[i].position.x = myPos.x;
cubeGroup.children[i].position.y = myPos.y;
cubeGroup.children[i].position.z = myPos.z;
}
Finally, I load the JSON-file and add it to the scene using THREE.ObjectLoader (as the THREE.JSONLoader would not accept my exported model)
// Load the rigged JSON-file based on this character http://www.blendswap.com/blends/view/21948
// converted to JSON with the Three.json-export-plugin
var loader = new THREE.ObjectLoader();
var s = 50;
loader.load(
"models/lowPoly-rigged.json",
function ( obj ) {
var material = new THREE.MeshLambertMaterial({color: 0xff0000});
scene.add( obj );
obj.position.y = 0;
obj.children[0].material = material;
obj.scale.set(s,s,s);
},
function ( xhr ) {
console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
},
function ( xhr ) {
console.error( 'An error happened' );
}
);
But now: How do I animate the model using the loaded BVH-data. Do I have to match the amount of bones from the JSON-model to the BVH? Can I just supply some data to the model and have it happen "magically"? I have seen some online demos that load BVH-files dynamically and pass the data to a model[2]
[1] Stuff I did research (sadly, I can't post more links) that did not help, is not quite specific to what I am looking to achieve:
stackoverflow.com/questions/20433474/dynamic-bones-animation-in-three-js
stackoverflow.com/questions/29970791/animate-makehuman-character-with-bvh-using-three-js
stackoverflow.com/questions/40719572/three-js-wrong-bones-orientation (this did look the most helpful as it is actually about combining a loaded BVH-file and a model but researching/implementing the code with my own model lead to error-messages about the material which points to some misunderstanding of mine in the export-process from Blender to JSON)
[2] Some resources that were able to handle BVH-files and could attach those animations to a model:
BVH Player by Aki A. on chromeExperiments: chromeexperiments.com/experiment/bvh-player
lo-th.github.io/root/blending2/index_bvh.html
Again, thanks a lot for any input!

Model illumination

After loading a model with OBJMTLLoader it is automatically set as a MeshLambertMaterial and unfortunatelly i can't get it to be in full color and textures look much darker. I have some problems with setting the correct light in the scene.
I have added an ambient light but its not enough:
scene.add(new THREE.AmbientLight( 0xffffff ));
Is it possible to turn off the 'luminance' and make the mesh lambert material visible without any light in the scene?
Two ways to recreate material, one is open your model file and change material here in source file manually. Or when usilg object loader or json loader, use "for" syntax to go trough each material and set another values
var jsonLoader = new THREE.JSONLoader();
jsonLoader.load(model, addthree1ToScene);
function addthree1ToScene( geometry, materials )
{
var materiall = new THREE.MeshFaceMaterial( materials );
for ( var i = 0; i < materials.length; i ++ )
{
var material = materials[i];
material.side = THREE.DoubleSide; /// there you can set attributes
}
three1 = new THREE.Mesh( geometry, materiall );
three1.position.set(x,y,z);
scene.add( three1 );
console.log(three1);
}

Three.js: How to create new 'morphing' geometry if I have all necessary buffers?

I'm using a web-worker to load a .json file of an animated 3D model. For each of the big arrays (vertices, normals, etc.), I'm transferring an Float32Array buffer back to the UI thread. Since such buffers are transferable objects, this will take (almost) zero time.
Now, it turns out that WebGL (and therefore, Three.js) use Float32Array buffers internally, too. This means I could probably load this 3D animation without copying anything, spending almost zero time in the main thread. Isn't that nice?
But it's not clear how to do that part: In the main thread, we have array buffers available for the vertices, normals (the main ones, and the 'morph' ones) and faces. How do I create a working Geometry (or BufferGeometry) from these, without translating or copying the data?
var scene,
vertices, normals, faces,
morphVertices, morphNormals; // <-- we have all these as typed arrays
var geometry = ...; // <-- insert code here
var material = new THREE.MeshLambertMaterial({ morphTargets: true });
var object3D = new THREE.MorphAnimMesh(geometry, material);
scene.add(object3D);
This answer gives a hint, but only point 7 seems relevant, it assumes there is already some Geometry instance, and it doesn't handle morph-targets.
Here's an example based on the mesh loading portion of THREE.GLTF2Loader.
// Create BufferGeometry and assign vertices and normals.
var geometry = new THREE.BufferGeometry();
geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
geometry.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
geometry.setIndex( new THREE.BufferAttribute( faces, 3 ) );
// Create material.
var material = new THREE.MeshStandardMaterial({
morphTargets: true,
morphNormals: true
});
// Set up morph target attributes.
var posAttr = new THREE.BufferAttribute( morphVertices, 3 );
var normAttr = new THREE.BufferAttribute( morphNormals, 3 );
for (var i = 0; i < posAttr.array.length; i++) {
posAttr.array[i] += geometry.attributes.position.array[i];
}
for (var j = 0; j < normAttr.array.length; j++) {
normAttr.array[j] += geometry.attributes.normal.array[j];
}
// Assign morph target attributes.
geometry.morphAttributes.position = [ posAttr ];
geometry.morphAttributes.normal = [ normAttr ];
// Create Mesh.
var mesh = new THREE.Mesh(geometry, material);
mesh.updateMorphTargets();
// Apply 50/50 blend of morph targets and default position/normals.
mesh.morphTargetInfluences[0] = 0.5;
three.js r86-dev.

intersectObjects not returning obj from OBJMTLLoader

I have a object that I'm adding into my scene. I also have various other cubes in the scene. I'm using the following code to fire a Ray and see if I can detect a collision:
var ray = new THREE.Raycaster(camera.position, vec);
var intersects = ray.intersectObjects( scene.children );
For some reason, the regular shapes (cubes) are detected, but the objects that loaded from the obj files are not.
var loader = new THREE.OBJMTLLoader();
loader.load( 'models/technicalTable1.obj', 'models/technicalTable1.mtl', function ( obj ) {
obj.scale.set(0.4, 0.4, 0.4);
obj.position.x = - roomWidth/2 + 100;
obj.position.y = 36;
obj.position.z = - roomLength/2 + 25;
scene.add( obj );
}, onProgress, onError );
Thanks for any help!
You need to pass the recursive flag to Raycaster.intersectObjects().
var intersects = ray.intersectObjects( scene.children, true );
three.js r.70
It doesnt work because the OBJMTLLoader creates a Three.Group
The Group does not have a raycaster.intersectObjects
(I just finished tracing it down and it went to a non implemented function
This REALLY limits the the use of OBJ objects.
I have tried JSON but since you can not load materials and have to create UVmap I find that I need a person that can 3D model or not be able to use the intersectObjects.
Not sure if this is a bug

Categories

Resources