I have a local 3D object model and I load it with:
const loader = new GLTFLoader();
loader.load("path/to/model", gltf => {
scene.add(gltf.scene);
});
How should I create a mesh out of the gltf, because when I try:
loader.load("path/to/model", (geometry, materials) => {
var mesh = new THREE.Mesh(geometry, materials);
scene.add(mesh);
mesh.position.z = -10;
});
I get the error:
Cannot convert undefined to object
I found out that actually by modifying the first function above like this:
loader.load("path/to/model", gltf => {
var object = gltf.scene.children[0];
scene.add(gltf.scene);
});
is the solution.
Because now object variable can be totally handled like a normal mesh.
Maybe a video to help you.
Related
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?
I have a Boat modal that is being imported as a JSON. Right now I am trying to also import it's texture map to apply to it's mesh.
The model loads fine, but when I use TGALoader to try to load the texture's .tga file, I get this error:
Uncaught TypeError: THREE.TGALoader is not a constructor
I have searched around for any other instances of this and can't find any. I am extremly new to threejs so I'm sorry - this is probably a super noob question.
Here is my code for the Boat class:
var scale = 10;
class Boat {
constructor(scene) {
var loader = new THREE.JSONLoader();
var textureLoader = new THREE.TGALoader();
// Texture
var texture = textureLoader.load('models/BW Bandit all in one.tga');
// Model
loader.load('models/boat.json', handle_load);
function handle_load(geom, mats) {
var material = new THREE.MeshPhongMaterial({
color: 0xffffff,
map: texture
});
var mesh = new THREE.Mesh(geom, material);
scene.add(mesh);
mesh.position.z = -5;
mesh.scale.set(scale, scale, scale);
mesh.rotation.y = -90;
}
}
}
You need to source TGALoader.js loader at the top of your file.
So for example say:
<script src="path_to_parent_folder_of_threeJS/threeJS/examples/js/loaders/TGALoader.js"></script>
I have been trying to find a way to be able to toggle between two scenes in three.js. I am aware that one can load a scene by using sceneLoader / exportScene combo.
Code taken from josdirksen/learning-threejs - loading a scene
var controls = new function () {
this.exportScene = function () {
var exporter = new THREE.SceneExporter();
var sceneJson = JSON.stringify(exporter.parse(scene));
localStorage.setItem('scene', sceneJson);
};
this.clearScene = function () {
scene = new THREE.Scene();
};
this.importScene = function () {
var json = (localStorage.getItem('scene'));
var sceneLoader = new THREE.SceneLoader();
sceneLoader.parse(JSON.parse(json), function (e) {
scene = e.scene;
}, '.');
}
};
From my understanding of the above code you need to have the scene loaded first before you can extract it and save to local storage before you can put it back into the scene. I am also aware that SceneLoader is now deprecated.
For my senario I want to have an initial scene load and by clicking the 'scene2' button I then want to display scene2 only and if I click the 'scene1' button go back to seeing scene1 only (see fiddle below).
A Basic Example setup
I'm not sure where to begin with this, so any pointers suggestions or advice would be helpful.
If you need to just switch to new scene, then why not have two scene object and one main scene. Try following code
/* Buttons to handle scene switch */
$("#scene2").click(function() {
scene = scene2
})
$("#scene1").click(function() {
scene = scene1
})
function init() {
....
/* I dont think you need to add camera to scene for viewing perpose. By doing this, essentially you are adding camera object to scene, and you won't be able to see it because scene is rendered using this camera and camera eye is at same location
*/
scene1 = new THREE.Scene();
// Build scene1
// scene1.add(camera);
scene2 = new THREE.Scene();
// Build scene2
// Choosing default scene as scene1
scene = scene1;
}
function render() {
// Try some checking to update what is necessary
renderer.render(scene, camera);
}
Updated jsfiddle
You can redraw the canvas by removing current scene scene.remove(mesh); and add create new mesh add into scene
Demo http://jsfiddle.net/sumitridhal/x8t801f5/4/
You can add custom controls using dat.GUI library.
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.4/dat.gui.js"></script>
//
var controls = new function() {
// we need the first child, since it's a multimaterial
this.radius = 10;
this.detail = 0;
this.type = 'Icosahedron';
this.redraw = function() {
// remove the old plane
scene.remove(mesh);
// create a new one
// add it to the scene.
scene.add(mesh);
}
});
var gui = new dat.GUI();
gui.add(controls, 'radius', 0, 40).step(1).onChange(controls.redraw);
gui.add(controls, 'detail', 0, 3).step(1).onChange(controls.redraw);
gui.add(controls, 'type', ['Icosahedron', 'Tetrahedron', 'Octahedron', 'Custom']).onChange(controls.redraw);
Demo http://codepen.io/sumitridhal/pen/NjbGpB
Smooth shading seems to have no effect on my model - the polygons are clearly visible and toggling between smooth and flat shading in the three.js inspector has no effect. There is vertex normal data in the obj so using geometry.computeVertexNormals() shouldn't be needed as has been suggested on a few other posts.
Using THREE.WebGLRenderer
Any leads appreciated :)
obj,mtl,img files
This is my loader function:
var mtlLoader = new THREE.MTLLoader();
mtlLoader.setBaseUrl(pathToLoad);
mtlLoader.setPath(pathToLoad);
mtlLoader.load('model_mesh.obj.mtl', function (materials) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials);
objLoader.setPath(pathToLoad);
objLoader.load('model_mesh.obj', function (obj) {
obj.name = pathToLoad.substring(12, 17);
testArray.push(obj);
scene.add(obj);
loadNextPath();
});
});
screenshot, polygons visible
The problem is with your model. It has "flat" vertex normals.
If you don't want to fix your model, you can recompute smooth vertex normals by using a pattern like this one:
loader.load( 'model_mesh.obj', function( object ) {
var mesh = object.children[ 0 ];
mesh.geometry = new THREE.Geometry().fromBufferGeometry( mesh.geometry );
mesh.geometry.mergeVertices();
mesh.geometry.computeVertexNormals();
// convert back to BufferGeometry prior to rendering
mesh.geometry = new THREE.BufferGeometry().fromGeometry( mesh.geometry );
scene.add( object );
});
three.js r.95
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? .