Three js merging Geometries and Mesh - javascript

Three js ver67
Current code something like this -
var materials = [];
var totalGeom = new THREE.Geometry();
var cubeMat;
for (var i = 0; i < dataSetArray.length; i++) {
var ptColor = dataSetArray[i].color;
var value = dataSetArray[i].value;
var position = latLongToVector3(dataSetArray[i].y, dataSetArray[i].x, 600, 1);
var cubeGeom = new THREE.BoxGeometry(5, 5, 1 + value / 20);
cubeMat = new THREE.MeshLambertMaterial({
color: new THREE.Color(ptColor),
opacity: 0.6
});
materials.push(cubeMat);
// cubeGeom.updateMatrix();
var cubeMesh = new THREE.Mesh(cubeGeom, cubeMat);
cubeMesh.position = position;
cubeMesh.lookAt(scene.position);
// totalGeom.merge(cubeMesh.geometry, cubeMesh.geometry.matrix);
//THREE.GeometryUtils.setMaterialIndex(cubeMesh.geometry, i);
THREE.GeometryUtils.merge(totalGeom, cubeMesh);
}
var total = new THREE.Mesh(totalGeom, new THREE.MeshFaceMaterial(materials));
scene.add(total);
However I get the message
DEPRECATED: GeometryUtils's .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.
in chrome dev tools.
When I try something like -
totalGeom.merge(cubeMesh.geometry, cubeMesh.geometry.matrix);
instead of THREE.GeometryUtils.merge(totalGeom, cubeMesh); I get exceptions.
How will I do the above merge? Please help.

Note: This answer applies to legacy versions of three.js.
Do this:
cubeMesh.updateMatrix();
totalGeom.merge( cubeMesh.geometry, cubeMesh.matrix );
For a further understanding, see the source code of THREE.Geometry.merge().
three.js r.69

Related

ThreeCSG don't give expected result

I want to create a simple app in three.js
For this app, I need to subtract two meshes and I have found that ThreeCSG can do this. But somehow I don't get the expected result.
I have copied the code from an example, but even this doesn't work properly.
Trying some other function like the union. But instead of merging two meshes into one it removes it.
link to ThreeCSG: https://github.com/chandlerprall/ThreeCSG/blob/master/ThreeCSG.js
result that I get when subtracting
result that I get when I use union
var materialNormal = new THREE.MeshNormalMaterial( { side: THREE.DoubleSide } );
var diceCube = new THREE.Mesh( new THREE.BoxGeometry(10,10,10), materialNormal);
diceCube.position.x = 0;
diceCube.position.y = 5;
diceCube.position.z = 0;
diceCube.geometry.computeFaceNormals();
diceCube.geometry.computeVertexNormals();
var cubeBSP = new ThreeBSP(diceCube);
var sphereGeometry = new THREE.SphereGeometry(7.5,16,8);
var sphereMesh = new THREE.Mesh(sphereGeometry, materialNormal);
sphereMesh.scale.x = 0.17;
sphereMesh.scale.y = 0.17;
sphereMesh.scale.z = 0.17;
//coords of the spheres
var xPositions = [ 0, 3 ]; // coordinates for xPositions of sphereMesh
var yPositions = [ 10, 10 ];
var zPositions = [ 0, 0 ];
var diceDots = new THREE.Geometry();
for(var i = 0; i < xPositions.length; i++){
sphereMesh.position.x = xPositions[i];
sphereMesh.position.y = yPositions[i];
sphereMesh.position.z = zPositions[i];
sphereMesh.updateMatrix();
diceDots.merge( sphereMesh.geometry, sphereMesh.matrix );
}
var material = new THREE.MeshPhongMaterial( { color: 0xffaa00 });
var dotsMesh = new THREE.Mesh(diceDots);
dotsMesh.geometry.computeFaceNormals();
dotsMesh.geometry.computeVertexNormals();
var dotsBSP = new ThreeBSP(dotsMesh);
var resultBSP = cubeBSP.subtract(dotsBSP);
result = resultBSP.toMesh(material);
scene.add(result);
I have found a solution.
The threeCSG I used was corrupted this one works exactly how it must be done.
Link to working ThreeCSG: https://github.com/oathihs/ThreeCSG/blob/master/dist/THREE.CSG.js

Three.js | Generate dynamically a BezierCurve

