Three.js - multiple material plane - three.js

I'm trying to have multiple materials on a single plane to make a simple terrain editor. So I create a couple of materials, and try to assign a material index to each vertex in my plane:
var materials = [];
materials.push(new THREE.MeshFaceMaterial( { color: 0xff0000 }));
materials.push(new THREE.MeshFaceMaterial( { color: 0x00ff00 }));
materials.push(new THREE.MeshFaceMaterial( { color: 0x0000ff }));
// Plane
var planegeo = new THREE.PlaneGeometry( 500, 500, 10, 10 );
planegeo.materials = materials;
for(var i = 0; i < planegeo.faces.length; i++)
{
planegeo.faces[i].materialIndex = (i%3);
}
planegeo.dynamic = true;
this.plane = THREE.SceneUtils.createMultiMaterialObject(planegeo, materials);
But I always get either a whole bunch of errors in the shader, or only a single all-red plane if I use MeshBasicMaterial instead of FaceMaterial. What am I doing wrong?

To get a checkerboard pattern with three colors do this:
// geometry
var geometry = new THREE.PlaneGeometry( 500, 500, 10, 10 );
// materials
var materials = [];
materials.push( new THREE.MeshBasicMaterial( { color: 0xff0000 }) );
materials.push( new THREE.MeshBasicMaterial( { color: 0x00ff00 }) );
materials.push( new THREE.MeshBasicMaterial( { color: 0x0000ff }) );
// assign a material to each face (each face is 2 triangles)
var l = geometry.faces.length / 2;
for( var i = 0; i < l; i ++ ) {
var j = 2 * i;
geometry.faces[ j ].materialIndex = i % 3;
geometry.faces[ j + 1 ].materialIndex = i % 3;
}
// mesh
mesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial( materials ) );
scene.add( mesh );
EDIT: Updated for three.js r.60

Related

Ray does not accurately determine the intersection

I want to find point of intersection curve and line. Created raycast, but it doesn't work well. The point of the ray is far from the actual intersection.
Webgl 1, threejs 0.109
var sartPoint = new THREE.Vector3( -30, -50, 0 );
var endPoint = new THREE.Vector3( 50, 80, 0 );
var geometry = new THREE.Geometry();
geometry.vertices.push(sartPoint);
geometry.vertices.push(endPoint);
var materialTmp = new THREE.LineBasicMaterial( { color: 0xffffff, linewidth: 5 } );
var itemTmp = new THREE.Line( geometry, materialTmp );
_this.add( itemTmp, 'lines' );
scene.updateMatrixWorld()
var curve = new THREE.EllipseCurve(
0, 0, // ax, aY
10, 10, // xRadius, yRadius
0, 2 * Math.PI, // aStartAngle, aEndAngle
false, // aClockwise
0 // aRotation
);
var points = curve.getPoints( 10 );
var geometry = new THREE.BufferGeometry().setFromPoints( points );
var material = new THREE.LineBasicMaterial( { color : 0xff00ff } );
var ellipse = new THREE.Line( geometry, material );
scene.add( ellipse );
var raycaster = new THREE.Raycaster(sartPoint, endPoint.clone().normalize());
var intersects = raycaster.intersectObject( ellipse );
console.log(intersects);
if(intersects.length > 0){
// FIRST dot of intersect
var dotGeometry2 = new THREE.Geometry();
dotGeometry2.vertices.push(intersects[0].point);
var dotMaterial2 = new THREE.PointsMaterial( { size: 5, color: 0x00ff00 } );
var dot2 = new THREE.Points( dotGeometry2, dotMaterial2 );
_this.add( dot2, 'points' );
}
The second argument to the Raycaster constructor is a direction vector. Instead of:
endPoint.clone().normalize()
I think you want:
endPoint.clone().sub(startPoint).normalize()
It is work if
curve.getPoints( 10 );
When
curve.getPoints( 100 );
That doesn't work.

Three.js : remove texture on top/bottom of CylinderGeometry

