How do you inherit the world location of a mesh? - three.js

I have a Three.js Scene with several meshes moving around. I want to take a snap shot and copy all of the mesh locations by placing a new instance of a mesh (with the same geometry) in the scene in the same location & with the same rotation. I can't simply copy the mesh's .position & .rotation because the mesh is a child to other meshes. I tried to .clone() the mesh's matrixWorld but that didn't work. How do you inherit the world location of a mesh?
This is what I am trying to do:
// mesh is an existing mesh loaded into a scene
// geom is an existing geometry definition
var material = new THREE.MeshFaceMaterial();
var newMesh = new THREE.Mesh( geom, material);
newMesh.matrixWorld = mesh.matrixWorld.clone();
scene.add(newMesh);
Any help would be much appreciated.

It's as simple as this:
newMesh.position.copy( mesh.matrixWorld.getPosition() );
EDIT: See this updated answer, instead.

Related

threejs render text on or next to object

How can I put a text next or in front of it or on its surface with three.js?
If I plot a object like a box, I'd like to give this object a visible name (so similar to tooltip, but always staying visible). Would be great if text size is adjusting to distance like object size.
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );
I'm using three.js with react-force-graph to render a diagram. Objects within it are rendered with three.js
https://threejs.org/docs/#api/en/geometries/BoxGeometry.parameters
Two ways to draw text with three.js. First, create a div element, set its position (In your case, you need to write code to calculate the position relative to the canvas according to the position of camera and 3d object). Second, create a text texture.

Rendering shadows from plane with partially transparent texture in three.js