I don't find the right syntax to generate dynamically a BezierCurve from an array, I've tried to make a push, but it don't work.
var x = [0,10,100,220,100, etc,...];
var y = [10,0,100,200,200, etc,...];
var z = [100,220,10,0,100, etc,...];
var points = [];
for(i=0; i < x.length; i++){
points.push( new THREE.Vector3( x[i], y[i], z[i] ) );
}
var curve = new THREE.CubicBezierCurve3( points );
var geometry = new THREE.Geometry();
var curveObject = new THREE.Line( geometry, material );
scene.add(curveObject);
An idea ?
you can use
var curve = new (Function.bind.apply(THREE.CubicBezierCurve3, [null].concat(points)));
or if you target only ES6 compatible browsers spread operator
var curve = new THREE.CubicBezierCurve3(...points);

Why mesh created with ThreeBSP.js is not working properly with any material other than MeshNormalMaterial?

The dice looks like a dice only if I use MeshNormalMaterial in the second last line (result = resultBSP.toMesh(materialNormal);). With any other material it just looks like a cube with no subtraction (dots) on it. The ThreeBSP (ThreeCSG upgrade) library I am using is located here.
There is no problem with using the MeshNormalMaterial. It just doesn't have almost any options to modify it. (It doesn't take parameters like other materials).
Here is my function that I am using to create a dice:
function buildDice(){
var materialNormal = new THREE.MeshNormalMaterial();
var diceCube = new THREE.Mesh( new THREE.BoxGeometry(100,100,100), materialNormal);
diceCube.position.x = 0;
diceCube.position.y = 50;
diceCube.position.z = 0;
diceCube.geometry.computeFaceNormals();
diceCube.geometry.computeVertexNormals();
var cubeBSP = new ThreeBSP(diceCube);
var sphereGeometry = new THREE.SphereGeometry(75,16,8);
var sphereMesh = new THREE.Mesh(sphereGeometry, materialNormal);
sphereMesh.scale.x = 0.17;
sphereMesh.scale.y = 0.17;
sphereMesh.scale.z = 0.17;
//coords of the spheres
var xPositions = [....]; // coordinates for xPositions of sphereMesh
var yPositions = [....];
var zPositions = [....];
var diceDots = new THREE.Geometry();
for(var i = 0; i < xPositions.length; i++){
sphereMesh.position.x = xPositions[i];
sphereMesh.position.y = yPositions[i];
sphereMesh.position.z = zPositions[i];
THREE.GeometryUtils.merge(diceDots, sphereMesh);
}
var dotsMesh = new THREE.Mesh(diceDots, materialNormal);
dotsMesh.geometry.computeFaceNormals();
dotsMesh.geometry.computeVertexNormals();
var dotsBSP = new ThreeBSP(dotsMesh);
var resultBSP = cubeBSP.subtract(dotsBSP);
result = resultBSP.toMesh(materialNormal);
scene.add(result);
}
It does work with other Materials, for example with a THREE.MeshPhongMaterial. This jsfiddle using your buildDice()-function may help you:
http://jsfiddle.net/L0rdzbej/152/
You have to update the mesh´s matrix before merging the geometry, and best not to use deprecated functions for this.
Three.js r73

THREE.ExplodeModifier hacking

