I wish to group loaded json models in THREE.js but am getting an error...
THREE.Object3D.add: object not an instance of THREE.Object3D
I'm using the example code of...
var group = new THREE.Object3D();
and then...
group.add(model1);
group.add(model2);
I load model1 and model2 previously - these render out fine when I add them to the scene individually, but when I add the group I get the error...
scene.add( group );
I've tried a combination of different techniques such as THREE.Mesh() or adding json models to other json models eg. model1.add(model2).
R72
The problem is asynchronous loading of the models - trying to perform actions on the models before they are loaded.
group = new THREE.Mesh(); //global to add transforms
var jsonLoader = new THREE.JSONLoader();
jsonLoader.load( "model1.json", function(geometry,materials) {
var mesh = createMesh(geometry,materials); //function to create generic mesh properties
group.add(mesh);
});
jsonLoader.load( "model2.json", function(geometry,materials) {
var mesh = createMesh(geometry,materials); //function to create generic mesh properties
group.add(mesh);
});
scene.add( group );
function createMesh( geometry, materials )
{
var material = new THREE.MeshFaceMaterial( materials );
mesh = new THREE.Mesh( geometry, material );
return mesh;
};
Related
I am trying to replace a cube mesh with a car mesh using .obj and .mtl files (using three.js)
But every everytime I got this error:
Uncaught TypeError: e.OBJMTLLoader is not a constructor
I made sure to include the library () and there is no typo.
This is how I load the mesh model:
var loader = new THREE.OBJMTLLoader();
loader.load('./toycar.obj', './toycar.mtl',
function (object) {
toycar = object;
toycar.rotateX(-12); //toycar.rotateZ(-10.99);
toycar.rotateY(4.718);
scene.add(toycar);
});
and this is how I use it to move the model on the y axis:
if (toycar != undefined){
toycar.position.y = disp * 0.07; // z for rightLeft, y for upDown
var relativeCameraOffset = new THREE.Vector3(5, 0, 0); // change camera offset
var cameraOffset = relativeCameraOffset.applyMatrix4(toycar.matrixWorld);
camera.position.x = cameraOffset.x;
camera.position.y = cameraOffset.y;
camera.position.z = cameraOffset.z;
camera.lookAt(toycar.position);
}
At this point, I am not sure what caused this error to happen.
THREE.OBJMTLLoader does not exist in the repository. There is only OBJLoader and MTLLoader. The latter one is only required if you want to load materials, too.
I suggest you study webgl_loader_obj_mtl to show how both are used. Typical code looks like so:
new MTLLoader().load( 'materials.mtl', function ( materials ) {
materials.preload();
new OBJLoader()
.setMaterials( materials )
.load( 'object.obj', function ( object ) {
scene.add( object );
} );
} );
I'm developing an AR application using WebRTC (webcam access), JSARToolKit (marker detection) and threeJS (3D library).
I want to place 3D objects (exported from Maya using threejs maya exporter) in the center of the detected marker.
This is the code where I load the 3D object using JSONLoader:
// load the model
var loader = new THREE.JSONLoader;
var object;
//var geometry = new THREE.BoxGeometry(1, 1, 1);
loader.load('js/cube.js', function(geometry, materials){
var material = new THREE.MeshFaceMaterial(materials);
object = new THREE.Mesh(geometry, material);
object.position.x -= ***3DobjectWidth/2***;
object.position.y -= ***3DobjectHeight/2***;
object.position.z -= ***3DobjectDepth/2***;
scene.add(object);
});
I need to get width, height and depth of the object to change his position (see 3DobjectWidth ecc).
Any suggestions?
The object size will be placed at geometry.boundingBox. But it has to be generated once.
try this.
geometry.computeBoundingBox();
var bb = geometry.boundingBox;
var object3DWidth = bb.max.x - bb.min.x;
var object3DHeight = bb.max.y - bb.min.y;
var object3DDepth = bb.max.z - bb.min.z;
Geometry has a .center() method. That might be more efficient and simpler if you need to center many meshes based on same geometry. It also calls computeBoundingBox for you.
I´m trying to load some STL files using Three.js. The models are loaded correctly, but there are too many triangles that I would like to merge/smooth.
I had successfully applied smooth loading terrains in other 3D formats, but I can´t do it with the BufferGeometry that results from loading an STL file with the STLLoader.
_
var material = new THREE.MeshLambertMaterial( { ... } );
var path = "./models/budah.stl";
var loader = new THREE.STLLoader();
loader.load( path, function ( object ) {
object.computeBoundingBox();
object.computeBoundingSphere();
object.computeFaceNormals();
object.computeVertexNormals();
object.normalizeNormals();
object.center();
// Apply smooth
var modifier = new THREE.SubdivisionModifier( 1);
var smooth = smooth = object.clone();
smooth.mergeVertices();
smooth.computeFaceNormals();
smooth.computeVertexNormals();
modifier.modify( smooth );
scene.add( smooth );
});
This is what I tried, it throws an error: Uncaught TypeError: smooth.mergeVertices is not a function
If I comment the "mergeVertices()" line, what I get is a different error: Uncaught TypeError: Cannot read property 'length' of undefined in SubdivisionsModifier, line 156.
It seems that the sample codes I´m trying are outdated (this is happenning a lot recently due to the massive changes in the Three.JS library). Or maybe I´m forgetting something. The fact is that the vertices seems to be null..?
Thanks in advance!
It seems I was looking in the wrong direction: smoothing the triangles has nothing to do with the SubdivisionsModifier... What I needed was easier than that, just compute the vertex BEFORE applying the material, so it can use SmoothShading instead of FlatShading (did I got it right?).
The problem here was that the BufferGeometry returned by the STLLoader has not calculated vertices/vertex, so I had to do it manually. After that, apply mergeVertices() just before computeVertexNormals() and voilà! The triangles dissappear and everything is smooth:
var material = new THREE.MeshLambertMaterial( { ... } );
var path = "./models/budah.stl";
var loader = new THREE.STLLoader();
loader.load( path, function ( object ) {
object.computeBoundingBox();
object.computeVertexNormals();
object.center();
///////////////////////////////////////////////////////////////
var attrib = object.getAttribute('position');
if(attrib === undefined) {
throw new Error('a given BufferGeometry object must have a position attribute.');
}
var positions = attrib.array;
var vertices = [];
for(var i = 0, n = positions.length; i < n; i += 3) {
var x = positions[i];
var y = positions[i + 1];
var z = positions[i + 2];
vertices.push(new THREE.Vector3(x, y, z));
}
var faces = [];
for(var i = 0, n = vertices.length; i < n; i += 3) {
faces.push(new THREE.Face3(i, i + 1, i + 2));
}
var geometry = new THREE.Geometry();
geometry.vertices = vertices;
geometry.faces = faces;
geometry.computeFaceNormals();
geometry.mergeVertices()
geometry.computeVertexNormals();
///////////////////////////////////////////////////////////////
var mesh = new THREE.Mesh(geometry, material);
scene.add( mesh );
});
Than, you can convert it back to BufferGeometry, because it's more GPU/CPU efficient for more complex models:
var geometry = new THREE.Geometry();
geometry.vertices = vertices;
geometry.faces = faces;
geometry.computeFaceNormals();
geometry.mergeVertices();
geometry.computeVertexNormals();
var buffer_g = new THREE.BufferGeometry();
buffer_g.fromGeometry(geometry);
var mesh = new THREE.Mesh(buffer_g, material);
scene.add( mesh )
Happened this issue for me while loading an obj file. If you have a 3d software like 3dsmax:
Open the obj file,
Go to polygons selection mode and select all polygons.
Under the Surface properties panel, click 'Auto Smooth' button.
Export the model back to obj format
Now you won't have to call the functions geometry.mergeVertices() and geometry.computeVertexNormals();. Just load the obj and add to the scene, mesh will be smooth.
EDIT:
My obj files had meshphongmaterial by default and on changing the shading property to value 2 the mesh became smooth.
child.material.shading = 2
STL does not support vertex index.
That is reason it has duplicated vertex of all triangles.
Each vertex has its normal as triangle normal.
As a result, at same position( multiple very closed vertices), there is multiple normal value.
This leads to non-smooth surface of geometry when using Normal for lighting calculation.
So I am working with the webgl_interactive_cubes.html in the three.js examples, and I have a relatively simple question: Is it possible to test for intersection of a ray with the children of an object.
for example, if I do something like:
for ( var i = 0; i < 2000; i ++ ) {
var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) );
scene.add( object );
}
When I call
var intersects = raycaster.intersectObjects( scene.children );
It will intersect these objects. However if I first create a 'subScene' like so:
var subScene = new THREE.Object3D();
scene.add(subScene);
And then add all of these objects to the subScene instead of the scene, the intersection will no longer occur.
Is it at all possible to intersect ALL the objects in the scene and subscenes ( in the final project I would like to have many layers of nested subScenes ) Or should I try to keep all objects in the same scene if I am using raycasting?
Thank you in advance for your time,
Isaac
You just need to set the recursive flag:
var intersects = raycaster.intersectObjects( scene.children, true );
three.js r.58
In THREE.js, if I have multiple calls to the JSONLoader to load multiple objects like this (simplified example):
function init() {
var loader = new THREE.JSONLoader();
loader.load("mesh1.js", createScene);
loader.load("mesh2.js", createScene);
}
function createScene( geometry ) {
if (geometry.filename == "mesh1.js") {
mesh1 = new THREE.Mesh( geometry, material );
scene.add( mesh1 );
} else if (geometry.filename == "mesh2.js") {
mesh2 = new THREE.Mesh( geometry, material );
scene.add( mesh2 );
}
}
How can I determine which mesh has been returned on callback, especially when they frequently arrive out of order?
I'm trying to handle multiple returned meshes with a single generic callback function. Is there some property in the returned geometry that indicates the original filename that I can test against?
Or perhaps there's a more elegant way? Perhaps creating a new THREE.JSONLoader object for each call would help the callback function determine which mesh has arrived?
I appreciate any help/ideas! Thanks!
well there is a more generic way then what WestLangley proposes.
loader.load( "mesh1.js", meshloader("mesh1.js"));
loader.load( "mesh2.js", meshloader("mesh2.js"));
then
function meshloader(fileName){
return function(geometry){
...
}
}
This way, you can add a identifier on each loaded file.
How about something like this?
loader.load( "mesh1.js", function( geometry ) { createScene( geometry, 1 ) } );
loader.load( "mesh2.js", function( geometry ) { createScene( geometry, 2 ) } );
Then,
function createScene( geometry, id ) {
...
}
The id can be the mesh name if you prefer that.