I'm using Three.js to make basic 3D cylinder rendering. I'm using TextureLoader to load texture async (based on UI interactions).
All is ok, but I would like those textures not to be applied on the cylinder top / bottom.
How can I achieve that?
Here's what I've done so far:
function threeJsRenderer() {
var width = 325;
var height = 375;
var scene = new THREE.Scene();
var camera = new THREE.OrthographicCamera(width / - 2, width / 2, height / 2, height / - 2, -200, 1000);
var renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setClearColor( 0x000000, 0 );
renderer.setSize(width,height);
document.getElementById('projection').appendChild(renderer.domElement);
// CylinderGeometry(radiusTop : Float, radiusBottom : Float, height : Float, radialSegments : Integer, heightSegments : Integer, openEnded : Boolean, thetaStart : Float, thetaLength : Float)
var geometry = new THREE.CylinderGeometry(135,128,110,64,1, false, 0, Math.PI-2);
var loader = new THREE.TextureLoader();
var material = new THREE.MeshPhongMaterial();
var cone = new THREE.Mesh();
var pointLight = new THREE.AmbientLight( 0xFFFFFF );
pointLight.position.x = 10;
pointLight.position.y = 50;
pointLight.position.z = 130;
scene.add(pointLight);
camera.position.z = 40;
camera.position.y = 0;
cone.rotation.x = 0.01;
cone.rotation.y = -10;
jQuery(document).on('new3DConfigReady', function () {
scene.remove(cone);
var newGeometry = new THREE.CylinderGeometry(state.cylinderGeometry.radiusTop,state.cylinderGeometry.radiusBottom,state.cylinderGeometry.height,64,1, false, 0, Math.PI-2);;
cone = new THREE.Mesh(newGeometry, material);
cone.rotation.x = 0.01;
cone.rotation.y = -0.55;
cone.position.y = state.cylinderGeometry.positionY;
geometry.dispose();
if(state.textureUrl !== ''){
scene.add(cone);
}
});
jQuery(document).on('newTextureReady', function () {
loader.load( state.textureUrl, function (texture){
material.map = texture;
material.map.anisotropy = 256;
material.map.needsUpdate = true;
material.needsUpdate = true;
scene.add(cone);
});
});
var render = function () {
requestAnimationFrame(render);
renderer.render(scene, camera);
};
render();
}
Using a materials array you can have different materials on the sides and ends of your cylinder.
var geometry = new THREE.CylinderBufferGeometry( 5, 5, 10, 16, 1 );
var materials = [
new THREE.MeshPhongMaterial( { map: texture } ),
new THREE.MeshPhongMaterial( { color: 0x0000ff } ),
new THREE.MeshPhongMaterial( { color: 0xff0000 } )
];
var mesh = new THREE.Mesh( geometry, materials );
three.js r.100

Three.js - Create new mesh from certain faces/vertices of another mesh

