babylonjs: multi-shadows for multi-elements not working - javascript

Im trying to get the shadows right using Babylonjs. Without any joy :p
Here is the resources on shadows I have found
Babylonjs wiki: Shadows
framework for building 3D games with HTML5 and WebGL
but I can't find anything on "element on element" shadows. :(
here is my attempt at it:
my sources loosely based on Babylonjs wiki: 17-Shadows
I have 2 lights and 3 objects
I get a shadow behind the sphere but then I also get an artefact on the front face of the sphere.
► Live code:
jsfiddle.net/codemeasandwich/z64Ba
I appreciate your help as I've been struggling with this for a while.
function createSceneTuto(engine) {
var scene = new BABYLON.Scene(engine);
//freeCamera is a FPS like camera where you control the camera with the cursors keys and the mouse
//touchCamera is a camera controlled with touch events (it requireshand.jsto work)
//arcRotateCamera is a camera that rotates around a given pivot. It can be controlled with the mouse or touch events (and it also requires hand.js to work)
// ArcRotateCamera >> Camera turning around a 3D point (here Vector zero)
// Parameters : name, alpha, beta, radius, target, scene
var camera = new BABYLON.ArcRotateCamera("Camera", 0, 0, 90, BABYLON.Vector3.Zero(), scene);
camera.setPosition(new BABYLON.Vector3(30, 30, 30));
// pointLight (like the sun for instance) which emits lights in every direction from a specific position
// directionalLight which emits lights from the infinite towards a specific direction
var light = new BABYLON.DirectionalLight("Dir0", new BABYLON.Vector3( -1,0, 0), scene);
var light0 = new BABYLON.PointLight("Omni0", new BABYLON.Vector3(1, 10, 100), scene);
light0.diffuse = new BABYLON.Color3( 0,1, 0);
light0.specular = new BABYLON.Color3(1, 1, 1);
var box = BABYLON.Mesh.CreateBox("Box", 3, scene);
var torus = BABYLON.Mesh.CreateTorus("torus", 5, 1, 20, scene);
// var plan = BABYLON.Mesh.CreatePlane("Plane", 50.0, scene);
// plan.position.z = -40
var sphere = BABYLON.Mesh.CreateSphere("Sphere", 15, 20, scene);
// Shadows
var shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
var shadowGenerator0 = new BABYLON.ShadowGenerator(1024, light0);
shadowGenerator.getShadowMap().renderList.push(box);
shadowGenerator.getShadowMap().renderList.push(torus);
shadowGenerator.getShadowMap().renderList.push(sphere);
shadowGenerator0.getShadowMap().renderList.push(box);
shadowGenerator0.getShadowMap().renderList.push(torus);
shadowGenerator0.getShadowMap().renderList.push(sphere);
box.receiveShadows = true;
torus.receiveShadows = true;
sphere.receiveShadows = true;
var alphaTorus = 0, alphaBox =0;
scene.registerBeforeRender(function () {
torus.rotation.x += 0.02;
torus.position = new BABYLON.Vector3(Math.cos(alphaTorus) * 15, 0, Math.sin(alphaTorus) * 15);
alphaTorus += 0.003;
box.position = new BABYLON.Vector3(Math.cos(alphaBox) * 15, 0, Math.sin(alphaBox) * 15);
alphaBox += 0.01;
});
return scene;
}
► The above lights as directional lights
var light = new BABYLON.DirectionalLight("Dir0", new BABYLON.Vector3( -1,0, 0), scene);
light.position = new BABYLON.Vector3(0, 0, 20);
light.intensity = 0.5;
var light0 = new BABYLON.DirectionalLight("Omni0", new BABYLON.Vector3(0,0,-1), scene);
light0.position = new BABYLON.Vector3(25, 0, 0);
light.intensity = 0.5;

Only directional lights can cast shadows and they also need a position to define from where the shadows come
I update the wiki to add this IMPORTANT information :)
Only directional ligths can cast shadows:
var light = new BABYLON.DirectionalLight("dir01", new BABYLON.Vector3(-1, -2, -1), scene);
You must also define a position for your light (because Babylon.js must define a point of view to > create the shadow map):
light.position = new BABYLON.Vector3(20, 40, 20);
Please note that you should have to move the position to define the area where the shadows are seen.

