THREE.ExplodeModifier hacking - javascript

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

Related

ThreeJS Merging multiple meshes with unique materials

I don't know what I'm doing wrong. I have multiple meshes that I am trying to merge into one mesh so that I can save on draw calls.
Each of my meshes has a unique materials. In this example it just has a different color, but really they will have unique textures mapped.
This is my code:
materials = [];
blocks = [];
var tempMat;
var tempCube;
var tempGeo;
var tempvec;
// block 1
tempMat = new THREE.MeshLambertMaterial({ color: '0x0000ff' });
materials.push( tempMat );
tempGeo = new THREE.CubeGeometry(1, 1, 1);
for (var ix=0; ix<tempGeo.faces.length; ix++) {
tempGeo.faces[ix].materialIndex = 0;
}
tempCube = new THREE.Mesh( tempGeo, tempMat );
tempCube.position.set(0, 3, -6);
blocks.push( tempCube );
// block 2
tempMat = new THREE.MeshLambertMaterial({ color: '0x00ff00' });
materials.push( tempMat );
tempGeo = new THREE.CubeGeometry(1, 1, 1);
for (var ix=0; ix<tempGeo.faces.length; ix++) {
tempGeo.faces[ix].materialIndex = 1;
}
tempCube = new THREE.Mesh( tempGeo, tempMat );
tempCube.position.set(1, 3, -6);
blocks.push( tempCube );
// Merging them all into one
var geo = new THREE.Geometry();
for (var i=0; i<blocks.length; i++) {
blocks[i].updateMatrix();
geo.merge(blocks[i].geometry, blocks[i].matrix, i);
}
var newmesh = new THREE.Mesh( geo, new THREE.MeshFaceMaterial( materials ) );
scene.add(newmesh);
Basically, that gives me an error that says:
Uncaught TypeError: Cannot read property 'visible' of undefined
every time my render function is called.
Where did I go wrong?
You are merging geometries into one, and using MeshFaceMaterial (renamed MultiMaterial in r.72).
It does not make any sense to merge geometries having different material indices.
WebGLRenderer needs to segment the geometry by material to render it.
As a rule-of-thumb, only merge geometries if they will be rendered with a single material.
three.js r.72

Threejs BufferGeometry - Render some faces with other Texture

i have an output date like this:
geom[0] = {
texturesindexT: new Int16Array([0,1,2,3]),
texturesindexS: new Int16Array([-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,...]),
materialsindexT: new Int16Array([-1,-1,-1,-1]),
materialsindexS: new Int16Array([-1,0,1,2,3,4,5,0,6,2,7,8,-1,0,...]),
startIndicesT: new Uint32Array([0,288,606,897,1380]),
startIndicesS: new Uint32Array([1380,1431,1479,1485,1497,1515,1659,...]),
m_indices: new Uint16Array([0,1,2,3,0,2,4,2,5,4,6,2,7,3,2,8,9,10,...]),
m_vertices: new Float32Array([-81.93996,25.7185,-85.53822,-81.93996,...]),
m_normals: new Float32Array([-0.004215205,0.9999894,-0.001817489,-0.004215205,...]),
m_texCoords: new Float32Array([0,0.04391319,0,0.2671326,0.009521127,0.03514284,...]),
}
var textures = new Array("-1_-1/t0.jpg","-1_-1/t1.jpg","-1_-1/t2.jpg",...);
The Data is in order for an Index, Vertex and Normal-Buffer but sections have to be rendered with other Textures and Maretials.
I have tried to make a THREE.Geometry out of the indices, vertices and texCoords/UVCoords but that didn't work.
Now i am trying use a THREE.BufferGeometry() and this work BUT i need to render index 0 to 287 with Texture "textures[0]" and index 288 to 605 with "textures[1]" and so on.
My first attempt was to make a BufferGeometry for each part with index 288 to 605 , but since the Indices are in order for the hole model, i have to put the complete vertices, normales and UVCoords in the Buffer for just a couple of faces.
Is there a way to render sections of the BufferGeometry with other Textures or to set the Texture Index for each Face?
Or is it possible to create a Material, that renders the first X faces with Texture A and the next with Texture B???
If you want to use two different textures with a single BufferGeometry, you can use this pattern, which sets drawcalls:
var geometry1 = new THREE.BufferGeometry();
// ...and set the data...
var geometry2 = geometry1.clone();
// set drawcalls
geometry1.offsets = geometry1.drawcalls = []; // currently required
geometry1.addDrawCall( start1, count1, 0 );
geometry2.offsets = geometry2.drawcalls = []; // currently required
geometry2.addDrawCall( start2, count2, 0 );
var material1 = new THREE.MeshPhongMaterial( { map: map1 } );
var material2 = new THREE.MeshPhongMaterial( { map: map2 } );
var mesh1 = new THREE.Mesh( geometry1, material1 );
var mesh2 = new THREE.Mesh( geometry2, material2 );
three.js r.70
You can create two geometries with same vertex buffers and different indexes:
var position = new THREE.BufferAttribute(positionArray, 3);
var normal = new THREE.BufferAttribute(normalArray, 3);
var uv = new THREE.BufferAttribute(uvArray, 2);
var indices1 = new THREE.BufferAttribute(indexArray1, 1);
var geometry1 = new THREE.BufferGeometry();
geometry1.addAttribute('position', position);
geometry1.addAttribute('normal', normal);
geometry1.addAttribute('uv', uv);
geometry1.addAttribute('index', indices1);
var indices2 = new THREE.BufferAttribute(indexArray2, 1);
var geometry2 = new THREE.BufferGeometry();
geometry2.addAttribute('position', position);
geometry2.addAttribute('normal', normal);
geometry2.addAttribute('uv', uv);
geometry2.addAttribute('index', indices2);
and then create 2 meshes with different materials as you normally would. As far as I understand, this will re-use same data in both meshes.

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);