I´ve been several days struggling with a particular Three.js issue, and I cannot find any way to do it. This is my case:
1) I have a floating mesh, formed by several triangled faces. This mesh is created from the geometry returned by a loader, after obtaining its vertices and faces using getAttribute('position'): How to smooth mesh triangles in STL loaded BufferGeometry
2) What I want to do now is to "project" the bottom face agains the floor.
3) Later, with this new face added, create the resulting mesh of filling the space between the 3 vertices of both faces.
I already have troubles in step 2... To create a new face I´m supossed to have its 3 vertices already added to geometry.vertices. I did it, cloning the original face vertices. I use geometry.vertices.push() results to know their new indexes, and later I use that indexes (-1) to finally create the new face. But its shape is weird, also the positions and the size. I think I´m not getting the world/scene/vector position equivalence theory right :P
I tried applying this, with no luck:
How to get the absolute position of a vertex in three.js?
Converting World coordinates to Screen coordinates in Three.js using Projection
http://barkofthebyte.azurewebsites.net/post/2014/05/05/three-js-projecting-mouse-clicks-to-a-3d-scene-how-to-do-it-and-how-it-works
I discovered that if I directly clone the full original face and simply add it to the mesh, the face is added but in the same position, so I cannot then change its vertices to place it on the floor (or at least without modifying the original face vertices!). I mean, I can change their x, y, z properties, but they are in a very small measure that doesn´t match the original mesh dimensions.
Could someone help me get this concept right?
EDIT: source code
// Create geometry
var geo = new THREE.Geometry();
var geofaces = [];
var geovertices = [];
original_geometry.updateMatrixWorld();
for(var index in original_geometry.faces){
// Get original face vertexNormals to know its 3 vertices
var face = original_geometry[index];
var vertexNormals = face.vertexNormals;
// Create 3 new vertices, add it to the array and then create a new face using the vertices indexes
var vertexIndexes = [null, null, null];
for (var i = 0, l = vertexNormals.length; i < l; i++) {
var vectorClone = vertexNormals[i].clone();
vectorClone.applyMatrix4( original_geometry.matrixWorld );
//vectorClone.unproject(camera); // JUST TESTING
//vectorClone.normalize(); // JUST TESTING
var vector = new THREE.Vector3(vectorClone.x, vectorClone.z, vectorClone.y)
//vector.normalize(); // JUST TESTING
//vector.project(camera); // JUST TESTING
//vector.unproject(camera); // JUST TESTING
vertexIndexes[i] = geovertices.push( vector ) - 1;
}
var newFace = new THREE.Face3( vertexIndexes[0], vertexIndexes[1], vertexIndexes[2] );
geofaces.push(newFace);
}
// Assign filled arrays to the geometry
geo.faces = geofaces;
geo.vertices = geovertices;
geo.mergeVertices();
geo.computeVertexNormals();
geo.computeFaceNormals();
// Create a new mesh with resulting geometry and add it to scene (in this case, to the original mesh to keep the positions)
new_mesh = new THREE.Mesh( geo, new THREE.MeshFaceMaterial(material) ); // material is defined elsewhere
new_mesh.position.set(0, -100, 0);
original_mesh.add( new_mesh );
I created a fully operational JSFiddle with the case to try things and see the problem more clear. With this STL (smaller than my local example) I cannot even see the badly cloned faces added to the scene.. Maybe they are too small or out of focus.
Take a look to the calculateProjectedMesh() function, here is where I tried to clone and place the bottom faces (already detected because they have a different materialIndex):
JSFiddle: https://jsfiddle.net/tc39sgo1/
var container;
var stlPath = 'https://dl.dropboxusercontent.com/s/p1xp4lhy4wxmf19/Handle_Tab_floating.STL';
var camera, controls, scene, renderer, model;
var mouseX = 0,
mouseY = 0;
var test = true;
var meshPlane = null, meshStl = null, meshCube = null, meshHang = null;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
/*THREE.FrontSide = 0;
THREE.BackSide = 1;
THREE.DoubleSide = 2;*/
var materials = [];
materials.push( new THREE.MeshPhongMaterial({color : 0x00FF00, side:0, shading: THREE.FlatShading, transparent: true, opacity: 0.9, overdraw : true, wireframe: false}) );
materials.push( new THREE.MeshPhongMaterial({color : 0xFF0000, transparent: true, opacity: 0.8, side:0, shading: THREE.FlatShading, overdraw : true, metal: false, wireframe: false}) );
materials.push( new THREE.MeshPhongMaterial({color : 0x0000FF, side:2, shading: THREE.FlatShading, overdraw : true, metal: false, wireframe: false}) );
var lineMaterial = new THREE.LineBasicMaterial({ color: 0x0000ff, transparent: true, opacity: 0.05 });
init();
animate();
function webglAvailable() {
try {
var canvas = document.createElement('canvas');
return !!(window.WebGLRenderingContext && (
canvas.getContext('webgl') || canvas.getContext('experimental-webgl')));
} catch (e) {
return false;
}
}
function init() {
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 0.1, 100000000);
camera.position.x = 1500;
camera.position.z = -2000;
camera.position.y = 1000;
controls = new THREE.OrbitControls(camera);
// scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight(0x101030); //0x101030
scene.add(ambient);
var directionalLight = new THREE.DirectionalLight(0xffffff, 2);
directionalLight.position.set(0, 3, 0).normalize();
scene.add(directionalLight);
var directionalLight = new THREE.DirectionalLight(0xffffff, 2);
directionalLight.position.set(0, 1, -2).normalize();
scene.add(directionalLight);
if (webglAvailable()) {
renderer = new THREE.WebGLRenderer();
} else {
renderer = new THREE.CanvasRenderer();
}
renderer.setClearColor( 0xCDCDCD, 1 );
// renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
document.addEventListener('mousemove', onDocumentMouseMove, false);
window.addEventListener('resize', onWindowResize, false);
createPlane(500, 500);
createCube(500);
loadStl();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function onDocumentMouseMove(event) {
mouseX = (event.clientX - windowHalfX) / 2;
mouseY = (event.clientY - windowHalfY) / 2;
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render(scene, camera);
}
function createPlane(width, height) {
var planegeometry = new THREE.PlaneBufferGeometry(width, height, 0, 0);
var material = new THREE.MeshLambertMaterial({
color: 0xFFFFFF,
side: THREE.DoubleSide
});
planegeometry.computeBoundingBox();
planegeometry.center();
meshPlane = new THREE.Mesh(planegeometry, material);
meshPlane.rotation.x = 90 * (Math.PI/180);
//meshPlane.position.y = -height/2;
scene.add(meshPlane);
}
function createCube(size) {
var geometry = new THREE.BoxGeometry( size, size, size );
geometry.computeFaceNormals();
geometry.mergeVertices();
geometry.computeVertexNormals();
geometry.center();
var material = new THREE.MeshPhongMaterial({
color: 0xFF0000,
opacity: 0.04,
transparent: true,
wireframe: true,
side: THREE.DoubleSide
});
meshCube = new THREE.Mesh(geometry, material);
meshCube.position.y = size/2;
scene.add(meshCube);
}
function loadStl() {
var loader = new THREE.STLLoader();
loader.load( stlPath, function ( geometry ) {
// Convert BufferGeometry to Geometry
var geometry = new THREE.Geometry().fromBufferGeometry( geometry );
geometry.computeBoundingBox();
geometry.computeVertexNormals();
geometry.center();
var faces = geometry.faces;
for(var index in faces){
var face = faces[index];
var faceNormal = face.normal;
var axis = new THREE.Vector3(0,-1,0);
var angle = Math.acos(axis.dot(faceNormal));
var angleReal = (angle / (Math.PI/180));
if(angleReal <= 70){
face.materialIndex = 1;
}
else{
face.materialIndex = 0;
}
}
geometry.computeFaceNormals();
geometry.computeVertexNormals();
meshStl = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials));
meshStl.position.x = 0;
meshStl.position.y = 400;
scene.add( meshStl );
// Once loaded, calculate projections mesh
calculateProjectedMesh();
});
}
function calculateProjectedMesh(){
var geometry = meshStl.geometry;
var faces = geometry.faces;
var vertices = geometry.vertices;
var geometry_projected = new THREE.Geometry();
var faces_projected = [];
var vertices_projected = [];
meshStl.updateMatrixWorld();
for(var index in faces){
var face = faces[index];
// This are the faces
if(face.materialIndex == 1){
var vertexIndexes = [face.a, face.b, face.c];
for (var i = 0, l = vertexIndexes.length; i < l; i++) {
var relatedVertice = vertices[ vertexIndexes[i] ];
var vectorClone = relatedVertice.clone();
console.warn(vectorClone);
vectorClone.applyMatrix4( meshStl.matrixWorld );
////////////////////////////////////////////////////////////////
// TEST: draw line
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(vectorClone.x, vectorClone.y, vectorClone.z));
//geometry.vertices.push(new THREE.Vector3(vectorClone.x, vectorClone.y, vectorClone.z));
geometry.vertices.push(new THREE.Vector3(vectorClone.x, meshPlane.position.y, vectorClone.z));
var line = new THREE.Line(geometry, lineMaterial);
scene.add(line);
console.log("line added");
////////////////////////////////////////////////////////////////
vectorClone.y = 0;
var vector = new THREE.Vector3(vectorClone.x, vectorClone.y, vectorClone.z);
vertexIndexes[i] = vertices_projected.push( vector ) - 1;
}
var newFace = new THREE.Face3( vertexIndexes[0], vertexIndexes[1], vertexIndexes[2] );
newFace.materialIndex = 2;
faces_projected.push(newFace);
}
}
geometry_projected.faces = faces_projected;
geometry_projected.vertices = vertices_projected;
geometry_projected.mergeVertices();
console.info(geometry_projected);
meshHang = new THREE.Mesh(geometry_projected, new THREE.MeshFaceMaterial(materials));
var newY = -(2 * meshStl.position.y) + 0;
var newY = -meshStl.position.y;
meshHang.position.set(0, newY, 0);
meshStl.add( meshHang );
}
EDIT: Finally!! I got it! To clone the original faces I must access their 3 original vertices using "a", "b" and "c" properties, which are indexes referencing Vector3 instances in the "vertices" array of the original geometry.
I cloned the 3 vertices flatting the Z position to zero, use their new indexes to create the new face and add it to the projection mesh (in blue).
I´m also adding lines as a visual union between both faces. Now I´m ready for step 3, but I think this is complex enough to close this question.
Thanks for the updateMatrixWorld clue! It was vital to achieve my goal ;)
try this
original_geometry.updateMatrixWorld();
var vertexIndexes = [null, null, null];
for (var i = 0, l = vertexNormals.length; i < l; i++) {
var position = original_geometry.geometry.vertices[i].clone();
position.applyMatrix4( original_geometry.matrixWorld );
var vector = new THREE.Vector3(position.x, position.y, position.z)
vertexIndexes[i] = geovertices.push( vector ) - 1;
}

