I am trying to change the material of a Collada model exported from Sketchup:
I have used:
dae.traverse(function(child) {
if (child.material!==undefined) {
if (child.material.materials===undefined) {
child.material.map = THREE.ImageUtils.loadTexture( "textures/monster.jpg", null, function( child ) {
child.geometry.buffersNeedUpdate;
child.geometry.uvsNeedUpdate;
});
} else {
for (var j=0;j<child.material.materials.length;j++)
child.material.materials[j].map = THREE.ImageUtils.loadTexture( "textures/monster.jpg", null, function( child ) {
child.material.materials[j].needsUpdate;
child.geometry.buffersNeedUpdate;
child.geometry.uvsNeedUpdate;
});
};
};
};
});
However when I try to change the texture map, or even the entire texture, my geometry disappears completely.
I have seen this, but it was not helpful. Three.js Map material showing warnings despite updating buffers
Does anyone know what I am doing wrong? Thanks!
UPDATE:
Changing my material from MeshLambertMaterial to ShaderMaterial makes the geometry appear, but it is red. (It should be a porcelain white). Should I not be using MeshLambertMaterial for a complex Collada model?
Related
I'm using an image as a texture several times, once via the material component, another time in a custom component. In the latter I'm using THREE.TextureLoader() which cause the application to load the image twice. I bet there's another way.
Current situation
HTML
<!-- Assets -->
<a-assets>
<img id="my-map" src="path/to/map.jpg">
<a-asset-item id="my-model" src=path/to/model.gltf""></a-asset-item>
</a-assets>
<!-- Entities -->
<a-box material="src: #my-map">
<a-entity gltf-model="src: #my-model" custom-component="mymap: #my-map">
JS
// custom-component extract
schema: { mymap: { type: 'map' } }
init: function()
{
let mesh = this.el.getObject3D('mesh')
mesh.traverse( function( child )
{
if ( child.isMesh )
{
let TextureLoader = new THREE.TextureLoader()
let mymap = TextureLoader.load( data.mymap )
child.material = new THREE.MeshPhongMaterial({ map: mymap })
child.material.needsUpdate = true;
}
})
}
Question
How can I use the same image asset in the custom component without loading it twice?
You're using mesh.traverse() which calls your function once per child of mesh. If you have two children in your mesh, it'll call TextureLoader.load() twice. Simply take the load call out of traverse(), and you should see the image loaded just once.
let mesh = this.el.getObject3D('mesh')
let TextureLoader = new THREE.TextureLoader()
let mymap = TextureLoader.load( data.mymap )
mesh.traverse( function( child )
{
if ( child.isMesh )
{
child.material = new THREE.MeshPhongMaterial({ map: mymap })
child.material.needsUpdate = true;
}
})
After some research digging in ThreeJs code I've found that TextureLoader relies on ImageLoader (see here) which, before loading a new image, it looks into THREE.Cache to see if the image has already been loaded (see here).
Loading images as img in AFrame doesn't store them in THREE.Cache therefore the image was loaded twice. But if you load the image as a-asset-item it does.
So there is no need to change anything in javascript. Just use a-asset-item instead of img.
For more info see AFrame documentation
I am using Three.js Mirror class to reflect a ring mesh in my scene and the reflection has strange artifacts in it.
This is the relevant code:
verticalMirror = new THREE.Mirror( renderer, camera, { clipBias: 0.003, textureWidth: 512, textureHeight: 512, color:0xdddddd} );
var verticalMirrorMesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 300, 300 ), verticalMirror.material );
verticalMirrorMesh.add( verticalMirror );
verticalMirrorMesh.position.y = 0;
verticalMirrorMesh.position.z = -10;
editor.scene.add( verticalMirrorMesh );
function render () {
renderer.clear();
verticalMirror.render();
renderer.render(scene, camera);
}
In the center the reflection looks more or less ok, but on the sides it's stretched to the edges of the mirror. Please see the images below.
I've researched other posts on mirroring in Three.js and seem to follow what they are suggesting. What am I still doing wrong? Any advice is appreciated.
Thank you,
Anton.
I am new to three.js, pls guide me how to change the image dynamically
Tried with the below option of replacing the image from the original image
I tried with the below code
var mtlLoader = new THREE.MTLLoader();
mtlLoader.load( 't2.mtl', function( materials ) {
materials.preload();
var texture = THREE.TextureLoader('bbc.png');
var material = new THREE.MeshLambertMaterial( { map: texture } );
materials.setMaterials( material );
objLoader.load( 't2.obj', function ( object ) {
scene.add( object );
}, onProgress, onError );
});
I am not getting any error but updated image is not displayed ? but the actual object is displayed in blank color. Pls suggest
Thanks
Shaik
I have a grid that contains boxes, very similar to http://threejs.org/examples/#canvas_interactive_voxelpainter. Now I initiated a hover state when a box on the scene is mouseover it turns the background gray. Which is great! Except when I multiple "box" on the grid and I go to change the material background color of the hovered item, it renders all of the "box's" with a gray background.
Heres what I am doing:
var voxel = new THREE.Mesh( this.cubeGeometry, this.cubeMaterial );
voxel.position.copy( intersect.point ).add( intersect.face.normal );
voxel.position.divideScalar( 50 ).floor().multiplyScalar( 50 ).addScalar( 25 );
this.scene.add( voxel );
this.blocks.push( voxel );
var domEvents = new THREEx.DomEvents(this.camera, this.renderer.domElement)
// DOM events for inside 3d rendering
domEvents.addEventListener(voxel, 'mouseover', this.onDocumentMouseOverCube.bind(this),false);
domEvents.addEventListener(voxel, 'mouseout', this.onDocumentMouseOutCube.bind(this),false);
Here we create our box - we than give it eventListeners for that specific mesh. Once this mesh is hovered over, our mouseover is executed:
this.mouse.x = ( event.origDomEvent.clientX / this.renderer.domElement.width ) * 2 - 1;
this.mouse.y = - ( event.origDomEvent.clientY / this.renderer.domElement.height ) * 2 + 1;
this.raycaster.setFromCamera( this.mouse, this.camera );
var intersects = this.raycaster.intersectObjects( this.blocks );
if ( intersects.length > 0 ) {
var intersect = intersects[ 0 ];
if ( intersect.object != this.plane ) {
console.log(intersect.object);
// update color on hover
intersect.object.material.color.setHex(this.colorHover);
console.log("hover color");
this.render();
}
}
Now this works great, the only issue is - this.render() is called (this.renderer.render( this.scene, this.camera )) like it should be. But when I have multiple box's on it goes ahead and changes every single background color of each box I have even logged all my objects to confirm object.material.color is the gray hex for only one box and that not all of the box's are being set, which proves to be true. I am sending the correct data over. So I am assuming it has to do with the rendering of the actual engine?
Suggestions?
There is only one instance of the material, which is shared among the meshes. The easy solution is to clone the material for each mesh:
var voxel = new THREE.Mesh( this.cubeGeometry, this.cubeMaterial.clone() );
Now every box accepts its own color.
I dont know if this still applies but thinking about performance you would want to go with a custom shader material, because the attributes and vertex/fragment programs are copied by reference then. See this post Three.js, sharing ShaderMaterial between meshes but with different uniform sets.
Example code:
var phongShader = THREE.ShaderLib.phong;
this.shaderMaterial = new THREE.ShaderMaterial({
uniforms: phongShader.uniforms,
vertexShader: phongShader.vertexShader,
fragmentShader: phongShader.fragmentShader,
lights:true
});
var voxel = new THREE.Mesh( this.cubeGeometry, this.shaderMaterial.clone() );
And then you change the color via uniforms like so:
intersect.object.material.uniforms.diffuse.value.setHex( this.colorHover );
Three.js r.71
PS: cloning the default Phong material in r.71 also shows only one programm in the renderer info for me, so maybe Three.js is optimizing this internally.
I have some .js files exported from Blender and load them with THREE.JSONLoader();
my callback:
var callback = function( geometry ) { createMesh(geometry);
my loading:
loader.load( "Models/sculp.js", callback );
my create method:
function createMesh(geometry){
inArr[id] = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: 0xbbbbbb} ) );
inArr[id].scale.set( 100, 100, 100 );
scene.add( inArr[id] );
id++;
}
Now I want to change my material on runtime by using my keyboard (changes color and opacity).
How can I do that?
As you create a new material for each mesh I assume you only want to change the color of one mesh and not of all in the inArr array, and you probably need some sort of select for that. But changing the color of the material alone is quite easy:
var onKeyDown = function(event) {
if (event.keyCode == 67) { // when 'c' is pressed
object.material.color.setHex(0xff0000); // there is also setHSV and setRGB
}
};
document.addEventListener('keydown', onKeyDown, false);
object is the mesh you want to change. Key codes can be found here: http://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes