I'm trying to recreate a classroom with individual students and furniture. It should be individual group of models that can be later assigned to a database to toggle on and off at will to simulate who's attending and who's missing in class. This is my block of code to load one native threejs model called "desk" and one animated GLTF model into the scene:
import {GLTFLoader} from "./GLTFLoader.js"
import * as THREE from 'three'
var unit = new THREE.Group();
var deskGeometry = new THREE.BoxBufferGeometry(10, 2, 50 );
var deskMaterial = new THREE.MeshPhongMaterial({ color: 0x49311c, side: THREE.DoubleSide });
var desk = new THREE.Mesh(deskGeometry, deskMaterial);
unit.add(desk);
//IMPORT GLTF MODEL
var loader = new GLTFLoader();
var clock, mixer;
clock = new THREE.Clock();
loader.load("./models/salary/scene.gltf", function(gltf){
gltf.scene.traverse( function( node ){
if(node.isMesh){node.castShadow = true, node.receiveShadow = false;}
});
//add and position the GLTF model
var obj = gltf.scene;
gltf.scene.scale.set( 8, 8, 8 );
gltf.scene.position.x = -10;
gltf.scene.position.y = -5;
gltf.scene.position.z = 3;
gltf.scene.rotation.y = 1.07;
scene.add(obj);
unit.add(obj); // unit is part of an array that the model is nested in,
//to create a THREE.group consisted of the student's desk, pc and the student
//Playing Animation
mixer = new THREE.AnimationMixer( gltf.scene );
console.log( gltf.animations );
mixer.clipAction( gltf.animations[ 0 ] ).play();
});
return unit;
}
function animate(){
requestAnimationFrame(animate);
renderer.setAnimationLoop(()=>renderer.tick());
//for GLTF
if ( mixer ) mixer.update( clock.getDelta() );
}
The above code also implies that the GLTF model is part of a group of model called "unit", and that there are native threejs models mixed in. In the near future I wish to replace "desk" with a proper GLTF model. So I tried using promise to load multiple models like this https://redstapler.co/load-multiple-model-three-js-promise/ , however, it only showed how to import static models without animations.
How should I use promise to load multiple animations as well, while nested inside a THREE group?
Related
I've got a gltf file. When I import it inside the Three.js editor (https://threejs.org/editor/) I get a correct result when I add an environment map.
On the other hand, when I import my gltf in my project scene I've a different result. Even when I use the very same HDRI image. The metalness is way too shinny in this case.
Does anyone know what I'm missing? Thank you.
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1;
renderer.outputEncoding = THREE.sRGBEncoding;
new RGBELoader()
.load( 'royal_esplanade_1k.hdr', function ( texture ) {
texture.mapping = THREE.EquirectangularReflectionMapping;
scene.environment = texture;
} );
loader.load(
'./gltf/canette.glb',
// called when the resource is loaded
function ( gltf ) {
obj = gltf.scene;
mixer = new THREE.AnimationMixer( gltf.scene );
action = mixer.clipAction( gltf.animations[ 0 ] );
//obj
scene.add( obj );
}
);
EDIT :
Here's a live demo.
Here's the gltf model.
Unfortunately, you are using a code snippet which does not match your actual three.js version. You have to use at least r131 (or better the latest one r141). Right now, you are using r129.
If you use three.js versions below r131, you have to use PMREMGenerator to prepare the environment map that you apply to PBR materials. Starting from r131, the engine is doing this for you so you don't have to worry about PMREMGenerator at all.
I'm trying to simplify an 3D Model (.STL file) that is loaded using STLLoader and the Three.js function SimplifyModifier to reduce vertices and simplify the model.
I have a function called loadMesh that loads the file and return an Three.js mesh that renders on a Scene:
/**
* Method to load the mesh model from .stl or .3mf file and return Mesh ThreeJS object
* #param {*} part Part or model of a part, on form of a .stl or .3mf file
* #param {*} color Color of the mesh material. Default = #0096d6
* #returns Mesh from ThreeJS
*/
static loadMesh(part, color = '#0096d6') {
if (!FileUtil.existsPath(part.path)) return null;
const loader = (Loader, encoding) => new Loader().parse(FileUtil.readFile(part.path, encoding));
let mesh = null;
const material = new THREE.MeshStandardMaterial({ color, flatShading: true });
const ext = FileUtil.getExtName(part.path, true).toLowerCase();
if (ext == 'stl') {
const geometry = loader(STLLoader, 'binary');
// Make an instance of SimplifyModifier
const modifier = new SimplifyModifier();
// Create Mesh wit geometry and material already defined
mesh = new THREE.Mesh(geometry, material);
// Number of vertices to remove from part
const count = Math.floor( mesh.geometry.attributes.position.count * 0.1 );
console.log(count);
// Now modify the geometry of our mesh to optimize number of polygons
mesh.geometry = modifier.modify( mesh.geometry, count );
} else if (ext == '3mf') {
mesh = loader(ThreeMFLoader, 'buffer');
for (const [index, buildItem] of Object.entries(mesh.children)) {
(function updateMaterial(children) {
for (const child of children) {
if (child.children?.length > 0) {
updateMaterial(child.children);
} else {
child.index = String(index);
child.material = material.clone();
}
}
})(buildItem.children);
}
if (part.buildItemIndex != 'all') {
mesh = mesh.children[+part.buildItemIndex];
mesh.position.set(0, 0, 0);
}
}
mesh.part = part;
MeshUtil.resetOrigin(mesh);
return mesh;
}
Currently the app supports two 3d files extensions: .STL and .3MF, that's why you can use that the function checks the extension and its executes based on that. But for the moment im trying to simplify an STL model, so the important thing here is happening inside the STL if condition:
const geometry = loader(STLLoader, 'binary');
// Make an instance of SimplifyModifier
const modifier = new SimplifyModifier();
// Create Mesh wit geometry and material already defined
mesh = new THREE.Mesh(geometry, material);
// Number of vertices to remove from part
const count = Math.floor( mesh.geometry.attributes.position.count * 0.1 );
//
console.log(count);
// Now modify the geometry of our mesh to optimize number of polygons
mesh.geometry = modifier.modify( mesh.geometry, count );
When I try to render the model on another component using
// mesh is the returned mesh from the above function
scene.add(mesh);
I get the next error on console:
Notes
Already tried to change count of vertices to remove and there is no difference.
For better reference, there are the properties of the geometry object that im trying to simplify:
The purpose of this is because in some cases the app can receive large files and it crashes when trying to render on Scene.
Also, don't know if there is any problem that is BufferGeometry, and if its the case, how can I solved that?
Was wondering if it's possible to translate, rotate, scale a model after it has been loaded to the scene. Also im looking a better way to use the loaded, since I have multiple models that I want to add in my program, but I do not want to call `loaded.load(...); every time.
Here is what I use:
var loader = new THREE.GLTFLoader();
loader.load( 'models/model_environment/scene.gltf',
function ( gltf ) {
cube_Geometry = new THREE.BoxGeometry(10,10,10);
cube_Material = new THREE.MeshNormalMaterial();
cube_Mesh = new THREE.Mesh(cube_Geometry, cube_Material);
cube_Mesh.position.set(0,5,0);
canvas_Scene.add(cube_Mesh);
canvas_Scene.add(gltf.scene);
modelsLoaded = true;
},
undefined,
function ( error ) {
console.error( error );
}
);
Notice that the model that has been loaded is below 0 on Y Axis (Ground):
I have several models in Clara.io, according to their help if you export selection, then it will be file for JSONLoader, and if you export full scene it will be file for ObjectLoader. However non of export function working with JSONLoader (https://forum.clara.io/t/export-to-three-js-json-export-all-and-export-selected-both-export-scene-object/3709).
In my app I need only geometry from model to build Points object. So I'm looking for either methods to convert loaded object to Mesh, or some king of geometry subtraction from object itself. In helps and examples(both three and clara) I see only one action with loaded object - scene.add(object).
import THREE from 'three/build/three.module';
import {Scene,PerspectiveCamera,Fog,WebGLRenderer,ObjectLoader,Geometry} from 'three/build/three.module';
....
var loader = new ObjectLoader();
loader.load( 'assets/models/rabbit.json', function ( object ) {
//I don't need to add object here but this is the only thing that works
//scene.add(object);
//I need to do something like this
geometry2 = new Geometry();
geometry2.vertices = object.geometry.vertices; // ???
particles = new Points( geometry2, new PointsMaterial( { color: 0xff0000, size:5 } ) );
scene.add(particles)
});
So clara.io seems to export single object in scene. So the way to extract mesh from it is something like:
loader.load( 'assets/models/rabbit.json', function ( object ) {
//I don't need to add object here but this is the only thing that works
//scene.add(object);
//I need to do something like this
geometry2 = new Geometry();
geometry2.vertices = object.children[0].geometry.vertices;
//object.children[0] - the first mesh in exported object
particles = new Points( geometry2, new PointsMaterial( { color: 0xff0000, size:5 } ) );
scene.add(particles)
});
I have been using Three.js for a few weeks now, I managed to apply a texture to a cube created directly via the code, but once I tried to load an OBJ file with OBJLoaderI noticed that I couldn't use the same method to load simple png or jpg textures with TextureLoader.
Now I'm not entirely sure what is going wrong, if either this is not the right way to do it, if I just can't load a simple image fine or if I'm not applying it right.
I've seen that applying a texture seems quite easy to do with an MTL file, but unfortunately I'm not sure how to make one.
My current code looks like something like this:
var loader = new THREE.OBJLoader( manager );
loader.load( 'models/tshirt.obj', function ( object ) {
object.position.x = 0;
object.position.y = -200;
object.scale.x = 10;
object.scale.y = 10;
object.scale.z = 10;
obj = object;
texture = THREE.TextureLoader('img/crate.png'),
material = new THREE.MeshLambertMaterial( { map: texture } );
mesh = new THREE.Mesh( object, material );
scene.add( mesh );
} );
But it just doesn't seem to work. The model doesn't load and gives me random errors from Three.js. If instead of the code above I change scene.add( obj); the model actually loads.
What should I be doing here? Should I just go ahead and try to make an MTL file?
My full working code can bee seen at http://creativiii.com/3Dproject/old-index.html
EDIT: The error I get when adding mesh instead of obj is
three.min.js:436 Uncaught TypeError: Cannot read property 'center' of undefined
Try this code:
var OBJFile = 'my/mesh.obj';
var MTLFile = 'my/mesh.mtl';
var JPGFile = 'my/texture.jpg';
new THREE.MTLLoader()
.load(MTLFile, function (materials) {
materials.preload();
new THREE.OBJLoader()
.setMaterials(materials)
.load(OBJFile, function (object) {
object.position.y = - 95;
var texture = new THREE.TextureLoader().load(JPGFile);
object.traverse(function (child) { // aka setTexture
if (child instanceof THREE.Mesh) {
child.material.map = texture;
}
});
scene.add(object);
});
});
The texture loading changes was taken from: How to apply texture on an object loaded by OBJLoader? .