three.js - cloning textures and onLoad - javascript

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.

Related

Is there a way to add different textures to an object with TextureLoader.load

I would like to add different textures to each face of a box but I am not sure if loader.load is the way to do it, right now I have:
loader.load('img/brick.jpg', function ( texture ){
var boxGeometry = new THREE.BoxGeometry( 3, 3, 3 );
var boxMaterial = new THREE.MeshLambertMaterial({
map: texture,
overdraw: 10
});
var box = new THREE.Mesh( boxGeometry, boxMaterial );
box.castShadow = true;
scene.add(box);
}
Is it possible to add more images in the loader.load or do I have to use a different method?
You can just load an image with loader.load, and store it in a variable:
var loader = new THREE.TextureLoader();
var brick = loader.load('img/brick.jpg');
var occlusion = loader.load('img/ao.jpg'); //Example texture
//More textures here
You can then apply it like so:
var boxGeometry = new THREE.BoxGeometry( 3, 3, 3 );
var boxMaterial = new THREE.MeshLambertMaterial({
map: brick,
aoMap: occlusion, //An example use
overdraw: 10
});
var box = new THREE.Mesh( boxGeometry, boxMaterial );
box.castShadow = true;
scene.add(box);
Instead of loading the texture and using an anonymous callback, just load the texture, store it in a variable, then apply where needed.

Three.js CSG How to change result's material and color?

I'd like to get MeshLambertMaterial on this particular object 'result' which I get after making union of two meshes:
var lathe = new THREE.Mesh(latheGeometry);
var cube_bsp = new ThreeBSP( lathe );
var box = new THREE.BoxGeometry( 2,30,3);
var sub = new THREE.Mesh( box );
sub.position = new THREE.Vector3(0,0,19);
var substract_bsp = new ThreeBSP( sub );
var subtract_bsp = cube_bsp.union( substract_bsp );
var result = subtract_bsp.toMesh();
result.rotation.x = Math.PI * -0.5;
scene.add(result);
Here I have a box and a latheGeometry. After union is done I get random plain color ugly object. Instead I should get LambertMaterial white color object.
Images: http://imgur.com/a/nbSq1
You can apply the material when calling ThreeBSP.toMesh():
subtract_bsp.toMesh( new THREE.MeshLambertMaterial( {color:0xFFFFFF} ) );
or after the creation on the resulting mesh:
result.material = new THREE.MeshLambertMaterial( {color:0xFFFFFF} ) );

Rotation around object using Three.js hierarchy (THREE.Group())

So I have the following javascript code:
var group = new THREE.Group();
var staffGeometry = new THREE.BoxGeometry(0.5, 6, 0.5);
var staffMaterial = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
var staffCube = new THREE.Mesh( staffGeometry, staffMaterial );
staffCube.position.x = -21;
staffCube.position.y = 1;
staffCube.position.z = -19;
group.add(staffCube);
//scene.add( staffCube );
var geometry = new THREE.TorusKnotGeometry( 10, 3, 100, 16 );
var material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
var torusKnot = new THREE.Mesh( geometry, material );
torusKnot.position.x = -21;
torusKnot.position.y = 4.8;
torusKnot.position.z = -19;
torusKnot.scale.set(0.08, 0.08, 0.08);
group.add(torusKnot);
//scene.add( torusKnot );
var ballGeometry1 = new THREE.IcosahedronGeometry(0.5);
var ballMaterial1 = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
var ball1 = new THREE.Mesh(ballGeometry1, ballMaterial1);
ball1.position.x = -22.5;
ball1.position.y = 4.5;
ball1.position.z = -20.5;
group.add(ball1);
//scene.add(ball1);
var ballGeometry2 = new THREE.IcosahedronGeometry(0.5);
var ballMaterial2 = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
var ball2 = new THREE.Mesh(ballGeometry2, ballMaterial2);
ball2.position.x = -22;
ball2.position.y = 6.5;
ball2.position.z = -20;
group.add(ball2);
//scene.add(ball2);
scene.add(group);
The THREE.Group() was an attempt to utilize hierarchy. I can't seem to find ANY useful examples or documentation on THREE.Group so not even sure if I'm using hierarchy correctly. My goal was to set the staff in place, and make it easier to do stuff with the other components through hierarchy.
Mainly, I want ball1 and ball2 to circle around the staff. I am not sure how to access them within the group so that they can use taurusKnots location (head of staff) to translate to, rotate on taurusKnots Y, then translate back.
Any help on utilizing hierarchy with three.js to achieve this would be awesome! Like I said, all I can find online are people saying to use Group() for hierarchy, but nothing is said or can be found (by me at least) on how to access members and actually utilize the hierarchy you establish.

Threejs TextureLoader CubeGeometry

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.

threecsg.js and subdivision modifier distorted geometry

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?

Categories

Resources