I am learning threejs and I want that my cube has 6 different textures on each side. I did make that with loadTexture
var material3 = new THREE.MeshPhongMaterial( {map: THREE.ImageUtils.loadTexture('textures/ps.png')} );
I did save 6 of this materials in array and then use THREE.MeshFaceMaterial. But there is problem with THREE.ImageUtils.loadTexture because it is deprecated and I should use THREE.TextureLoader and I do not know how to load 6 textures in this way.
This is what I have:
function texture()
{
var loader = new THREE.TextureLoader();
loader.load( 'textures/ps.png', function ( texture )
{
var geometry = new THREE.CubeGeometry( 10, 10, 10 );
var material = new THREE.MeshBasicMaterial( { map: texture, overdraw: 0.5 } );
mesh = new THREE.Mesh( geometry, material );
mesh.position.z = -50;
scene.add( mesh );
} );
}
I think this is close to what you are looking for:
function MultiLoader(TexturesToLoad, LastCall, ReturnObjects) {
if (TexturesToLoad.length == 0) return;
if (!ReturnObjects) ReturnObjects = [];
var loader = new THREE.TextureLoader()
//Pop off the latest in the ,
//you could use shift instead if you want to read the array from
var texture = TexturesToLoad.shift()
loader.load(texture,
function (texture) {
ReturnObjects.push(texture);
if (TexturesToLoad.length > 0) {
MultiLoader(TexturesToLoad, LastCall, ReturnObjects)
} else {
LastCall(ReturnObjects)
}
},
LoadProgress,
LoadError);
}
function LoadProgress(xhr) {
console.log(('Lodaing ' + xhr.loaded / xhr.total * 100) + '% loaded ');
}
function LoadError(xhr) {
console.log('An error happened ');
}
call it with this
var TexturesToLoad = []
TexturesToLoad.push("../surfacemap.jpg")
TexturesToLoad.push("../normalmap.jpg");
TexturesToLoad.push("../spekularmap.jpg");
var ReturnedMaterials=[];
var ReturnMaterials=[];
var LastCall=function(ReturnedMaterials)
{
var surfaceMap = ReturnedMaterials[0];
var normalMap = ReturnedMaterials[1];
var specularMap = ReturnedMaterials[2];
var decalMaterial = new THREE.MeshPhongMaterial(
{
map: surfaceMap,
normalMap: normalMap,
normalScale: new THREE.Vector2( 1, 1 ),
specularMap: specularMap,
transparent:false,
wireframe: false
} );
var globeGeometry = new THREE.SphereGeometry(100.0, SPHERE_SIDES, SPHERE_SIDES);
mesh = new THREE.Mesh( globeGeometry, decalMaterial );
mesh.rotation.x=Math.PI/2;
};
MultiLoader(TexturesToLoad,LastCall,ReturnMaterials)
Explanation:
The new THREE.TextureLoader, uses a callback function. This ensures that the ressource you are using is really loaded when you need to add it.
Callbacks are difficult however if you want to use a lot of material.
above MultiLoader allows you to call recursivly and then call back to the function you want to use all your materials at. The materials are collected into an array (ReturnObjects) .
There are many ways to achieve it. I'll show you 2;
1)
Make your object (cube) using own vertices and faces
.vertices.push( new THREE.Vector3( x, y, z ) );
.faces.push( new THREE.Face3( x, y, z ) );
I've prepared an example in jsfiddle.
2)
Using UV map. So first you need to prepare object with UV map in 3D software like Blender and export it as a JSON file.
I've also prepared an example in jsfiddle.
If you are not familiar with UV map or Blender, check toturials.
Related
I have an object in which I want to store the geometry contained on a Ply file, for which I have the following:
function MyObject(filename) {
var container = this.createObject(filename);
this.mesh = container.children[0];
this.geometry = this.mesh.geometry;
};
MyObject.prototype.createObject = function(filename) {
var loader = THREE.PLYLoader();
var container = new THREE.Object3D();
loader.load(filename, function ( geometry ) {
var material = new THREE.MeshStandardMaterial( { color: 0x0055ff } );
var mesh = new THREE.Mesh( geometry, material );
mesh.position.y = -0.25;
mesh.rotation.x = -Math.PI / 2;
mesh.scale.multiplyScalar( 0.04 );
container.add( mesh );
});
return container;
};
But the loader is run asynchronously and therefore when creating this.mesh it becomes "undefined" making the program fail at this.geometry.
I thought the return function would fix this but it does not and after reading about asynchronicity in javascript I haven't found the solution. Does anyone know how to implement three.js loaders with objects?
I'm currently working on my little project using three.js, but I am having hard time mapping texture on objects loaded by THREE.OBJLoader. There is no such problem for three.js built in geometry. I'm really confused now...
// Load the model from obj file [teapot, 74KB]
var onProgress = function ( xhr ) {
};
var onError = function ( xhr ) {
};
//var oCubeMap = THREE.ImageUtils.loadTexture(imageNames);
var objTexture = new THREE.ImageUtils.loadTexture(textureImage);
var oMaterial = new THREE.MeshLambertMaterial( { map: objTexture } );
var ooMaterial = new THREE.MeshPhongMaterial( { color: 0x99CCFF } );
var thisTexture = THREE.ImageUtils.loadTexture( textureImage, {}, function() {
renderer.render(scene, camera);
} );
var loader = new THREE.OBJLoader();
loader.load(teapot, function (object) {
object.position.set(8, -5, -25);
object.scale.set(0.04, 0.04, 0.04);
object.rotation.set(0, 180 * Math.PI / 180, 0);
// object.color = '0x99CCFF';
object.material = ooMaterial;
// object.texture = thisTexture;
scene.add(object);
}, onProgress, onError);
As you can see I've tried several things like color, material and texture but nothing worked so far. Can anyone tell me how to apply a texture onto this object?
OBJLoader returns an object with children. Add the following to your loader callback function:
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.material.map = texture;
}
} );
Make sure your model geometry has UVs, otherwise textures are not supported.
three.js r.73
just getting my hands dirty with three.js and im curious on something that may be relatively simpleā¦
I built a scene in the three.js editor and have figured out how to load the scene. In the editor, I added an image as a map texture but I realize it wont know where it is loaded externally on my server. So I've loaded the scene and objects and lights, but how can I map my textures to the objects?
// MATERIALS
var wireframe = THREE.ImageUtils.loadTexture( 'textures/wireframe.jpg' );
wireframe.wrapS = wireframe.wrapT = THREE.RepeatWrapping;
wireframe.repeat.set( 4, 4 );
var wireframeMaterial = new THREE.MeshLambertMaterial({
map : wireframe,
side : THREE.DoubleSide
});
// LOAD SCENE
var loader = new THREE.ObjectLoader();
loader.load( 'js/scene.js', function ( obj ) {
obj.traverse( function( node ) {
if ( node instanceof THREE.Mesh ) {
node.castShadow = true;
node.receiveShadow = true;
var plane = scene.getObjectByName( "plane", true );
plane.material = wireframeMaterial;
}
});
scene.add( obj );
render();
});
When adding plane.material = wireframeMaterial; into the loader, all my objects just disappearā¦ How can I properly map the wireframeMaterial the plane object?
working example with var plane and plane.material commented out:
http://goo.gl/czSg7P
Scene:
http://goo.gl/BAVgVS
To map the texture you have to create a material using your texture and apply it to your plane.
If necessary you can set repetition of your texture with:
wireframe.wrapS = THREE.RepeatWrapping;
wireframe.wrapT = THREE.RepeatWrapping;
wireframe.repeat.set( 4, 4 );
And then you need to do something like this:
material = new THREE.MeshLambertMaterial({ map : wireframe, side : THREE.DoubleSide });
plane.material = material;
EDIT
You create a scene inside a scene. Since the loader returns a scene and then you add it again to a scene. This will give issues for sure.
Try to replace scene.add( obj ); with this in your loader:
for( var i = 0; i < obj.children.length; i++ ){
scene.add( obj.children[i] );
}
Not sure if that causes the problem, but I made a fiddle and it all seems to work fine for me. The problem is that I can't load external sources in my fiddle so instead I had to use your image as base64 string and the code becomes a bit different.
This means you have to change it a bit to make it work again with an image from your local folder.
If you exchange the Whole //TEXTURE part of the code with the following it should work:
var texture = new THREE.ImageUtils.loadTexture( 'img/wireframe.jpg' );
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 1, 1 );
var textureMaterial = new THREE.MeshLambertMaterial({ map: texture, side : THREE.DoubleSide });
I have the latest threecsg.js library and my use of it has been okay, except when I try to use the subdivision modifier after a CSG operation. Here is example code, modified from the example.html file that comes with the library from github:
var start_time = (new Date()).getTime();
var cube_geometry = new THREE.CubeGeometry( 3, 3, 3 );
var cube_mesh = new THREE.Mesh( cube_geometry );
cube_mesh.position.x = -6;
var cube_bsp = new ThreeBSP( cube_mesh );
var sphere_geometry = new THREE.SphereGeometry( 1.8, 12, 12 );
var sphere_mesh = new THREE.Mesh( sphere_geometry );
sphere_mesh.position.x = -7;
sphere_mesh.position.y -= 0;
var sphere_bsp = new ThreeBSP( sphere_mesh );
var subtract_bsp = cube_bsp.union( sphere_bsp );
var result = subtract_bsp.toMesh( new THREE.MeshLambertMaterial({ shading: THREE.SmoothShading, map: THREE.ImageUtils.loadTexture('texture.png') }) );
result.geometry.computeVertexNormals();
var smooth = result.geometry.clone() ;
smooth.mergeVertices();
var modifier = new THREE.SubdivisionModifier(0.1);
modifier.modify( smooth );
var mesh = new THREE.Mesh( smooth, new THREE.MeshPhongMaterial( { wireframe:true, color: 0xffffff } ) );
mesh.geometry.computeFaceNormals();
scene.add( mesh );
The above code unites a sphere and a cube. After this, it runs the resulting geometry through the subdivision modifier. The final output that is added to the scene has faces that are protruding from the object, other than that, the object does look smooth. Can anyone please help in solving this issue, that is, removing the protruding faces?
My goal is to create a cube/box with a single texture but different repeat values for each of the sides. Working code is below:
var cubeMaker = function(w,h,d, tName)
{
var g = new THREE.CubeGeometry( 50*w, 50*h, 50*d );
var tx = THREE.ImageUtils.loadTexture( tName );
var ty = THREE.ImageUtils.loadTexture( tName );
var tz = THREE.ImageUtils.loadTexture( tName );
tx.wrapS = tx.wrapT = THREE.RepeatWrapping;
ty.wrapS = ty.wrapT = THREE.RepeatWrapping;
tz.wrapS = tz.wrapT = THREE.RepeatWrapping;
tx.repeat.set(d,h);
ty.repeat.set(w,d);
tz.repeat.set(w,h);
var mx = new THREE.MeshBasicMaterial( {map: tx} );
var my = new THREE.MeshBasicMaterial( {map: ty} );
var mz = new THREE.MeshBasicMaterial( {map: tz} );
var mArray = [mx,mx,my,my,mz,mz];
var m6 = new THREE.MeshFaceMaterial( mArray );
var cube = new THREE.Mesh(g, m6);
return cube;
}
However, it seems wasteful to load the texture three times. Earlier, I instead tried passing a texture as an argument to the function (instead of a string representing the filename), as follows:
var cubeMaker = function(w,h,d, texture)
{
...
var tx = texture.clone();
var ty = texture.clone();
var tz = texture.clone();
...
but then the textures didn't appear in the scene, only solid black images appeared in their place. My best guess is that the texture image hadn't finished loading before the clone methods were called, and perhaps some kind of null value was copied instead. Is there some way to use an onLoad method to wait long enough so that the clone function works as intended?
Note: I have tried the suggestion from Can't clone() Texture but it does not solve my issue.
Thanks for any assistance!
Load your texture once, and move the rest of your code into the loader callback function. You also have to set the needsUpdate flag to true when you clone your texture.
var tx = THREE.ImageUtils.loadTexture( tName, undefined, function() {
var ty = tx.clone();
ty.needsUpdate = true; // important!
var tz = tx.clone();
tz.needsUpdate = true; // important!
tx.wrapS = tx.wrapT = THREE.RepeatWrapping;
ty.wrapS = ty.wrapT = THREE.RepeatWrapping;
tz.wrapS = tz.wrapT = THREE.RepeatWrapping;
tx.repeat.set( 1, 1 );
ty.repeat.set( 2, 1 );
tz.repeat.set( 2, 2 );
var mx = new THREE.MeshBasicMaterial( { map: tx } );
var my = new THREE.MeshBasicMaterial( { map: ty } );
var mz = new THREE.MeshBasicMaterial( { map: tz } );
var mArray = [ mx, mx, my, my, mz, mz ];
var mesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial( mArray ) );
scene.add( mesh );
} );
Why don't you create then your texture outside of your function and just use this texture inside of your function, assigning it to special variable for each side? That way for sure you are going to load it just once.