Threejs | How to tween Radius (RingGeometry)

How can I tween the innerRadius attribute of THREE.RingGeometry() in three.js using tween.js. I don't want to scale the ring, I want to update geometry.
You will need to look at morphing the vertices, This website has great examples for different situations:
https://stemkoski.github.io/Three.js/Graphulus-Surface.html
https://stemkoski.github.io/Three.js/
Have look through the morphing samples aswell..
May be an answer if it can give idea to help.
1 - Give a name to the ring,
2 - Create a function to find, remove and redraw the ring
3 - and with Tween.js or setInterval use the function to animate.
Something like :
var rStart = 100;
var rStep = 10;
var ep = 50;
//create circle
var geometry = new THREE.RingGeometry( rStart, rStart + ep, 32,3,0, Math.PI * 2 );
var material = new THREE.MeshBasicMaterial( { color: 0xff0000, side: THREE.DoubleSide } );
var ring = new THREE.Mesh( geometry, material );
ring.name = 'the_ring';
scene.add( ring );
// function to find ring, remove and redraw
function grow(i,rStart,rStep,ep){
var ringToRemove = 'the_ring';
var ringToRemoveSelected = scene.getObjectByName(ringToRemove);
scene.remove(ringToRemoveSelected);
var newRadius = rStart + ( rStep * i);
var geometry = new THREE.RingGeometry( newRadius , newRadius + ep , 32,3,0, Math.PI * 2);
var material = new THREE.MeshBasicMaterial( { color: 0xff0000, side: THREE.DoubleSide } );
var ring = new THREE.Mesh( geometry, material );
ring.name = 'the_ring';
scene.add( ring );
}
//and animate
var i = 0;
setInterval(function () {
i++;
if(i < 100){
grow(i,rStart,rStep,ep);
}
}, 100);

Shading of a plane

It is probably a beginner question, but why does the plane appear to be flat? Why is the structure of the plane not visible, only the spikes on the edges?
http://codepen.io/asz/pen/GgXpXW
var geometry = new THREE.PlaneBufferGeometry( 100, 100, 50, 50 );
var vertices = geometry.attributes.position.array;
for ( var i = -1; i < vertices.length; i += 3) {
vertices[i] = Math.random() * 10;
}
geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2.8 ) );
var material = new THREE.MeshPhongMaterial( { ambient: 0x00ff00, color: 0x00ff00, specular: 0x00ff00, shininess: 30, shading: THREE.FlatShading } );
var ground = new THREE.Mesh( geometry, material );
scene.add( ground );
To do with normals.
add geometry.computeVertexNormals();
eg.
var vertices = geometry.attributes.position.array;
for ( var i = -1; i < vertices.length; i += 3) {
vertices[i] = Math.random() * 10;
}
geometry.computeVertexNormals();

Resources