I'm attempting to user the Subdivision Modifier on an objectloader-loaded scene. I am getting the following javascript error when attempting this:
"BufferSubdivisionModifier.js:514 Uncaught TypeError: Cannot read property 'array' of undefined"
My code is the following to load the object with the subdivision modifier.
var loader = new THREE.ObjectLoader();
loader.load("../js/brain2.json", function(object) {
var material = new THREE.MeshPhongMaterial( { color: 0x888888, specular: 0x222222, shininess: 20} );
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
var modifier = new THREE.BufferSubdivisionModifier( 1 );
child.material = material;
child.geometry.computeFaceNormals();
modifier.modify( child.geometry );
child.material.overdraw = 1
};
});
object.scale.set(15, 15, 15);
object.position.x = 1;
object.position.y = 1;
object.position.z = 1;
object.rotation.set( 1, 1, 1 );
scene.add( object );
});
If I had to guess, it's that the subdivision modifier dislikes the 'child.geometry' object?
Solution is to load geometries for the chilren.
var loader = new THREE.ObjectLoader();
loader.load("../js/brain2.json", function(object) {
var material = new THREE.MeshPhongMaterial( { color: 0x888888, specular: 0x222222, shininess: 20} );
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.material = material;
var geometry = new THREE.Geometry().fromBufferGeometry( child.geometry );
geometry.computeFaceNormals();
geometry.mergeVertices();
geometry.computeVertexNormals();
child.geometry = new THREE.BufferGeometry().fromGeometry( geometry );
var modifier = new THREE.BufferSubdivisionModifier(1);
modifier.modify(geometry);
child.material.overdraw = 1
};
});
object.scale.set(15, 15, 15);
object.position.x = 1;
object.position.y = 1;
object.position.z = 1;
object.rotation.set( 5, 1, 1 );
scene.add( object );
});
Related
So I have a glb that I just isnt showing up in the scene no matter where i position it maybe it's the code? (as i've tried glb's i know work with the same code and non appear) here are the relevant code parts:
var loader2 = new GLTFLoader();
loader2.load( 'https://jokertattoo.co.uk/gun.glb', function ( gltfgame ) {
hammer = gltfgame.scene;
gltfgame.scene.traverse( function ( child ) {
if ( child.isMesh ) {
child.frustumCulled = false;
child.castShadow = true;
child.receiveShadow = true;
}
});
hammer.position.y = 120;
scene.add(hammer);
});
and:
function hammerLoaded( geometry ) {
geometry.computeVertexNormals();
geometry.computeFaceNormals();
hammer.position.y = 120;
scene.add(hammer);
var wood = new THREE.MeshPhongMaterial( { color: 0xffffff, map: THREE.ImageUtils.loadTexture( "284-v7.jpg"), shininess: 100, side: THREE.DoubleSide } );
geometry.materials[0] = wood;
var metal = new THREE.MeshPhongMaterial( { color: 0x222222, specular: 0x888888, shininess: 10, metal: true } );
geometry.materials[1] = metal;
var h = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial() );
h.castShadow = true;
h.receiveShadow = false;
var scale = 3.5;
h.scale.set(scale,scale,scale);
h.rotation.x = Math.PI/2;
h.rotation.z = -Math.PI/2;
h.position.y = 12.0;
h.position.z = -26.5;
h.position.x = -6.2;
hammer.add(h);
}
heres a codepen also https://codepen.io/uiunicorn/pen/YzQjJmp
thanks for reading
When I create multiple mesh's with the same name I can't select them all when I want to remove them from a scene.
I've tried traversing the function to no avail.
event.preventDefault();
scene.traverse(function(child) {
if (child.name === "blueTiles") {
var remove_object = scene.getObjectByName( "blueTiles", true );
scene.remove(remove_object);
}
});
var surroundMaterial = new THREE.MeshBasicMaterial({ color: 0x154995, side: THREE.DoubleSide, transparent: true, opacity: 0.8 });
surroundingCubes = new THREE.Mesh( geometry, surroundMaterial );
scene.add( surroundingCubes );
surroundingCubes.name = "blueTiles";
surroundingCubes.position.set(selectedObject.position.x - 1, 0.11, selectedObject.position.z);
var surroundMaterial = new THREE.MeshBasicMaterial({ color: 0x154995, side: THREE.DoubleSide, transparent: true, opacity: 0.8 });
surroundingCubes = new THREE.Mesh( geometry, surroundMaterial );
scene.add( surroundingCubes );
surroundingCubes.name = "blueTiles";
surroundingCubes.position.set(selectedObject.position.x + 1, 0.11, selectedObject.position.z);
surroundingCubes.rotation.x = Math.PI / 2;
I should be able to only delete all the objects with the name blueTiles
EDIT I switched from names to Groups, and that worked wonders
SOLUTION BELOW
function onDocumentMouseDown(event) {
for (var i = group.children.length - 1; i >= 0; i--) {
group.remove(group.children[i]);
}
var surroundingMaterial = new THREE.MeshBasicMaterial({ color: 0x154995, side: THREE.DoubleSide, transparent: true, opacity: 0.8 });
var geometry = new THREE.PlaneGeometry(1, 1, 1, 1);
if ( selectedObject.position.x - 1 >= 0) {
surroundingCube = new THREE.Mesh( geometry, surroundingMaterial );
surroundingCube.position.set(selectedObject.position.x - 1, 0.11, selectedObject.position.z);
surroundingCube.rotation.x = Math.PI / 2;
group.add(surroundingCube);
}
if ( selectedObject.position.x + 1 <= 9) {
surroundingCube = new THREE.Mesh( geometry, surroundingMaterial );
surroundingCube.position.set(selectedObject.position.x + 1, 0.11, selectedObject.position.z);
surroundingCube.rotation.x = Math.PI / 2;
group.add(surroundingCube);
}
scene.add( group );
}
You can try grouping the meshes using the THREE.group class.
A very basic usage would be something like:
var meshes = new THREE.Group();
var mesh1 = new THREE.Mesh( geometry, material );
var mesh2 = new THREE.Mesh( geometry, material );
var mesh3 = new THREE.Mesh( geometry, material );
meshes.add( mesh1 );
meshes.add( mesh2 );
meshes.add( mesh3 );
scene.add( meshes );
getObjectByName just calls getObjectByProperty (using the name property) which only returns the first object it finds.
You really just need to loop over children of the scene / object3d, check their name, and remove.
If you know all the ones you want to remove will be at the top level it is simple.
Something like....(untested)
for ( var i = 0, l = scene.children.length; i < l; i ++ ) {
if (scene.children[i].name === 'blueTiles') {
scene.remove(scene.children[i]);
}
}
If you also want to check at lower levels, you probably want some sort of recursion.
EDIT...
After having another look at your question..and remembering that there is a traverse function
You seem to already be traversing the scene and have access to the child.
I think you just need to change your function (and not call getObjectByName at all).
scene.traverse(function(child) {
if (child.name === "blueTiles") {
scene.remove(child);
}
});
I try several times to implement the edges in a model loader with the OBJLoader but can't do it.
Mloader = new THREE.MTLLoader();
Mloader.setPath( dir );
Mloader.load( mtl_dir, function ( materials ) {
materials.preload();
OLoader = new THREE.OBJLoader();
OLoader.setMaterials( materials );
OLoader.setPath( dir );
OLoader.load( name_file, function ( object ) {
object.scale.set( scale, scale, scale );
scene.add( object );
var edges = new THREE.EdgesGeometry( object, 11 );
var line = new THREE.LineSegments( edges, new THREE.LineBasicMaterial( {color: 0x111111 } ) );
line.scale.set( scale, scale, scale );
scene.add( line )
} );
} );
The model load fine, but the edges don't.
When the model is loader with the STLloader the edges of the geometry render fine, but i need to do it with .obs files.
var loader = new THREE.STLLoader();
loader.load(dir, function (geometry) {
material = new THREE.MeshPhongMaterial({
color: 0xAAAAAA,
specular: 0x111111,
shininess: 200
});
var edges = new THREE.EdgesGeometry(geometry, 11);
var line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({color: 0x111111}));
line.scale.set(scale, scale, scale);
scene.add(line)
});
STL vs OBJ Loader
Thanks, yes it a group. The code with the solution:
Mloader = new THREE.MTLLoader();
Mloader.setPath( dir );
Mloader.load( mtl_dir, function ( materials ) {
materials.preload();
OLoader = new THREE.OBJLoader();
OLoader.setMaterials( materials );
OLoader.setPath( dir );
OLoader.load( name_file, function ( object ) {
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.castShadow = true;
edges = new THREE.EdgesGeometry( child.geometry,11);
line = new THREE.LineSegments( edges, new THREE.LineBasicMaterial( {
color: 0x111111
} ) );
line.scale.set( scale, scale, scale );
line.position.set( 0, 0.7, 0 );
scene.add( line );
}
} );
object.scale.set( scale, scale, scale );
object.position.set( 0, 0.7, 0 );
scene.add( object );
} );
} );
attempting to apply a bump map to an object loaded, but no change is seen on the material
var loader = new THREE.ObjectLoader();
loader.load("../js/brain2.json", function(object) {
var mapHeight = new THREE.TextureLoader().load( "../js/texture.jpg" );
var material = new THREE.MeshPhongMaterial( { color: 0x888888, specular: 0xEEEEEE, shininess: 5} );
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.material = material;
child.material.bumpMap = mapHeight;
child.material.bumpScale = 1;
var geometry = new THREE.Geometry().fromBufferGeometry( child.geometry );
geometry.computeFaceNormals();
geometry.mergeVertices();
geometry.computeVertexNormals();
child.geometry = new THREE.BufferGeometry().fromGeometry( geometry );
var modifier = new THREE.BufferSubdivisionModifier(1);
modifier.modify(geometry);
child.material.overdraw = 1
};
});
object.scale.set(15, 15, 15);
object.position.x = 1;
object.position.y = 1;
object.position.z = 1;
object.rotation.set( 5, 1, 1 );
scene.add( object );
});
I've already fixed earlier issues with this code, including applying a subdivision modifier to it, but now it appears the bump map refuses to be applied.
I would like to change the material of a objects based on a radio button selection. A color change works in my current example. Unfortunately it does not update the whole material.
How do I tell THREE to update its internal data accordingly?
Materials
var lederfaserMap = THREE.ImageUtils.loadTexture( 'textures/lederfaser.jpg' );
lederfaserMap.anisotropy = 16;
lederfaserMap.wrapS = lederfaserMap.wrapT = THREE.RepeatWrapping;
lederfaserMap.repeat.set( 5, 5 );
var feinleinenMap = THREE.ImageUtils.loadTexture( 'textures/feinleinen.jpg' );
feinleinenMap.anisotropy = 16;
feinleinenMap.wrapS = feinleinenMap.wrapT = THREE.RepeatWrapping;
feinleinenMap.repeat.set( 8, 8 );
var material = {
"feinleinen-schwarz": new THREE.MeshPhongMaterial( { color: 0x222222, map: feinleinenMap, combine: THREE.MixOperation} ),
"feinleinen-weiss": new THREE.MeshPhongMaterial( { color: 0xffffff, map: feinleinenMap, combine: THREE.MixOperation } ),
"lederfaser-schwarz": new THREE.MeshPhongMaterial( {color: 0x222222, map: lederfaserMap, combine: THREE.MixOperation } ),
}
Update the Material here
var group = new THREE.Group();
loader.load('/models/object.js', function(geometry){
var materialchange= new THREE.Mesh(geometry, material[ "feinleinen-schwarz" ]);
materialchange.name = "materialchange";
group.add( materialchange);
});
scene.add( group );
function animate() {
requestAnimationFrame(animate);
TWEEN.update();
renderer.render(scene, camera);
}
Function to get and update the material
function colorTo (target, value){
var target = scene.getObjectByName(target);
var initial = new THREE.Color(target.material.color.getHex());
var value = new THREE.Color(value.color.getHex());
var tween = new TWEEN.Tween(initial).to(value, 500)
.easing(TWEEN.Easing.Cubic.InOut)
.onUpdate(function(){
target.material.color = initial;
})
.start();
}
Function to get the attribute of the checked radio button
$("input[name=material]").change(function(event){
if ($(this.checked)) {
var targetColor = $(this).data("visual");
colorTo("materialchange", material[targetColor]);
}
});
What you need to do is to traverse the object to assign the new color to all of its sub-parts:
object.traverse ( function (child) {
if (child instanceof THREE.Mesh)
child.material.color.copy = new_color;
}