I'm trying to render shadows using the latest version of three.js (r102), and I'm not sure what I am doing wrong. I am using MeshPhongMaterial with castShadow and receiveShadow set on all relevant meshes, and a directional light facing towards the scene content. Could someone take a look at this and help me figure out how to get these shadows working? Thanks!
Live demo (toggle shadows in the menu):
https://argonjs.github.io/three-web-layer/
Source:
https://github.com/argonjs/three-web-layer
If you add a small cube in front of your WebLayer3D, it correctly casts shadows on rendered DOM layers:
//in app.ts just after light with shadow camera:
let geometryBox = new THREE.BoxBufferGeometry( 0.01, 0.01, 0.01 )
let materialRed = new THREE.MeshPhongMaterial( {color: 0xff0000} )
let cubeSmall = new THREE.Mesh( geometryBox, materialRed )
cubeSmall.position.set( 0.1, -0.03, 0.1 )
cubeSmall.castShadow = true
cubeSmall.receiveShadow = true
scene.add( cubeSmall )
So, only the planes produced by the WebLayer3D do not cast shadows, the setup for the scene / camera / light is correct.
Update: the explanation below is not the reason, see the solution with material.shadowSide in another answer.
If you look at the tree of objects in three.js realm (i.e. traversing through children[]), starting with todoLayer - a lot of them will have castShadow at "false". You will have to re-think your strategy here. Also note, castShadow=false on parent Object3D turns it off for the children.
I figured it out after the hint from Alex (thanks Alex!).
Basically, as strange as it seems, a plane in three.js will not cast shadows unless it is double-sided (Update: Or unless material.shadowSide is set to THREE.FrontSide). Once I set THREE.DoubleSide on the plane material, it worked as expected. Basically, for a textured plane to cast shadows, the following is needed (as of three.js r102):
var mesh = new THREE.Mesh(
new THREE.PlaneGeometry(1,1,2,2),
new THREE.MeshPhongMaterial({
map: texture,
side: THREE.DoubleSide, // important!
alphaTest: 0.1,
})
mesh.customDepthMaterial = new THREE.MeshDepthMaterial({
map: texture
depthPacking: THREE.RGBADepthPacking,
alphaTest: 0.1
})
I also had to adjust the light's shadow bias in order to eliminate artifacts.

Self reflection in three.js

How to make a part of object visible in reflection on another part of the same object?
Threejs is not a ray tracing engine. Therefore it is not using real ray casts to calculate reflections on surfaces. It actually renders the other objects onto your reflective object.
What your scene is missing is an infinity mirror effect between surfaces, where light rays can bounce between two reflective surfaces multiple times. This is what a ray tracing engine would do.
You can try to fake this. Just split your geometry of into multiple objects and use extra cameras for them to render the scene looking from your reflective object into the scene and using this as a texture for the object.
You can use this example as a starting point: https://threejs.org/examples/#webgl_materials_cubemap_dynamic
Here an example of a material with dynamic environment texture captured by a CubeCamera:
var near = 1
var far = 100
var cubeResolution = 128
var camera1 = new THREE.CubeCamera( near, far, cubeResolution);
camera1.renderTarget.texture.generateMipmaps = true;
camera1.renderTarget.texture.minFilter = THREE.LinearMipMapLinearFilter;
scene.add( camera1 );
var material = new THREE.MeshBasicMaterial( { envMap: camera1.renderTarget.texture } );
var cube = new THREE.Mesh( new THREE.BoxBufferGeometry( 20, 20, 20 ), material );
scene.add(cube)

threejs - creating 3d perspective for a line

I'm working on an app where I visualize ATV trails in a 3d perspective (NAIP imagery draped over elevation data). I am using three.js for the rendering engine.
In the above image, the white line you see is just a THREE.Line instance, where I convert a trails gps coordinates into threejs coordinates. I'd like to add more of 3d perspective to this line. I tried implementing a THREE.TubeGeometry where the path was a THREE.CatmullRomCurve3 using the same Vector3 points as how I built the line you see in the image above. That did not produce a desirable result...
From the many, many THREE examples I have looked at, I really think an extruded geometry would achieve the look I am after... But I cant for the life of me figure out how to extrude a geometry for the line. Any suggestions/thoughts?
UPDATE 1:
Here is my desired look (same trail - no imagery). This image was produced in QGIS using the Q2Threejs plugin
UPDATE 2: Here is a code of how I have attempted to create a tubegeometry. Maybe I am messing something up in there...
// trailVectors are an array of Vector3 - same as ones used to create line
var trailCurve = new THREE.CatmullRomCurve3(trailVectors);
var tubeGeometry = new THREE.TubeGeometry(trailCurve,80,1,15,false);
var material = new THREE.MeshBasicMaterial({color:0x00ff00});
var tubeMesh = new THREE.Mesh(tubeGeometry,material);
var wireframeMaterial = new THREE.LineBasicMaterial({color:0xffffff,lineWidth:2});
var wireframe = new THREE.Mesh(tubeGeometry,wireframeMaterial);
tubeMesh.add(wireframe);
scene.add(tubeMesh);
UPDATE 3
THREE.TubeGeometry(trailCurve,80,4,2,false) per mzartman request
I think that you should be able to achieve what you want with a TubeGeometry. I think the big thing is that your example (from the picture shown) has more than 2 radius segments. That gives it the tubular shape and makes it look sort of like a blob. If you set the radial segment count to 2 (as it's shown below) then I think it would look a lot better.
tubeGeometry = new THREE.TubeBufferGeometry(
[YOUR_PATH_HERE],
params.extrusionSegments, // <--- Edit this for higher resolution on the spline
3, // <--- This defines the height
2, // <--- This 2 keeps 2D (i.e. not a tube!!!!)
true );
var mesh = new THREE.Mesh( geometry, material );
var wireframe = new THREE.Mesh( geometry, wireframeMaterial );
mesh.add( wireframe );
scene.add( mesh );
Update:
I think that you might do better with a material that shows some shadow like the MeshPhong. Also, to do the wireframe you want to add it as an option in the material initialization. Give it a show with the following:
var tubeGeometry = new THREE.TubeGeometry(curve,80,1,2,false);
var material = new THREE.MeshPhongMaterial({color:0x00ff00, wireframe: true});
var tubeMesh = new THREE.Mesh(tubeGeometry,material);
scene.add(tubeMesh);

three.js share a geometry for lines and particles

I would like to use one geometry object for some fancy particle animation and display lines between those particles.
geometry = new THREE.Geometry();
particles = new THREE.ParticleSystem(geometry, particleMaterial);
line = new THREE.Line(geometry, lineMaterial, THREE.LinePieces);
Somehow the Line Object is not rendered in the scene, but the particles show up.
http://jsfiddle.net/Pk85y/1/
With WebGLRenderer you can't share geometry in some specific situations. Best option is to do geometry.clone().
Here's the jsfiddle fixed: http://jsfiddle.net/Pk85y/3/

Resources