Three.js marker on mesh - three.js

I'm working on a visualization that uses a height map to create a 3d terrain, and want to add some markers for locations of cities on the map. I'm guessing some sort of raycaster and intersectObject might do it, but I'm lost as to how to place a marker directly on the surface of the terrain. For instance, I know that a city is at (640, 480) in the pixel units of my texture, so I'm doing something like:
// This is the terrain, loaded from a PNG of the digital elevation model
var planeGeo = new THREE.PlaneGeometry( bumpTexture.image.width, bumpTexture.image.height, 600, 450 );
var plane = new THREE.Mesh( planeGeo, customMaterial );
plane.rotation.x = -Math.PI / 2;
plane.position.y = 0;
scene.add( plane );
// At (640, 480) of the image, there should be a marker directly on top of the mesh.
var cube = new THREE.Mesh( new THREE.CubeGeometry( 20, 20, 20 ), new THREE.MeshNormalMaterial() );
cube.position.x = 640;
cube.position.y = 0;
cube.position.z = 480;
scene.add(cube);
raycaster.setFromCamera(cube, camera );
var intersects = raycaster.intersectObject(plane);
console.log(raycaster, intersects);
// no intersects???
Any suggestions, Threejs wizards?

Related

Get center position of hole in ExtrudeGeometry

I trying to get position of hole in extruded geometry. I created plane and made hole in her geometry. I want get x,y,z coordinates in center of hole. Is there some methods to get it?
Here demo: https://codepen.io/DYDOI-NSK/pen/XWqJzXG?editors=0011
Here code:
I created shape of plane
let shape = new THREE.Shape();
let width = 30;
let height = 30;
shape.moveTo(-width, height);
shape.lineTo(-width, -height);
shape.lineTo(width, -height);
shape.lineTo(width, height);
shape.lineTo(-width, height);
I created hole path and add it to shape
let hole = new THREE.Path();
hole.absarc(20, 10, 10, 0, Math.PI * 2, false) //first two argumets is x,y coord of hole
shape.holes.push(hole)
I created plane add add extruded geometry
let geometry = new THREE.PlaneGeometry( 30, 30);
let material = new THREE.MeshBasicMaterial( {color: new THREE.Color('#cea6a6'), side: THREE.DoubleSide} );
let mesh = new THREE.Mesh( geometry, material );
let newGeometry = new THREE.ExtrudeGeometry(shape, settings);
mesh.geometry.dispose()
mesh.geometry = newGeometry;
After 4 days I understand how can do it. I simple created line from mesh center to hole config position. Applied quaternion to line and got x,y,z cords of hole.
Maybe there are more optimized solutions, but it only that I could create. I will be glad if someone share more optimized solution :D
Here codepen: https://codepen.io/DYDOI-NSK/pen/XWqJzXG?editors=0011
Here code:
/*
* findCoords - function to find hole coords in 3d space
* data - parameter that require x,y of hole
*/
let findCoords = function (data) {
let vertexes = [];
//Set coords where you was created hole
let hole = new THREE.Vector3(
data.x,
data.y,
0
);
vertexes.push( new THREE.Vector3() );
vertexes.push(hole)
//Create line
const material = new THREE.LineBasicMaterial({
color: 0x0000ff
});
const geometry = new THREE.BufferGeometry().setFromPoints( vertexes );
const line = new THREE.Line( geometry, material );
scene.add(line)
//Set line to center of mesh
line.position.copy(mesh.position)
//Rotate line like rotated mesh
line.applyQuaternion(mesh.quaternion)
//Extract hole coords from second vertex of line
let holeCoord = new THREE.Vector3()
const positionAttribute = line.geometry.getAttribute( 'position' );
holeCoord.fromBufferAttribute( positionAttribute, 1);
return holeCoord;
}

Directional light shadow in mapbox