Related

Distorted mesh when rotating group in Three.JS

Bit of context, I have two skinned meshes with the same bone structure that over lap each other. The difference between these two meshes is what they have attached to them and where. For the purpose of this question I will be referring to the body and hand skinned meshes.
I am trying to rotate both the body forearm and hand forearm at the same time and in the same direction. I have added both to the group leftforearmgroup which is a child group. The group structure present is bodygroup > leftclaviclegroup > leftarmgroup > leftforearmgroup.
The issue I am running into is when I attempt to rotate the leftforearmgroup it distorts as shown in the image below. The other image shows how it should behave, this is achieved by attaching the control directly to the body forearm which won't control the hand forearm sadly. Every other group when rotated via TransformControl behaves as expected.
distorted mesh when rotating
correct mesh behavior
So my question is how do I fix the mesh from being distorted when rotating?
Code:
const headaccgroup = new THREE.Group();
headaccgroup.position.set(0, -15, 0);
headaccgroup.rotateY(THREE.Math.degToRad(180));
const headgroup = new THREE.Group();
headgroup.scale.set(1, 1, 1);
headgroup.position.set(0, 0.38, 0.028);
headgroup.rotateY(THREE.Math.degToRad(0));
const bodygroup = new THREE.Group();
bodygroup.rotateY(THREE.Math.degToRad(180));
const leftclavgroup = new THREE.Group();
leftclavgroup.scale.set(1, 1, 1);
leftclavgroup.position.set(-0.055, -0.055, 40);
leftclavgroup.rotateY(THREE.Math.degToRad(0));
const leftarmgroup = new THREE.Group();
leftarmgroup.scale.set(1, 1, 1);
leftarmgroup.position.set(-0.055, -0.055, 0);
leftarmgroup.rotateY(THREE.Math.degToRad(180));
const leftforearmgroup = new THREE.Group();
leftforearmgroup.scale.set(1, 1, 1);
leftforearmgroup.position.set(0,0,0);
function loadBody(modelPath, x = 0.1, y = 0.1, z = 0.1) {
gltf_loader.load(modelPath, function (gltf) {
scene.remove(body);
body = gltf.scene;
gltf.scene.traverse(function(object) {
if(object.isMesh) object.frustumCulled = false;
});
gltf.scene.scale.set(x, y, z);
body.position.set( 0, -2, 20);
leftforearmgroup.add( body.getObjectByName('bip02_l_forearm'));
controlleftforearm.attach(leftforearmgroup);
controlleftforearm.setMode('rotate');
scene.add(controlleftforearm);
leftarmgroup.add(body.getObjectByName('bip02_l_upperarm'));
leftclavgroup.add(body.getObjectByName('bip02_l_clavicle'));
leftarmgroup.add(leftforearmgroup);
leftclavgroup.add(leftarmgroup);
bodygroup.add(leftclavgroup);
headgroup.add(body.getObjectByName('bip02_head'));
bodygroup.scale.set( 10, 10, 10);
bodygroup.add(body.getObjectByName('bip02_spine1'));
bodygroup.add(headgroup);
scene.add(body);
controlspine1.attach(bodygroup);
controlspine1.setMode('rotate');
scene.add(controlspine1);
});
}

Moving Camera from Point A to Point B On the Geometry Created Autodesk Viewer using three js

