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.
Related
I have a scene which contains multiple meshes, each of varying shapes and sizes.
I have looped through each Mesh and using geometry.merge() I have been able to create a new mesh from the geometries in the scene.
I want to mask the entire mesh with an alphaMask, however, each geometry has the material applied to it separately.
An example of this can be seen here - https://codepen.io/danlong/pen/KXOObr
function addObjects(scene) {
// merged geomoetry & material
var mergedGeometry = new THREE.Geometry();
var mergedMaterial = new THREE.MeshStandardMaterial({ color: "#444", transparent: true, side: THREE.DoubleSide, alphaTest: 0.5, opacity: 1, roughness: 1 });
// multiple meshes
var geometry = new THREE.IcosahedronGeometry(30, 5);
var material = new THREE.MeshStandardMaterial({ color: "#444" });
var geo1 = new THREE.IcosahedronGeometry(30, 5);
var mesh1 = new THREE.Mesh( geo1, material );
mesh1.position.x = 10;
mesh1.position.y = 10;
mesh1.position.z = 0;
var geo2 = new THREE.IcosahedronGeometry(30, 5);
var mesh2 = new THREE.Mesh( geo2, material );
mesh2.position.x = 20;
mesh2.position.y = 20;
mesh2.position.z = 0;
var geo3 = new THREE.IcosahedronGeometry(30, 5);
var mesh3 = new THREE.Mesh( geo3, material );
mesh3.position.x = 30;
mesh3.position.y = 30;
mesh3.position.z = 0;
// scene.add(mesh1, mesh2, mesh3);
mesh1.updateMatrix();
mergedGeometry.merge(mesh1.geometry, mesh1.matrix);
mesh2.updateMatrix();
mergedGeometry.merge(mesh2.geometry, mesh2.matrix);
mesh3.updateMatrix();
mergedGeometry.merge(mesh3.geometry, mesh3.matrix);
// alpha texture
var image = document.createElement('img');
var alphaMap = new THREE.Texture(image);
image.onload = function() {
alphaMap.needsUpdate = true;
};
image.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAGUlEQVQoU2NkYGD4z4AHMP7//x+/gmFhAgCXphP14bko/wAAAABJRU5ErkJggg==';
mergedMaterial.alphaMap = alphaMap;
mergedMaterial.alphaMap.magFilter = THREE.NearestFilter;
mergedMaterial.alphaMap.wrapT = THREE.RepeatWrapping;
mergedMaterial.alphaMap.repeat.y = 1;
// merged geometry with alpha mask
merge1 = new THREE.Mesh(mergedGeometry, mergedMaterial);
merge1.rotation.z = -Math.PI/4;
// merge geometry without alpha mask
var merge2 = new THREE.Mesh(mergedGeometry, material);
merge2.position.x = -100;
merge2.rotation.z = -Math.PI/4;
scene.add(merge1, merge2);
return mesh;
}
The mesh on the left is the merged geometries which I want to apply the alphaMask to. The mesh on the right is the outcome of this and instead of the map being applied to the mesh as a whole, each of the geometries has the map applied.
Is there a way to mask the entire mesh and not each geometry?
--
three.js r86
EDIT:
I've tried to apply a clipping plane to my mesh but it's not the effect I'm looking for. I want to be able to apply an alphaMask across the whole mesh and reveal it however I make my mask image. Something like this effect - https://codepen.io/supah/pen/zwJxdb
Is it something to do with the UV's being preserved from the original geometries? Do I need to change these in some way?
I think what you really want is an overlaid mask. This can be accomplished by rendering a single plane that has the alpha map applied, on top of the scene rendering. Using an orthographic camera, and controlling certain renderer settings, such as disabling automatic clearing of color.
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.
I'm trying to create multiple meshes, and then merge them into one (with Three.js r.71). Each mesh can have different materials on different faces. I need to get all the materials appear correctly on the resulting mesh.
I found a way to achieve the desired result, but my code looks pretty awful now. Are the developers of three.js really kept in mind this method?
I could not find a suitable example. Can anyone show a better way to do this?
var totalGeom = new THREE.Geometry();
var meshes = [getBlock(), getBlock()];
meshes[0].position.x = 1;
for (var i = 0; i < meshes.length; i++) {
var mesh = meshes[i];
totalGeom.mergeMesh(mesh);
for (var j = mesh.geometry.faces.length-1; j <= 0; j--) {
totalGeom.faces[j].materialIndex = mesh.geometry.faces[j].materialIndex;
}
}
var materials = meshes[0].material.materials.concat(meshes[1].material.materials);
var totalMesh = new THREE.Mesh(totalGeom, new THREE.MeshFaceMaterial(materials));
scene.add(totalMesh);
function getBlock() {
var geometry = new THREE.BoxGeometry(1, 1, 1, 1, 1, 1);
var material = new THREE.MeshFaceMaterial([
new THREE.MeshPhongMaterial({map: THREE.ImageUtils.loadTexture('sides/2.png')}),
new THREE.MeshPhongMaterial({map: THREE.ImageUtils.loadTexture('sides/2.png')}),
new THREE.MeshPhongMaterial({map: THREE.ImageUtils.loadTexture('sides/1.png')}),
new THREE.MeshPhongMaterial({map: THREE.ImageUtils.loadTexture('sides/3.png')}),
new THREE.MeshPhongMaterial({map: THREE.ImageUtils.loadTexture('sides/2.png')}),
new THREE.MeshPhongMaterial({map: THREE.ImageUtils.loadTexture('sides/2.png')})
]);
return new THREE.Mesh(geometry, material);
}
I would suggest you that instead of mixing all the meshes inside one, you create a group containing all the different meshes. If you do that, you don't need to mix all the materials and you avoid a lot of problems there. And, of course, you get all the advantges of having all the meshes on the same object.
As an example, to do so, follow this procedure:
var scene = new THREE.Scene();
var group = new THREE.Object3D();
var numObjects = 5; //As an example
for(var i=0;i<numObjects;i++){
var cubeGeometry = new THREE.BoxGeometry(100, 100, 100);
var material = new THREE.MeshPhongMaterial();
var mesh = new THREE.Mesh(cubeGeometry, material);
group.add(mesh);
}
scene.add(group);
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.