Is it possible in Three.js to merge two or more meshes, with different materials?
The solutions I've found, merges geometry only, or just puts the Meshes into one Object3D or Group.
Yes: Kind-of (see the comments attached to the question and this answer post):
var blueMaterial = new THREE.MeshPhongMaterial( {color: 0x0000FF } );
var redMaterial = new THREE.MeshPhongMaterial({ color:0xFF0000 });
var meshFaceMaterial = new THREE.MeshFaceMaterial( [ blueMaterial, redMaterial ] );
var boxGeometry = new THREE.BoxGeometry( 10, 10, 10 );
for ( var face in boxGeometry.faces ) {
boxGeometry.faces[ face ].materialIndex = 0;
}
var sphereGeometry = new THREE.SphereGeometry( 5, 16, 16 );
sphereGeometry.applyMatrix( new THREE.Matrix4().makeTranslation(0, 5, 0) );
var mergeGeometry = new THREE.Geometry();
mergeGeometry.merge( boxGeometry, boxGeometry.matrix );
mergeGeometry.merge( sphereGeometry, sphereGeometry.matrix, 1 );
var mesh = new THREE.Mesh( mergeGeometry, meshFaceMaterial );
scene.add( mesh );
I went with a cube and a sphere because a box for example wants to know a material id for each of its faces.
http://jsfiddle.net/v49ntxfo/
Related
var groundMaterial = new THREE.MeshPhongMaterial( { color: 0xff43fee, specular: 0x360000 ,} );
groundMesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2000, 2000 ), groundMaterial );
groundMesh.rotation.x = - Math.PI / 2;
scene.add( groundMesh );
I want to add grass texture down but not getting any possible solution this code is taken from https://github.com/schteppe/gpu-physics.js this following github repo.
You need to create a Texture and send it as map property in the MechPhongMaterial constructor
I have loaded .obj using OBJLoader2 and also with its .mtl , now when user click on one of Mesh, then i want to change mesh geometry such like that it divides into two equal parts and also have different material for them.
//this.currentobj represents the user clicked mesh.
let geometry = this.currentobj.geometry;
geometry.clearGroups();
geometry.addGroup( 0, Infinity, 0 );
geometry.addGroup( 0, Infinity, 1 );
geometry.addGroup( 0, Infinity, 2 );
geometry.addGroup( 0, Infinity, 3 );
let material0 = new THREE.MeshBasicMaterial({color: 0xff0000});
let material1 = new THREE.MeshBasicMaterial({color: 0x444444});
let material2 = new THREE.MeshBasicMaterial({color: 0x111111});
let material3 = new THREE.MeshBasicMaterial({color: 0x555555});
var materials = [ material0, material1, material2, material3 ];
let mesh = new THREE.Mesh(geometry, materials);
this.scene.add(mesh);
Dividing mesh is a solved problem in three.js. It was recently revised with a new implementation in Jun by #Manthrax and mrdoob requested it be pulled to main as the original csg solution had issues, as per this thread: https://discourse.threejs.org/t/looking-for-updated-plug-in-for-csg/6785/8
I do not know the current status of main, but Manthrax's library is available here: https://github.com/manthrax/THREE-CSGMesh with example code.
The operation returns the resulting mesh collection and the material objects can be modified individually. My own tangentially related question was answered here by Manthrax in April: Threecsg flat sides when expecting volumetric result It shows two different materials on the resulting cut of a sphere and a cube.
For example:
function doCSG(a,b,op,mat){
var bspA = CSG.fromMesh( a );
var bspB = CSG.fromMesh( b );
var bspC = bspA[op]( bspB );
var result = CSG.toMesh( bspC, a.matrix );
result.material = mat;
result.castShadow = result.receiveShadow = true;
return result;
}
var meshA = new THREE.Mesh(new THREE.BoxGeometry(1,1,1));
var meshB = new THREE.Mesh(new THREE.BoxGeometry(1,1,1));
meshB.position.add(new THREE.Vector3( 0.5, 0.5, 0.5);
var meshC = doCSG( meshA,meshB, 'subtract',meshA.material);
console.log(meshC.material);//mesh C result has it's own material derived from meshA but can be new Material.
In your case you'd want to use the bounding box helper to produce a mesh that you move half way into the object and then use that to cut your geometry in half.
I have a binary STL colored file. I can see the color using the online mesh viewer http://www.viewstl.com/.
I am using the below standard approach to load and visualize the stl file, and it works well. However, the intrisic colors do not appear correctly and are too sensivite to light changes.
Detector.addGetWebGLMessage();
scene = new THREE.Scene();
var aspect = window.innerWidth / window.innerHeight;
var d = 100;
camera = new THREE.OrthographicCamera( - d * aspect, d, d * aspect,- d, 1, 1000 );
camera.position.set( 0, 0, 200 );
camera.lookAt( scene.position );
scene.add( camera );
var light = new THREE.AmbientLight( 0x404040 ); // soft white light
scene.add( light );
var directionalLight = new THREE.DirectionalLight( 0xffffff );
directionalLight.position.x = 0;
directionalLight.position.y = 0;
directionalLight.position.z = 1;
directionalLight.position.normalize();
scene.add( directionalLight );
var loader = new THREE.STLLoader();
var material = new THREE.MeshPhongMaterial( { color: 0xAAAAAA, specular: 0x111111, shininess: 0 } );
// Colored binary STL
var stlfile = "myBinarySTLColoredFile.stl"
loader.load( stlfile, function ( geometry ) {
var meshMaterial = material;
if (geometry.hasColors) {
meshMaterial = new THREE.MeshPhongMaterial({ opacity: geometry.alpha, vertexColors: THREE.VertexColors });
}
mesh = new THREE.Mesh( geometry, meshMaterial );
scene.add(
mesh.position.set(-100, -100, 0);
});
renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true } );
renderer.setSize(....);
var container = document.getElementById('`enter code here`flex2');
Question: How to visualize the color enclosed in the STL file ?
Thanks
I finally succeed to render correctly the colored object.
I use PLYLoader instead of STLLoader.
Apparently, three.js STL api do not manage the color.
There is no right answer. The color will depend on the light.
See the clear thread Realistic lighting (sunlight) with Three.js?
To get the original colors and no directional lighting, remove the directional light and crank the ambient light up to full brightness:
...
scene.add( camera );
var light = new THREE.AmbientLight( 0xFFFFFF ); // Full-bright white light
scene.add( light );
var loader = new THREE.STLLoader();
...
If that doesn't give you the desired result, please add screenshots of the actual and desired visual look to your question.
This is an old question, but for anyone else who comes across it, I don't think the accepted answer about using the PLYLoader instead of the STLLloader is correct, the STLLoader appears to support embedded color in binary STLs just fine in the example.
One possible cause of the OP's issue using THREE.VertexColors in THREE.MeshPhongMaterial({ opacity: geometry.alpha, vertexColors: THREE.VertexColors }) instead of true as in the docs/example.
I'm trying to subtract "holes" in platforms using ThreeCSG. I want the holes to be subtracted in specific locations on the larger platform.
var geometry = new THREE.CubeGeometry( 500, 10, 500 );
var hole_geometry = new THREE.CubeGeometry( 50, 11, 50 );
var material = Physijs.createMaterial( new THREE.MeshLambertMaterial( { color: 0xEEEEEE } ), 0.2, 0.8 );
var hole_material = Physijs.createMaterial( new THREE.MeshLambertMaterial( { color: 0x000000, side: THREE.DoubleSide } ), 0.2, 0.8 );
var platform = { platform: null, hole: null };
// platform
platform.platform = new Physijs.BoxMesh(geometry, material, 0);
platform.platform.position.y = -i*300;
var platformBSP = new ThreeBSP( platform.platform );
// hole
platform.hole = new Physijs.BoxMesh(hole_geometry, hole_material, 0);
platform.hole.position.y = -i*300;
platform.hole.position.x = Math.floor(Math.random()*(251))*(Math.random() < 0.5 ? -1 : 1);
platform.hole.position.z = Math.floor(Math.random()*(251))*(Math.random() < 0.5 ? -1 : 1);
var holeBSP = new ThreeBSP( platform.hole );
platformBSP = platformBSP.subtract(holeBSP);
platform.platform = platformBSP.toMesh(material);
platform.platform.position.y = -i*300;
scene.add( platform_array[i].platform );
scene.add( platform_array[i].hole );
My problem is whenever the hole is converted from Threejs to ThreeCSG is doesn't take the position into account so every hole created in a platform is dead center instead of a random place.
I can't seem to find any documentation on how to reposition the "hole" after it is converted to a ThreeCSG object.
The technique used in the ThreeCSG source is to transform the geometry into a mesh, then to translate the mesh, and then to make a BSP from the translated mesh. See the 2nd line here:
var cube_geometry = new THREE.CubeGeometry( 3, 3, 3 );
var cube_mesh = new THREE.Mesh( cube_geometry );
cube_mesh.position.x = -7;
var cube_bsp = new ThreeBSP( cube_mesh );
var sphere_geometry = new THREE.SphereGeometry( 1.8, 32, 32 );
var sphere_mesh = new THREE.Mesh( sphere_geometry );
sphere_mesh.position.x = -7;
var sphere_bsp = new ThreeBSP( sphere_mesh );
var subtract_bsp = cube_bsp.subtract( sphere_bsp );
var result = subtract_bsp.toMesh( new THREE.MeshLambertMaterial({ shading: THREE.SmoothShading, map: THREE.ImageUtils.loadTexture('texture.png') }) );
result.geometry.computeVertexNormals();
scene.add( result );
In order to set your mesh in the correct place before cutting, you can perform a matrix transform on the geometry itself.
platform.platform = new Physijs.BoxMesh(geometry, material, 0);
platform.platform.geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, -i * 300, 0 ) );
It's possible a bug is preventing THREEBSP to use the position attribute (does Physijs mess with this? I have never used it).
I have several meshes; each mesh has a different texture. Now I want to merge them all:
mergedGeo.merge( mesh.geometry, mesh.matrix);
This works fine.
But when I want to add the merged mesh to the scene, they information about the texture on each mesh is lost:
mergedGeo.computeFaceNormals();
group = new THREE.Mesh( mergedGeo, new THREE.MeshBasicMaterial({ color: parseInt("ffffff", 16) }));
group.matrixAutoUpdate = false;
group.updateMatrix();
scene.add( group );
I am using Revision 68.
Each face has to have a proper material index.
Merge your geometries like so, incrementing materialIndexOffset each time, starting from 0:
mergedGeo.merge( mesh.geometry, mesh.matrix, materialIndexOffset );
...
Then construct a materials array:
var materials = [];
materials.push( material1 );
materials.push( material2 );
....
Then create your mesh:
mesh = new THREE.Mesh( mergedGeo, new THREE.MeshFaceMaterial( materials ) );
three.js r.68