I am trying move my camera view state from point A to Point B.
For that I am creating a path using LineDashedMaterial in autodesk viewer and I am able to create and show the line from Point A to B.
here is the code
geometry = new THREE.Geometry();
geometry.vertices.push(getBoundingBox([2112]).center(),
getBoundingBox([2109]).center());
material = new THREE.LineDashedMaterial({color: 0xFF0000,
dashSize: 3,
gapSize: 1,
transparent: true,
depthWrite: false,
depthTest: true,
});
checkLineDistance = geometry.computeLineDistances();
geometry.lineDistancesNeedUpdate = true;
NOP_VIEWER.impl.matman().addMaterial('material', material, true);
line= new THREE.Line(geometry, material);
NOP_VIEWER.impl.sceneAfter.skipDepthTarget = true;
NOP_VIEWER.impl.sceneAfter.skipIdTarget = true;
NOP_VIEWER.impl.sceneAfter.add(line);
which has resulted me with:
Now I am stuck with issue where I want to navigate or move my camera as per the direction of my line/path(A to B).
I am using a sample model right now consider this would be a building which have a room A and B.
2.Is there any way I can get all the vectors from where the line is passing I basically need position, target,and upVector for my camera movement
3.Is there any way I can get all the vectors or points from A to B using dbid in forge viewer api
4.I tried putting up Three.js with forge viewer but it seems difficult though.
This is what actually I am trying to achieve but instead of that moving geometry I need to show viewport in forge viewer
The navigation system in Forge Viewer supports a smooth, linear interpolation into custom camera state using the setRequestTransition method, for example, like so:
let newCameraPosition = new THREE.Vector3(1.0, 2.0, 3.0);
let newCameraTarget = new THREE.Vector3(4.0, 5.0, 6.0);
let fov = viewer.navigation.getHorizontalFov();
viewer.navigation.setRequestTransition(true, newCameraPosition, newCameraTarget, fov);
The default duration of this transition is 1 second, so if you had a list of vertices of a path that you would like to follow with the camera, you could do it with something like this:
function followPath(viewer, vertices, delayMs) {
let index = 0;
let timer = setInterval(function () {
if (index >= vertices.length) {
clearInterval(timer);
return;
}
let newPos = vertices[index];
let newTarget = vertices[index + 1];
if (!newTarget) {
let oldPos = viewer.navigation.getPosition();
let oldTarget = viewer.navigation.getTarget();
newTarget = new THREE.Vector3(
newPos.x + (oldTarget.x - oldPos.x),
newPos.y + (oldTarget.y - oldPos.y),
newPos.z + (oldTarget.z - oldPos.z)
);
}
viewer.navigation.setRequestTransition(true, newPos, newTarget, viewer.navigation.getHorizontalFov());
index++;
}, delayMs);
}
followPath(viewer, [
new THREE.Vector3(10, 20, 30),
new THREE.Vector3(40, 50, 60),
new THREE.Vector3(70, 80, 90),
new THREE.Vector3(0, 10, 0)
], 2000);
If you need more flexibility (e.g., interpolating around curves, custom ease-in/ease-out, etc.), you can still use three.js or other animation libraries, and simply control the camera position and target yourself.

Three.js - Rotate camera separately from it's parent