I'm using the ExplodeModifier to duplicate the vertices so I can have individual control over Face3 objects.
For my specific example, this alone looks visually poor, so I decided to add 3 extra faces (per existing face) so I can have a pyramid shape pointing inwards the geometry.
I managed to modify the ExplodeModifier and create the extra faces, however I get several errors:
THREE.DirectGeometry.fromGeometry(): Undefined vertexUv and THREE.BufferAttribute.copyVector3sArray(): vector is undefined
I understand that now I have 9 extra vertices per face, so I need according uv's, and since I don't need a texture but a solid color I don't mind having the wrong uvs... So, I also duplicated the uvs and avoid the first warning but I can't get rid of the copyVector2sArray...
pseudo code:
var geometry = new THREE.IcosahedronGeometry( 200, 1 );
var material = new THREE.MeshPhongMaterial( { shading: THREE.FlatShading } );
var explodeModifier = new THREE.ExplodeModifier();
explodeModifier.modify( geometry );
var mesh = new THREE.Mesh( geometry, material );
scene.addChild( mesh );
The Explode Modifier has this pseudo code:
var vertices = [];
var faces = [];
for ( var i = 0, il = geometry.faces.length; i < il; i ++ ) {
(...)
var extraFace1 = new THREE.Face3().copy(face)
extraFace1.c = geometry.vertices[0]
var extraFace2 = new THREE.Face3().copy(face)
extraFace2.b = geometry.vertices[0]
var extraFace3 = new THREE.Face3().copy(face)
extraFace3.a = geometry.vertices[0]
faces.push( extraFace1 );
faces.push( extraFace2 );
faces.push( extraFace3 );
}
geometry.vertices = vertices;
geometry.faces = faces;
```
I added an example HERE. It works, but I want to avoid the console warnings...
As pointed out by #mrdoob I was assigning a THREE.Vector3 and not an index to the added THREE.Face3.
var extraFace1 = new THREE.Face3().copy(face)
extraFace1.a = geometry.faces.length * 3 - 1
var extraFace2 = new THREE.Face3().copy(face)
extraFace2.b = geometry.faces.length * 3 - 1
var extraFace3 = new THREE.Face3().copy(face)
extraFace3.c = geometry.faces.length * 3 - 1
jsfiddle updated

Use multiple materials for merged geometries in Three.js

I want to create a Pine using 2 meshes, 1 for the trunk and another one for the bush, this what I've done:
var pine_geometry = new THREE.Geometry();
var pine_texture_1 = THREE.ImageUtils.loadTexture('./res/textures/4.jpg');
var pine_geometry_1 = new THREE.CylinderGeometry(25, 25, 50, 6);
var pine_material_1 = new THREE.MeshBasicMaterial({
map : pine_texture_1
});
var pine_1 = new THREE.Mesh(pine_geometry_1);
pine_1.position.x = x;
pine_1.position.y = y + 25;
pine_1.position.z = z;
pine_1.updateMatrix();
pine_geometry.merge(pine_1.geometry, pine_1.matrix);
var pine_texture_2 = THREE.ImageUtils.loadTexture('./res/textures/5.jpg');
var pine_geometry_2 = new THREE.CylinderGeometry(0, 70, 250, 8);
var pine_material_2 = new THREE.MeshBasicMaterial({
map : pine_texture_2
});
var pine_2 = new THREE.Mesh(pine_geometry_2);
pine_2.position.x = x;
pine_2.position.y = y + 175;
pine_2.position.z = z;
pine_2.updateMatrix();
pine_geometry.merge(pine_2.geometry, pine_2.matrix);
var pine = new THREE.Mesh(pine_geometry, new THREE.MeshFaceMaterial([pine_material_1, pine_material_2]));
pine.geometry.computeFaceNormals();
pine.geometry.computeVertexNormals();
Game.scene.add(pine);
The Pine is correctly positioned as I want, however, the whole merged shape only uses 1 material instead of the 2 (the whole shape is covered by the 1st) and I want that each mesh has it's respective material when mergin both.
What I'm doing wrong? any idea?
After a long research I discovered that I was missing an extra parameter for the method 'merge' from the Geometry object, the last parameter is the index of the material that the mesh must have from the materials array, ex: 0 -> first material in 'materials' array... and so on.
So, my final piece of code looks like:
pine_geometry.merge(pine_1.geometry, pine_1.matrix, 0);
var pine_texture_2 = THREE.ImageUtils.loadTexture('./res/textures/5.jpg');
var pine_geometry_2 = new THREE.CylinderGeometry(0, 70, 250, 8);
var pine_material_2 = new THREE.MeshBasicMaterial({
map : pine_texture_2
});
var pine_2 = new THREE.Mesh(pine_geometry_2);
pine_2.position.x = x;
pine_2.position.y = y + 175;
pine_2.position.z = z;
pine_2.updateMatrix();
pine_geometry.merge(pine_2.geometry, pine_2.matrix, 1);
(Note the last numbers I add to each merge).
However, I want to clarify that this practice only works when we are dealing with various geometries that are from the same type, in this case, we're merging two CylinderGeometry, but if we wanted to merge for example a Cylinder with a Box AND add the MeshFaceMaterial, it wouldn't be recognized properly and the console will throw us 'Cannot read property map/attributes from undefined', nevertheless we can still merge both geometries but not providing multiple materials (that's a terrible mistake I made).
Hope this helps to anyone.
Here's a general function to merge meshes with materials, You can also specify if you want it to return it as a buffer geometry.
function _mergeMeshes(meshes, toBufferGeometry) {
var finalGeometry,
materials = [],
mergedGeometry = new THREE.Geometry(),
mergeMaterial,
mergedMesh;
meshes.forEach(function(mesh, index) {
mesh.updateMatrix();
mesh.geometry.faces.forEach(function(face) {face.materialIndex = 0;});
mergedGeometry.merge(mesh.geometry, mesh.matrix, index);
materials.push(mesh.material);
});
mergedGeometry.groupsNeedUpdate = true;
mergeMaterial = new THREE.MeshFaceMaterial(materials);
if (toBufferGeometry) {
finalGeometry = new THREE.BufferGeometry().fromGeometry(mergedGeometry);
} else {
finalGeometry = mergedGeometry;
}
mergedMesh = new THREE.Mesh(finalGeometry, mergeMaterial);
mergedMesh.geometry.computeFaceNormals();
mergedMesh.geometry.computeVertexNormals();
return mergedMesh;
}
var mergedMesh = _mergeMeshes([trunkMesh, treeTopMesh], true);

Categories