Basically i want to change ambient and specular value of a collada object on the fly, while keeping the original texture. So i would like to have something like this:
var changeMaterial = function(node, ambient, specular) {
if (node.material != null) {
var material = new THREE.MeshPhongMaterial();
material.clone(node.material);
material.ambient = ambient;
material.specular = specular;
node.material = material;
}
if (node.children) {
for (var i = 0; i < node.children.length; i++) {
setMaterial(node.children[i]);
}
}
}
After i tried this, ambient and specular seem to be changed but the texture is lost. What did i do wrong? Is there a methode to get the map so like:
material.map = node.material.map;
Related
I've got a textured model in three.js and I want to be able to swap out the texture defined in the .gltf file when the page loads. I've looked here for inspiration.
"images": [
{
"uri": "flat_baseColor.png"
},
// etc
So to update the texture I do
var images = [
"./textures/01.jpg",
// "./textures/01.jpg",
];
var texture = new THREE.TextureLoader().load( images[0] );
var my_material = new THREE.MeshBasicMaterial({map: texture});
// load the model
var loader = new GLTFLoader().setPath( 'models/gltf/' ); // trex
loader.load( 'creature_posed.gltf', function ( gltf )
{
gltf.scene.traverse( function ( child )
{
if ( child.isMesh )
{
// The textures go for a double flip, I have no idea why
// Texture compensation
texture.flipX = false;
texture.flipY = false;
child.material = my_material;
texture.needsUpdate = true;
}
} );
var model = gltf.scene;
Only the texture is considerably pale. :(
I've tested it against itself, so it's not the texture). What have a missed out?
When loading textures you'll need to pay attention to colorspace: if the texture has color data (like .map or .emissiveMap) it's probably sRGB.
texture.encoding = THREE.sRGBEncoding;
See GLTFLoader docs and color management in three.js. This assumes that renderer.outputEncoding = THREE.sRGBEncoding as well.
three.js r113
I am playing with Three.js. I am working on a project that uses an old version of Three.js. I'm trying to replace the old library with the new one (r71). Everything is going well except the texture mapping.
In the old project we use the Face4 which is deprecated and have been removed in the newest version of Three.js. So I have created two Face3 instead of just one Face4. Now the question is: how can I make one texture to cover both Face3 as they were like a single Face4?
Old pseudo-code using Face4
numberOfFaces = 10;
function createMyGeometry(){
var geometry = new THREE.Geometry();
...
for (var i = 0; i < numberOfFaces; i++) {
var face = new THREE.Face4(v1.index, v2.index, v3.index, v4.index, [v1.clone(), v2.clone(), v3.clone(), v4.clone()]);
face.centroid.add(v1).add(v2).add(v3).add(v4).divideScalar(4);
face.normal = face.centroid.clone().normalize();
face.materialIndex = matIdx;
geometry.faces.push(face);
geometry.faceVertexUvs[0].push([uv1.clone(), uv2.clone(), uv3.clone(), uv4.clone()]);
}
...
return geometry;
}
for (var i = 0; i < numberOfFaces; i++) {
var texture = THREE.ImageUtils.loadTexture("MyImage"+ i + '.jpg');
var material = new THREE.MeshBasicMaterial();
material.map = texture;
material.side = THREE.FrontSide;
materials.push(material);
}
materials = new THREE.MeshFaceMaterial(materials);
myGeometry = new THREE.Mesh(createMyGeometry(), materials);
scene.add(myGeometry);
Pseudo-code using Face3
numberOfFaces = 10;
function createMyGeometry(){
var geometry = new THREE.Geometry();
...
for (var i = 0; i < numberOfFaces; i++) {
var face1 = new THREE.Face3(v1.index, v2.index, v3.index, [v1.clone(), v2.clone(), v3.clone()]);
var face2 = new THREE.Face3(v1.index, v3.index, v4.index, [v1.clone(), v3.clone(), v4.clone()]);
face1.materialIndex = matIdx;
face2.materialIndex = matIdx;
geometry.faces.push(face1);
geometry.faces.push(face2);
geometry.faceVertexUvs[0].push([uv1.clone(), uv2.clone(), uv3.clone(), uv4.clone()]);
}
...
return geometry;
}
for (var i = 0; i < numberOfFaces; i++) {
var texture = THREE.ImageUtils.loadTexture("MyImage"+ i + '.jpg');
var material = new THREE.MeshBasicMaterial();
material.map = texture;
material.side = THREE.FrontSide;
materials.push(material);
}
materials = new THREE.MeshFaceMaterial(materials);
myGeometry = new THREE.Mesh(createMyGeometry(), materials);
scene.add(myGeometry);
You should just do the same thing with vertex uvs as with vertices.
For example:
geometry.faceVertexUvs[0].push([uv1.clone(), uv2.clone(), uv3.clone()]);
geometry.faceVertexUvs[0].push([uv1.clone(), uv3.clone(), uv4.clone()]);
I'm wondering if it is possible to attach a text above a 3D Object?
If so, how would I do it?
So far I'm doing the following below to load a mesh with its material and lastly adding it to a THREE.Object3D(); and adding it to the scene. Works great without any problems.
Next step is I want to show a nice text above its this object that is always fixed and can be seen from every angle.
loader.load('assets/' + enemyUrl, function (geometry, materials) {
material = new THREE.MeshFaceMaterial( materials );
model = new THREE.SkinnedMesh( geometry, material );
var mats = model.material.materials;
for (var i = 0,length = mats.length; i < length; i++) {
var m = mats[i];
m.skinning = true;
}
ensureLoop(geometry.animations[0]);
function ensureLoop( tmp ) {
for ( var i = 0; i < tmp.hierarchy.length; i ++ ) {
var bone = tmp.hierarchy[ i ];
var first = bone.keys[ 0 ];
var last = bone.keys[ bone.keys.length - 1 ];
last.pos = first.pos;
last.rot = first.rot;
last.scl = first.scl;
}
}
model.scale.set(2.5,2.5,2.5);
// TODO: Randomize where to put it in the world
yawObject.position.y = spawnPosition.y;
yawObject.position.x = spawnPosition.x;
yawObject.position.z = spawnPosition.z;
yawObject.add(model);
scene.add(yawObject);
});
Something like this:
This is what my game looks like now:
sure its possible. you can create a canvas with text on it, which you use as a texture on a plane that always looks at the camera, you could also do it with a single particle, but im not quite sure how it would work since particle materials have a size parameter. something like:
var name = 'Rovdjuret';
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
ctx.font="20px Georgia";
ctx.fillText(name,10,50);
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true; //just to make sure it's all up to date.
var label = new THREE.Mesh(new THREE.PlaneGeometry, new THREE.MeshBasicMaterial({map:texture}));
and inside the render/animation loop you make all the labels look at the camera:
label.lookAt(camera.position);
I'm working with three.js and I have a plane shape. This shape should get a texture. Below you can see how I try this. The problem is, that the texture is only shown in two stripes at two edges of the shape. If I take higher values for texture.repeat this stripes get thinner.
I know there is a related topic here Three.JS Is it possible to render a texture on shapeGeometry?, but I can't understand how that could help me.
//Draw Floorshape
var points_floor = [];
points_floor.push(new THREE.Vector2(edges[edges.length-1][edges[3].length-1][0], edges[edges.length-1][edges[3].length-1][1]));
for (var i = 0; i < edges.length; i++) {
for (var j = 0; j < edges[i].length; j++) {
points_floor.push(new THREE.Vector2(edges[i][j][0], edges[i][j][1]));
}
}
var shape_floor = new THREE.Shape(points_floor);
var floorGeometry = new THREE.ShapeGeometry(shape_floor);
var texture = new THREE.ImageUtils.loadTexture("./img/tileable_wood_texture_by_ftIsis_Stock.jpg");
texture.repeat.set(0.1, 0.1);
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
var materialFloor = new THREE.MeshBasicMaterial({
map : texture,
side : THREE.DoubleSide
});
var floor = new THREE.Mesh(floorGeometry, materialFloor);
floor.overdraw = true;
I'm trying to make a terrain editor. So I have a plane with multiple materials on it, which I initialize like so:
var materials = [];
var grasstexture = THREE.ImageUtils.loadTexture('Img/grass.png', {}, function() { });
var rocktexture = THREE.ImageUtils.loadTexture('Img/rock.png', {}, function() { });
materials.push(new THREE.MeshPhongMaterial({color: 0xffffff, map: grasstexture}));
materials.push(new THREE.MeshPhongMaterial({color: 0xffffff, map: rocktexture}));
// Plane
this.geometry.materials = materials;
for(var i = 0; i < this.geometry.faces.length; i++)
{
this.geometry.faces[i].materialIndex = 0;
}
this.geometry.dynamic = true;
this.mesh = new THREE.Mesh( this.geometry, new THREE.MeshFaceMaterial( ) );
this.mesh.receiveShadow = true;
The result is a square plane stripped with the two textures.
Now, I can't seem to update the materialIndex for each vertex during the runtime. I have tried:
face.materialIndex = 1;
for(var i = 0; i < geo.materials.length; i++){
geo.materials[i].needsUpdate = true;
}
for(var i = 0; i < this.geometry.faces.length; i++)
{
geo.faces[i].materialIndex = 0;
}
for(var i = 0; i < geo.materials.length; i++){
geo.materials[i].needsUpdate = true;
}
this.mesh.needsUpdate = true;
this.mesh.material.needsUpdate = true;
geo.verticesNeedUpdate = true;
geo.normalsNeedUpdate = true;
geo.colorsNeedUpdate = true;
geo.computeFaceNormals();
geo.computeVertexNormals();
geo.computeCentroids();
geo.computeTangents() ;
And every other 'materialIndex' and 'needsUpdate' variable in Three.js I was able to find, but still nothing happens. How do I force an update on the material indices?
You can't. materialIndex is only used in the first render to partition the geometry into chunks, with each chunk having the same material. Also, you cannot re-partition the chunks.
What you can do, however, is change a material in the materials array.
materials[ 0 ] = new THREE.MeshBasicMaterial();
You do not need to set any needsUpdate flags to true after doing so, either.
An alternate thing you can do is give each face it's own material from the start, and then, for example, just change a material's texture.
mesh.geometry.materials[ 0 ].map = texture;
texture.needsUpdate = true;