I am adding a plane and a grid using Gridhelper to my 3d scene:
// PLANE XY static
var gridplaneSize = 20;
var color = 0xFFDCBB;
var plGeometry = new THREE.PlaneGeometry(gridplaneSize, gridplaneSize);
var plMaterial = new THREE.MeshBasicMaterial( {color:color, ambient:color, side:THREE.DoubleSide, opacity:0.5, transparent:true, depthWrite: false } );
var planeMesh_xy = new THREE.Mesh(plGeometry, plMaterial);
planeMesh_xy.rotation.x = -Math.PI/2;
scene.add(planeMesh_xy);
planeMesh_xy.receiveShadow = true;
// GRID XY static
var gridstep = 1;
var gridcolor = 0xCCCCCC;
var gridHelper_xy = new THREE.GridHelper(gridplaneSize/2, gridstep);
gridHelper_xy.position = new THREE.Vector3(0, 0, 0);
gridHelper_xy.setColors( new THREE.Color(gridcolor), new THREE.Color(gridcolor) );
scene.add(gridHelper_xy);
Then I would like to decide if the plane or the grid is visible.
This works for the plane:
planeMesh_xy.visible = false;
But not for the grid:
gridHelper_xy.visible = false; // not working
I also tried workarounds, not working:
gridHelper_xy.material.transparent = true;
gridHelper_xy.material.opacity = 0;
gridHelper_xy.parent.visible = false;
Can anyone tell me how to hide the grid then?
What should happen when you set the visible flag of an object is that all of its children should inherit the flag.
In r71 this should work out of the box (look at issue https://github.com/mrdoob/three.js/issues/1524)
In r65 you would have to traverse your hierarchy setting the flag on all the sub-parts:
object.traverse ( function (child) {
child.visible = false;
}
Related
I'm looking for a way to store and reload the decals that produced in This example ( source-code), so when a user "paints" a new splatter - he could save it and later on - reloads it.
I've tried many ways but unfortunately none of them seems to work.
My approach was to save the position, rotation and scale and then build them like this :
function loadLeePerrySmith(callback) {
var loader = new THREE.JSONLoader();
loader.load('assets/LeePerrySmith.js', function (geometry) {
geometry.verticesNeedUpdate = true;
geometry.elementsNeedUpdate = true;
geometry.morphTargetsNeedUpdate = true;
geometry.uvsNeedUpdate = true;
geometry.normalsNeedUpdate = true;
geometry.colorsNeedUpdate = true;
geometry.tangentsNeedUpdate = true;
var material = new THREE.MeshPhongMaterial({
map: THREE.ImageUtils.loadTexture('assets/Map-COL.jpg'),
specularMap: THREE.ImageUtils.loadTexture('assets/Map-SPEC.jpg'),
normalMap: THREE.ImageUtils.loadTexture('assets/Map-NOR.jpg'),
shininess: 10
});
mesh = new THREE.Mesh(geometry, material);
// ALL FOLLOWING VECTORS SHOULD COME FROM D.B
var pos = new THREE.Vector3(0.18564199509178245, 23.11243036463454, 21.79273328636014);
var rot = new THREE.Vector3(-0.24357513937426453, -0.07708039421506024, 4.358263365975027);
var some = new THREE.Vector3(20.694949486021706, 20.694949486021706, 20.694949486021706);
var check = new THREE.Vector3(1, 1, 1);
var m = new THREE.Mesh(new THREE.DecalGeometry(mesh, pos, rot, some, check), decalMaterial);
scene.add(mesh);
decals.push(m);
scene.add(m);
mesh.scale.set(10, 10, 10);
});
}
Adding a simple timeOut did the trick
setTimeout(function(){
let m = new THREE.Mesh(new DecalGeometry(mesh, pos, rot,
size), decalMateria2);
decals.push(m);
scene.add(m);
},100)
I have a library I'm working on that's supposed to display graphs. It'd be nice to include a way to label the vertices with a parameterized text, but unfortunately, I'm stuck.
My scene consists of boxes and lines. Below is how I render the box, and I'd like to know how to render a short text beneath it.
Graph.prototype.add_vertex = function(options){
var v = new Vertex(this.vertex_id_spawn++, options);
v.paint(this.scene);
this.V[v.id] = v;
return v;
};
As you can see, Vertex relies on paint to draw itself to the screen.
Vertex.prototype.paint = function(scene){
this.object = new Cube(scene, this.options.cube);
scene.add(this.object);
};
and the Cube function:
var Cube = function(scene, options){
if(options === undefined){
options = {};
}
if(options.width === undefined){
options.width = 3;
}
if(options.height === undefined){
options.height = 3;
}
if(options.depth === undefined){
options.depth = 3;
}
if(options.color === undefined){
options.color = 0x00ccaa;
}
if(options.wireframe === undefined){
options.wireframe = false;
}
var geometry, material, material_args;
geometry = new THREE.BoxGeometry(
options.width,
options.height,
options.depth
);
geometry.dynamic = true;
if(options.texture !== undefined){
material_args = {
map: THREE.ImageUtils.loadTexture( options.texture )
};
}else{
material_args = {
color: options.color,
wireframe: true
};
}
material = new THREE.MeshBasicMaterial( material_args );
var cube = new THREE.Mesh( geometry, material );
var scale = 2;
cube.position = new THREE.Vector3(
Math.random() * scale, Math.random() * scale,
Math.random() * scale
);
cube.matrixAutoUpdate = true;
scene.add(cube);
return cube;
};
The full code is available at https://github.com/thwee-alchemist/fourd.js/blob/gh-pages/lib/fourd.js
I've been stuck on the label problem forever, and would welcome any suggestions.
Edit In the meantime, I've tried creating a canvas context filled with text, to get the dataURL, but to no avail. I passed the dataURL to the ImageLoader, to the map parameter of the texture which is what I do with regular vertices.
It seems like this should be easy, but I have spent nearly a week on this trying every possible combination of how to drag multiple items as a group in Three.js. It started out simple, I used this example https://jsfiddle.net/mz7Lv9dt/1/ to get the ball working. I thought I could just add some TextGeometry, which of course had some major API changes in the last couple releases rendering most examples obsolete.
After finally getting it to work as a single line, I wanted to add in wordwrap and move it as a group, but I can't seem to do so.
Here you can see it working just fine with the ball, but you can't drag the text https://jsfiddle.net/ajhalls/h05v48wd/
By swapping around three lines of code (location line 93-99), I can get it to where you can drag the individual lines around, which you can see here: https://jsfiddle.net/ajhalls/t0e2se3x/
function addText(text, fontSize, boundingWidth) {
var wrapArray;
wrapArray = text.wordwrap(10,2);
var loader = new THREE.FontLoader();
loader.load( 'https://cdn.coursesaver.com/three.js-74/examples/fonts/helvetiker_bold.typeface.js',
function ( font ) {
group = new THREE.Group();
group.name = "infoTag";
for (var i = 0; i < wrapArray.length; i++) {
var objectID=i;
var line = wrapArray[objectID];
var textGeo = new THREE.TextGeometry( line, {font: font,size: fontSize,height: 10,curveSegments: 12,bevelThickness: 0.02,bevelSize: 0.05,bevelEnabled: true});
textGeo.computeBoundingBox();
var centerOffset = -0.5 * ( textGeo.boundingBox.max.x - textGeo.boundingBox.min.x );
var textMaterial = new THREE.MeshPhongMaterial( { color: 0xff0000, specular: 0xffffff } );
var mesh = new THREE.Mesh( textGeo, textMaterial );
mesh.position.x = centerOffset +200;
mesh.position.y = i*fontSize*-1+11;
mesh.position.z = 280;
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.geometry.center();
mesh.lookAt(camera.position);
mesh.name = i;
group.add( mesh ); //this should work - comment out and swap for other two lines to see
scene.add(mesh); // this should work- comment out and swap for other two lines to see
//objects.push(mesh);//works on individual lines if you uncomment this
//scene.add(mesh); //works on individual lines if you uncomment this
}
objects.push( group ); // this should work- comment out and swap for other two lines to see
});
}
That "should" be working according to everything I had researched over the last week. I had one moment where it was "working" but because of the size of the group object, the pivot points were wrong, the setLength function didn't work, and it flipped the object away from the camera. All in all it was a mess.
I did try using 2d objects such as canvases and sprites, but for reasons detailed here Three.js TextGeometry Wordwrap - drag as group couldn't get it working.
Please, someone help me!
The issue ended up being with the group. Previously I was creating it, adding objects to it with a position.z which increased the size of the box around the group, then after doing that I moved the box to in front of the camera and did a group.lookAt which meant that when I was dragging it everything including pivot point and looking at it from the back was wrong. The right way was to create the group, position it, face the camera, then add the text.
function addText(text, fontSize, wrapWidth, tagColor, positionX, positionY, positionZ) {
var wrapArray;
wrapArray = text.wordwrap(wrapWidth,2);
var loader = new THREE.FontLoader();
loader.load( '/js/fonts/helvetiker_bold.typeface.js', function ( font ) {
group = new THREE.Group();
group.position.x=positionX;
group.position.y=positionY;
group.position.z=positionZ;
group.lookAt(camera.position);
group.tourType = "infoTag";
group.name = "infoTag-" + objects.length;
group.dataID=objects.length;
group.textData=text;
for (var i = 0; i < wrapArray.length; i++) {
var objectID=i;
var line = wrapArray[objectID];
var textGeo = new THREE.TextGeometry( line, {
font: font,
size: fontSize,
height: 1,
curveSegments: 12,
bevelThickness: 0.02,
bevelSize: 0.05,
bevelEnabled: true
});
textGeo.computeBoundingBox();
var centerOffset = -0.5 * ( textGeo.boundingBox.max.x - textGeo.boundingBox.min.x );
var textMaterial = new THREE.MeshPhongMaterial( { color: tagColor, specular: 0xffffff } );
var mesh = new THREE.Mesh( textGeo, textMaterial );
mesh.dataID=objects.length;
mesh.position.x = 0;
mesh.position.y = (i*mesh.geometry.boundingBox.max.y*-1)*1.15;
mesh.position.z = 0;
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.geometry.center();
//mesh.lookAt(camera.position);
mesh.name = "infoTag-mesh-" + objects.length;
group.add( mesh );
}
scene.add(group);
objects.push( group );
});
}
Of course there were some changes to be made in the mouse events to take into account that you want to move the parent of the object, which looks something like this:
function onDocumentMouseDown(event) {
event.preventDefault();
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(objects, true);
if (intersects.length > 0) {
if (intersects[0].object.parent.tourType == 'infoTag') {
var manipulatingInfoTag = true;
SELECTED = intersects[0].object.parent;
}else{
SELECTED = intersects[0].object;
}
var intersects = raycaster.intersectObject(plane);
if (intersects.length > 0) {
offset.copy(intersects[0].point).sub(plane.position);
}
container.style.cursor = 'move';
}
isUserInteracting = true;
onPointerDownPointerX = event.clientX; onPointerDownPointerY = event.clientY; onPointerDownLon = lon; onPointerDownLat = lat;
}
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
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);