Interpolated surface in Three.js

Hi folks,
I've got a question belongig surfaces in Three.js:
I got a bunch of Vec3 Points and want want to interpolate a surface through them. While searching, I stumbeled across beziers (three.js bezier - only as lines) and what looked more like I was searching : three.js Nurbs. I've tried to reconstruct the code, but the documentation was terrible (pages like this) and I didn't get how everything worked by reconstructing the code...
So here's the question:
Is there any easy way to get a shape out of my calculated points? (I would still be happy, if it's not interpolated).
Thank you guys!
Mat
Edit: What I want to acchieve is a surface plot. I stumbeled across http://acko.net/blog/making-mathbox/ but it's way too big for my needs...
After some try and error I found a solution: add a plane and than transform the single vertices.
// need to setup 'step', 'xStart', 'xEnd', 'yStart', 'yEnd'
// calc the variables
var width = Math.abs(-xStart+xEnd),
height = Math.abs(-yStart+yEnd);
var stepsX = width*step, stepsY = height*step;
var posX = (xStart+xEnd)/2;
var posZ = (yStart+yEnd)/2;
// add a plane and morph it to a function
var geometry = new THREE.PlaneGeometry( width, height, stepsX - 1, stepsY - 1 );
geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
var size = stepsX * (stepsY),
data = new Float32Array( size );
var count = 0, scope = {};
mesh = new THREE.Mesh( geometry, new THREE.MeshNormalMaterial( {
side : THREE.DoubleSide,
transparent: true,
shading: THREE.SmoothShading,
opacity : _opacity }));
mesh.updateMatrixWorld();
// calc y value for every vertice
for ( var i = 0; i < size; i ++ ) {
// calculate the current values
// http://stackoverflow.com/questions/11495089/how-to-get-the-absolute-position-of-a-vertex-in-three-js
var vector = mesh.geometry.vertices[i].clone();
vector.applyMatrix4(
mesh.matrixWorld
);
// set them into the scope
scope.x = vector.x + posX;
scope.y = vector.z + posZ;
// calculate point and write it in a temp array
data[i] = math.eval(term, scope);
}
// push the new vertice data
for ( var i = 0, l = geometry.vertices.length; i < l; i ++ ) {
geometry.vertices[ i ].y = data[ i ];
}
// update the new normals
geometry.computeFaceNormals();
geometry.computeVertexNormals();
// add to scene
scene.add( mesh );
Only issue is that it is not working for non static functions like tan(x). This snippet is using math.js to calc the term.
Greetings Mat

Three js merging Geometries and Mesh

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

Categories