I have a custom mesh geometry (three js) in mapbocx. I am trying to create a light for casting directional shadows but I always end up woth the light source in the base plane (which results in no casted shadows on my objects above the plane). Does anyone know how I can move the light source so it is above the plane? I added a helper to see the scope box and I would like to move it upwards along the z-vector in the image below.
//Create a WebGLRenderer and turn on shadows in the renderer
const renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap
//Add Ambient light
const amblight = new THREE.AmbientLight(0xffffff, 0.8);
amblight.position.set(8, 10, 5); //default; light shining from top
scene.add(amblight);
//Create a DirectionalLight and turn on shadows for the light
const light = new THREE.DirectionalLight(0xffffff, 0.5);
//light.position.set(8, 10, 5); //default; light shining from top
light.position.y = 2000;
light.position.x = 10;
light.position.z = 5;
light.castShadow = true; // default false
scene.add(light);
//scene.add(light.target);
//Set up shadow properties for the light
light.shadow.mapSize.width = 512;
light.shadow.mapSize.height = 512;
light.shadow.camera.left = -100;
light.shadow.camera.right = 100;
light.shadow.camera.top = 100;
light.shadow.camera.bottom = -100;
light.shadow.camera.near = 0.5;
light.shadow.camera.far = 100; //Scope box depth
//Create a plane that receives shadows (but does not cast them)
const planeGeometry = new THREE.PlaneGeometry(1000, 1000, 10, 10);
const planeMaterial = new THREE.MeshPhongMaterial({
color: 0x808080,
opacity: 0.8,
transparent: true,
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
scene.add(plane);
const meshString = result.mesh.meshString;
const mesh = meshToThreejs(rhino, meshString, THREE);
//scene.add(mesh);
//Add shadows
mesh.castShadow = true; //default is false
mesh.receiveShadow = true; //default
scene.add(mesh);
//ENd shadows
//Create a helper for the shadow camera (optional)
const helper = new THREE.CameraHelper(light.shadow.camera);
scene.add(helper);
"move the light source so it is above the plane" - It looks like you already know how to do this, just change the z number.
light.position.z = 20;
// or
light.position.set(0, 0, 20);
// Check note below - If y is up
light.position.y = 20;
// or
light.position.set(0, 20, 0);
Just a note, by default Y is up in Three.js unless you have already handled that in code not shown here. If you need to check this add the axesHelper to your scene. The X axis is red. The Y axis is green. The Z axis is blue. Make sure the camera is moved in the correct direction.
const axesHelper = new THREE.AxesHelper( 100 );
scene.add( axesHelper );
If you are still not getting shadows you could try to add a sphere like in the Three.js docs (https://threejs.org/docs/#api/en/lights/shadows/DirectionalLightShadow)
//Create a sphere that cast shadows (but does not receive them)
const sphereGeometry = new THREE.SphereGeometry( 5, 32, 32 );
const sphereMaterial = new THREE.MeshStandardMaterial( { color: 0xff0000 } );
const sphere = new THREE.Mesh( sphereGeometry, sphereMaterial );
sphere.castShadow = true; //default is false
sphere.receiveShadow = false; //default
scene.add( sphere );
If that is casting a shadow correctly then perhaps there is an issue with your mesh, or the height of those buildings is so small that the shadows are really small

how to get the boundingSphere for a whole scene in three.js?

How to get the bounding sphere for a whole scene in three.js?
I may try to get the bounding sphere for each object and compute the resulting union of them, but I think there may be a more straight forward method.
There are different methods to get a boundingSphere of multiple objects dynamically. You can get first the bounding box of all of them, and then create a sphere of that bounding box... here is a sample fiddle I have shaped on boundingSphere of a boundingBox.
Basically you put all the geometries into a Group, you get the Box3 of the group, and then you do getBoundingSphere from the Box3 and position at the center. Code in the fiddle would be this.
let g = new THREE.Group();
scene.add(g);
for (let i = 0; i < 5; i++) {
// geometry
var geometry = new THREE.BoxGeometry(20, 20, 20);
// material
var material = new THREE.MeshToonMaterial({
color: 0xff0000,
opacity: 0.7,
});
// mesh
mesh = new THREE.Mesh(geometry, material);
mesh.position.set(100 * Math.random(), 100 * Math.random(), 100 * Math.random());
g.add(mesh);
}
//g.updateWorldMatrix(true);
var gridHelper = new THREE.GridHelper(400, 40, 0x0000ff, 0x808080);
gridHelper.position.y = 0;
gridHelper.position.x = 0;
scene.add(gridHelper);
let bbox = new THREE.Box3().setFromObject(g);
let helper = new THREE.Box3Helper(bbox, new THREE.Color(0, 255, 0));
scene.add(helper);
const center = new THREE.Vector3();
bbox.getCenter(center);
let bsphere = bbox.getBoundingSphere(new THREE.Sphere(center));
let m = new THREE.MeshStandardMaterial({
color: 0xffffff,
opacity: 0.3,
transparent: true
});
var geometry = new THREE.SphereGeometry(bsphere.radius, 32, 32);
let sMesh = new THREE.Mesh(geometry, m);
scene.add(sMesh);
sMesh.position.copy(center);
EDITED: If you want to include in the boundingSphere for the scene including the lights (which could get you a huge sphere), just start from let bbox = new THREE.Box3().setFromObject(scene)

Isometric cube, size does not match up with tilesize 128x128

I've been trying for bigger parts of the night to make a export code that quickly will let me texture cubes and export them to a game i'm making, but for some reason I can't make my cube to cover the entire 128x128 width and height that I want it to have.
I have the following code:
function init(){
if( Detector.webgl ){
renderer = new THREE.WebGLRenderer({
antialias : false, // to get smoother output
preserveDrawingBuffer : true // to allow screenshot
});
renderer.setClearColorHex( 0xBBBBBB, 1 );
// uncomment if webgl is required
//}else{
// Detector.addGetWebGLMessage();
// return true;
}else{
renderer = new THREE.CanvasRenderer();
}
renderer.setSize(128,128);
document.getElementById('container').appendChild(renderer.domElement);
// add Stats.js - https://github.com/mrdoob/stats.js
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.bottom = '0px';
document.body.appendChild( stats.domElement );
var zoom = 1.0;
// create a scene
scene = new THREE.Scene();
// put a camera in the scene
camera = new THREE.OrthographicCamera(WIDTH / -zoom, HEIGHT / zoom, WIDTH / zoom, HEIGHT / -zoom, -2000, 1000);
//camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set(0.45,0.45,0.45);
camera.lookAt(scene.position);
//camera.position.set(0, 0, 5);
scene.add(camera);
// create a camera contol
//cameraControls = new THREEx.DragPanControls(camera)
// transparently support window resize
THREEx.WindowResize.bind(renderer, camera);
// allow 'p' to make screenshot
THREEx.Screenshot.bindKey(renderer);
// allow 'f' to go fullscreen where this feature is supported
//if( THREEx.FullScreen.available() ){
// THREEx.FullScreen.bindKey();
// document.getElementById('inlineDoc').innerHTML += "- <i>f</i> for fullscreen";
//}
// here you add your objects
// - you will most likely replace this part by your own
//var geometry = new THREE.TorusGeometry( 1, 0.42 );
var cubeSize = 128;
var geometry = new THREE.CubeGeometry( cubeSize, cubeSize, cubeSize );
var material = new THREE.MeshNormalMaterial();
mesh= new THREE.Mesh( geometry, material );
mesh.rotation.x = 0;
mesh.rotation.y = 0;
mesh.rotation.z = 0;
scene.add( mesh );
}
I've been trying out different "zooms" but it still ends up either too big or too small.
The point with all this is to end up with a code that can generate something like this:
https://dl.dropboxusercontent.com/u/5256694/cube_ex.png
What am I doing wrong?
Kind Regards
Hiam
Instead of thinking about the parameters of THREE.OrthographicCamera as "zoom" levels, you should think of them as coordinates of boundary planes for what the camera is able to see.
Also see the answer to Three.js - Orthographic camera for more details about using orthographic cameras in Three.js

Three.js Rotate Texture

I have a texture applied to a mesh I can change the offset with
mesh.material.map.offset.set
I can change the scaling with
mesh.material.repeat.set
so my question is, how can I rotate texture inside a plane?
Example:
From This:
To this
Thanks.
use 2D canvas as a texture
demo:
https://dl.dropboxusercontent.com/u/1236764/temp/stackoverflow_20130525/index.html
example code
var camera, scene, renderer, mesh;
var width = window.innerWidth;
var height = window.innerHeight;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 30, width / height, 1, 1000 );
camera.position.z = 100;
renderer = new THREE.WebGLRenderer();
renderer.setSize( width, height );
document.body.appendChild( renderer.domElement );
var img = new Image();
img.onload = createMeshThenRender;
img.src = 'img.jpg';
function createMeshThenRender () {
var imgWidth = imgHeight = 256;
var mapCanvas = document.createElement( 'canvas' );
mapCanvas.width = mapCanvas.height = 256;
// document.body.appendChild( mapCanvas );
var ctx = mapCanvas.getContext( '2d' );
ctx.translate( imgWidth / 2, imgHeight / 2 );
ctx.rotate( Math.PI / 4 );
ctx.translate( -imgWidth / 2, -imgHeight / 2 );
ctx.drawImage( img, 0, 0, imgWidth, imgHeight );
var texture = new THREE.Texture( mapCanvas );
texture.needsUpdate = true;
mesh = new THREE.Mesh(
new THREE.PlaneGeometry( 50, 50, 1, 1 ),
new THREE.MeshBasicMaterial( {
map : texture
} )
);
scene.add( mesh );
renderer.render( scene, camera );
}
three.js does not have a UV editing utility, so you either have to edit the geometry.faceVertexUvs manually, or rotate your image in an image editing program. I'd suggest the latter.
three.js r.58
three.js r85
For those looking to actually "rotate UVs" on a Plane sitting in XY plane (default plane) using a ShapeBufferGeometry or PlaneBufferGeometry.
var planeGeo = new THREE.PlaneBufferGeometry(24,24);
var planeMesh = new THREE.Mesh(planeGeo, new THREE.MeshBasicMaterial({map: yourTexture}));
scene.add(planeMesh);
rotateUVonPlanarBufferGeometry(45, planeMesh);
function rotateUVonPlanarBufferGeometry(rotateInDeg, mesh){
if(rotateInDeg != undefined && mesh){
var degreeInRad = THREE.Math.degToRad(rotateInDeg);
var tempGeo = mesh.geometry.clone();
var geo;
if(tempGeo instanceof THREE.BufferGeometry){
geo = new THREE.Geometry().fromBufferGeometry(tempGeo);
}else{
console.log('regular geometry currently not supported in this method, but can be if code is modified, so use a buffer geometry');
return;
}
// rotate the geo on Z-axis
// which will rotate the vertices accordingly
geo.applyMatrix(new THREE.Matrix4().makeRotationZ(degreeInRad));
// loop through the vertices which should now have been rotated
// change the values of UVs based on the new rotated vertices
var index = 0;
geo.vertices.forEach(function(v){
mesh.geometry.attributes.uv.setXY( index, v.x, v.y );
index++;
});
mesh.geometry.attributes.uv.needsUpdate = true;
}
}
three.js r121
In the newer version of the three.js, you can directly set rotation and rotation center of texture.
var texture = new THREE.Texture( ... );
texture.rotation = Math.PI/4;
texture.center = new Vector2d(0.5, 0.5); // center of texture.
The above solutions were not good to me, a little bit old. That's simple solution worked for me (Three.js 125)
imgData = canvasCtx.getImageData(0, 0, canvasElement.width, canvasElement.height);
texture = new THREE.DataTexture( imgData.data, canvasElement.width, canvasElement.height, THREE.SRGB );
texture.rotation = Math.PI;
texture.center = new THREE.Vector2(0.5, 0.5); // center of texture.
mymaterial = new THREE.MeshBasicMaterial({
map: texture,
alphaTest: 0.5,
transparent: true,
side: THREE.DoubleSide,
});

Resources