I have a scene in which an Object moves around a CatmullRomCurve3 path, going left when you press the left or down arrow keys and right when you press the right or up arrow keys.
I initially had a problem in which I wanted the Object to face 90 degrees from the path instead of facing along the path. You can find my code for this scene there. This problem was solved with this answer.
Basically, all I had to do was implement the following code to rotate the object:
object.lookAt((p2).sub(p1).applyAxisAngle(axis, -Math.PI * 0.5).add(p1));
Although the object now rotated correctly, I now had the problem that the camera wasn't facing the same direction as the object or moving with the object (I wanted an over the shoulder type perspective of the object) but someone suggested I fix this by just adding the camera as a child of the object, which worked!
The New Problem
I now want the object's default position to be facing 90 degrees away from the path (as it is), but when you press the left or down arrows, I want the object to face left (back along the path as it was originally), and face the other way when the right and up arrows are pressed.
I managed to get this to work by setting up some if statements in the render loop that change the value of -Math.PI * 0.5 to either -Math.PI * 2 or -Math.PI * 0.5 for left and right depending on which key is pressed.
However, this also causes the camera to take that new lookAt value as it is a child of the object, but I want the camera to always stay in the same position (i.e. facing 90 degrees from the path, as the object does when it is still).
So, how can I stop the camera from rotating/moving/changing it's lookAt (I'm not sure which one it is) when the Object changes?
It seems like nothing I do to the camera affects it, it is always locked to the same orientation of the object.
Thanks in advance.
I hope I've understood your question correctly:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 20, -20);
camera.lookAt(0, 0, 0);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var grid = new THREE.GridHelper(300, 30);
grid.position.setZ(100);
scene.add(grid);
var points = [
new THREE.Vector3(-80, 5, 35),
new THREE.Vector3(-80, 5, 192.5),
new THREE.Vector3(80, 5, 192.5),
new THREE.Vector3(80, 5, 35)
];
var curve = new THREE.CatmullRomCurve3(points);
curve.closed = true;
var curvePoints = curve.getPoints(200);
var geomCurvePath = new THREE.BufferGeometry().setFromPoints(curvePoints);
var matCurvePath = new THREE.LineBasicMaterial({
color: 0xff0000
});
var CurvePath = new THREE.Line(geomCurvePath, matCurvePath);
scene.add(CurvePath);
var group = new THREE.Group();
scene.add(group);
group.add(camera);
var pointerGeom = new THREE.ConeGeometry(4, 20, 4);
pointerGeom.translate(0, 10, 0);
pointerGeom.rotateX(Math.PI * 0.5);
var pointer = new THREE.Mesh(pointerGeom, new THREE.MeshNormalMaterial());
group.add(pointer);
btnLeft.addEventListener("click", function() {
turn(Math.PI * 0.5)
});
btnRight.addEventListener("click", function() {
turn(-Math.PI * 0.5)
});
function turn(angle) {
pointer.rotation.y += angle;
}
var p1 = new THREE.Vector3();
var p2 = new THREE.Vector3();
var lookAt = new THREE.Vector3();
var axis = new THREE.Vector3(0, 1, 0);
var percentage = 0;
render();
function render() {
requestAnimationFrame(render);
percentage += 0.0005;
curve.getPointAt(percentage % 1, p1);
curve.getPointAt((percentage + 0.001) % 1, p2);
lookAt.copy(p2).sub(p1).applyAxisAngle(axis, -Math.PI * 0.5).add(p1); // look at the point 90 deg from the path
group.position.copy(p1);
group.lookAt(lookAt);
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
#buttons {
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/92/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<div id="buttons">
<button id="btnLeft">left</button>
<button id="btnRight">right</button>
</div>

ThreeBSP wrong result

I'm having some troubles with a tube in Three.JS. I have to cut it at 45 degrees and then connect this tube with another one (a connection with "L" form).
My idea is this:
- I create a cube
- I change the position of the cube
- I use a substration between the tube and the cube to get the result
I found that the "subtract" function gives to me a strange result.
Here there is the simplified code (here the cube is not rotated but I will need to rotate it to get my result) and the image the with the undesired result (inside the yellow rectangle).
var cube = new THREE.CubeGeometry(25, 25, 25);
var cMesh = new THREE.Mesh(cube);
cMesh.position.y=100;
cMesh.updateMatrix();
var path = new THREE.LineCurve3(new THREE.Vector3(0, 100, 0), new THREE.Vector3(0,-100, 0));
var geometry = new THREE.TubeGeometry(path, 20, 2, 18, false);
material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
mesh = new THREE.Mesh(geometry, material);
var geom1 = new ThreeBSP(mesh);
var geom2=new ThreeBSP(cMesh);
var geomF=geom1.subtract(geom2);
mesh = geomF.toMesh();
problem detail

How can I bind two shapes together as one in Three.js?

Can I bind two different shapes together as one shape?
For example, binding sphere and cylinder together as one?
Kind of, yes. There are multiple options:
via hierarchy you can simply add one mesh to another using the add() function
via the GeometryUtil's merge() function to merge vertices and meshes of two Geometry objects into one
using a basic 3D editor that supports Boolean operations between meshes and exporting.
Method 1 is pretty straightforward:
var sphere = new THREE.Mesh(new THREE.SphereGeometry(100, 16, 12), new THREE.MeshLambertMaterial({ color: 0x2D303D, wireframe: true, shading: THREE.FlatShading }));
var cylinder = new THREE.Mesh(new THREE.CylinderGeometry(100, 100, 200, 16, 4, false), new THREE.MeshLambertMaterial({ color: 0x2D303D, wireframe: true, shading: THREE.FlatShading } ));
cylinder.position.y = -100;
scene.add(sphere);
scene.add(cylinder);
Notice that 16 is repeated, so the subdivisions level in one mesh matches the other (for a decent look).
Method 2.1 - via GeometryUtils
// Make a sphere
var sg = new THREE.SphereGeometry(100, 16, 12);
// Make a cylinder - ideally the segmentation would be similar to predictable results
var cg = new THREE.CylinderGeometry(100, 100, 200, 16, 4, false);
// Move vertices down for cylinder, so it maches half the sphere - offset pivot
for(var i = 0 ; i < cg.vertices.length; i++)
cg.vertices[i].position.y -= 100;
// Merge meshes
THREE.GeometryUtils.merge(sg, cg);
var mesh = new THREE.Mesh(sg, new THREE.MeshLambertMaterial({ color: 0x2D303D, wireframe: true, shading: THREE.FlatShading }));
scene.add(mesh);
Method 2.2 merging a Lathe half-sphere and a cylinder:
var pts = []; // Points array
var detail = .1; // Half-circle detail - how many angle increments will be used to generate points
var radius = 100; // Radius for half_sphere
var total = Math.PI * .51;
for(var angle = 0.0; angle < total ; angle+= detail) // Loop from 0.0 radians to PI (0 - 180 degrees)
pts.push(new THREE.Vector3(0,Math.cos(angle) * radius,Math.sin(angle) * radius)); // Angle/radius to x,z
var lathe = new THREE.LatheGeometry(pts, 16); // Create the lathe with 12 radial repetitions of the profile
// Rotate vertices in lathe geometry by 90 degrees
var rx90 = new THREE.Matrix4();
rx90.setRotationFromEuler(new THREE.Vector3(-Math.PI * .5, 0, 0));
lathe.applyMatrix(rx90);
// Make cylinder - ideally the segmentation would be similar for predictable results
var cg = new THREE.CylinderGeometry(100, 100, 200, 16, 4, false);
// Move vertices down for cylinder, so it maches half the sphere
for(var i = 0 ; i < cg.vertices.length; i++)
cg.vertices[i].position.y -= 100;
// Merge meshes
THREE.GeometryUtils.merge(lathe, cg);
var mesh = new THREE.Mesh(lathe, new THREE.MeshLambertMaterial({ color: 0x2D303D, wireframe: true, shading: THREE.FlatShading}));
mesh.position.y = 150;
scene.add(mesh);
The one problem I can't address at the moment comes from the faces that are inside the mesh. Ideally, those would have normals flipped, so they wouldn't render, but I haven't found a quick solution for that.
The third is fairly straightforward. Most 3D packages allow Boolean operation on meshes (e.g., merging two meshes together with the ADD operation (meshA + meshB)). Try creating a cylinder and a sphere in Blender (free and opensource), which already has a Three.js exporter. Alternatively you can export an .obj file of the merged meshes from your 3D editor or choice and use the convert_obj_three script.
I've found yet another method, which might be easier/more intuitive. Remember the Boolean operations I've mentioned above?
It turns out there is an awesome JavaScript library just for that: Constructive Solid Geometry:
Chandler Prall wrote some handy functions to connect CSG with three.js. So with the CSG library and the Three.js wrapper for it, you can simply do this:
var cylinder = THREE.CSG.toCSG(new THREE.CylinderGeometry(100, 100, 200, 16, 4, false), new THREE.Vector3(0, -100, 0));
var sphere = THREE.CSG.toCSG(new THREE.SphereGeometry(100, 16, 12));
var geometry = cylinder.union(sphere);
var mesh = new THREE.Mesh(THREE.CSG.fromCSG(geometry), new THREE.MeshNormalMaterial());
Which gives you a nice result (no problems with extra faces/flipping normals, etc.):

Categories