Three JS Shadow from tree not cast - three.js

Learning Three Js by my own and with some (incredible) stuff found on Internet, I use Script to generate a LowPoly Planet, but trees does not cast Shadow on the ground.
I look for help but didn't find something to work. Can you explain me why please ?
I tried many things with MeshLambertMaterial and things like :
mesh.castShadow = true; mesh.receiveShadow = true; etc..
Here is a fiddle.
https://jsfiddle.net/Fliip36/fcz2psuj/

Trees are objects, that consist of several meshes (as they are Object3D or Group), so to make them to cast shadows, you need to use .traverse() method.
So change this part
objects.push(obj);
obj.scale.set(0.01, 0.01, 0.01);
obj.tween = TweenMax.to(obj.scale, rnd(3, 10), { x: 1, y: 1, z: 1, ease: Elastic.easeOut.config(1, 0.2), delay: rnd(0, 4) });
obj.receiveShadow = true;
obj.castShadow = true;
planet.add(obj);
to this:
objects.push(obj);
obj.scale.set(0.01, 0.01, 0.01);
obj.tween = TweenMax.to(obj.scale, rnd(3, 10), { x: 1, y: 1, z: 1, ease: Elastic.easeOut.config(1, 0.2), delay: rnd(0, 4) });
obj.traverse(o => {
if (o.isMesh){ // set these parameters for meshes only
o.receiveShadow = true;
o.castShadow = true;
}
})
planet.add(obj);

Related

SpriteMaterial with ThreeJs | How can i set the Sprite position to stay with mesh?

I need to set the Sprite(Hello) position to stay with the mesh...Now, the Sprite is independent. If I rotate the object, the Sprite should stay with the mesh
LATER EDIT:
Mesh position is always 0..
I tried for couple of hours but nothing worked..
Here is a screenshot of the code:
And the code...
loader.load('/3D_Cars/Dacia_1410_Tuned/components/scene.gltf', function (gltf) {
carModel = gltf.scene.children[0];
gltf.scene.traverse(function (child) {
if (child.isMesh) {
var v = new THREE.Vector3(0, 0, 0);
domEvents.addEventListener(carModel.getObjectByName(child.name), 'click', function (event) {
app.RenameAsCarParts(child.name)
console.log(child.name)
}, false)
if (child.name == 'rim_RSL1_Aluminium_0') {
child.position.set(100, 50, 3); //testing
var spritey = makeTextSpriteNew(" Hello ",
{ fontsize: 15, textColor: { r: 0, g: 0, b: 0, a: 1.0 } });
scene.add(spritey);
}
}
});
carModel.getObjectByName('Body_paint_0').material = bodyMaterial;
carModel.rotation.set(Math.PI / 2, 0, 0);
carModel.rotateY(Math.PI);
scene.add(carModel);
});
Thank you!
Where is this method defined makeTextSpriteNew?
You can refer this three.js example for drawing Text Sprites in three.js
https://stemkoski.github.io/Three.js/Sprite-Text-Labels.html
The idea is, you need to provide a 3d position to this sprite so that it will project that position on screen and display the Sprite.
In your case you need to provide it the mesh's position (bounding box corner/center based on where you need to show the sprite).
spritey.position.set(posX, posY, posZ);

Threebox Tooltip in 3D models

I´ve been trying to enable tooltips on some imported 3D models, but it isnt working.
I already enabled tooltips in threbox, and I enabled tooltips in the options for the 3d element, as shown below.
tb = new Threebox(
map,
mbxContext,
{
realSunlight: true,
enableSelectingFeatures: true, //change this to false to disable fill-extrusion features selection
enableTooltips: true // change this to false to disable default tooltips on fill-extrusion and 3D models
}
);
var proptions = {
obj: './models/er.glb',
type: 'gltf',
scale: 10,
units: 'meters',
rotation: { x: 90, y: 0, z: 0 }, //default rotation
anchor: 'center',
adjustment: { x: 0, y: 0, z: 0.4 },
enableToltips: true
}
When i load the object i did the following:
tb.loadObj(proptions, function (model) {
model.setCoords(place);
model.addTooltip("A radar in the middle of nowhere", true);
model.setRotation({ x: 0, y: 0, z: Math.floor(Math.random() * 100) })
tb.add(model);
});
Although the object appears in the render, when I put the mouse above or i click it nothing shows the tooltip.
What am I missing ?
EDIT:
Following #jscastro response i changed the import in the top of my html page to <link href="./threebox-plugin/examples/css/threebox.css" rel="stylesheet" /> (the path is the correct to where the file is)
I also removed the enableTooltip: true in proptions.
Despite that it still does not work, Below i will leave the code as it is:
var origin = [-8.4, 41.20, 1];
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
center: origin,
zoom: 11,
pitch: 30,
antialias: true
});
//Things related to dateTime ommited
window.tb = new Threebox(
map,
map.getCanvas().getContext('webgl'),
{
realSunlight: true,
enableSelectingFeatures: true, //change this to false to disable fill-extrusion features selection
enableTooltips: true // change this to false to disable default tooltips on fill-extrusion and 3D models
}
);
map.on('style.load', async function () {
await importarLinhas();
// stats
// stats = new Stats();
// map.getContainer().appendChild(stats.dom);
animate();
map.addLayer({
id: 'custom_layer',
type: 'custom',
renderingMode: '3d',
onAdd: function (map, mbxContext) {
var eroptions = {
obj: './models/stationBus.fbx',
type: 'fbx',
scale: 0.01,
units: 'meters',
rotation: { x: 90, y: 20, z: 0 }, //default rotation
anchor: 'center',
adjustment: { x: -0.1, y: -0.1, z: 0.4 }
}
var poptions = {
obj: './models/Busstop.fbx',
type: 'fbx',
scale: 0.03,
units: 'meters',
rotation: { x: 90, y: 20, z: 0 }, //default rotation
anchor: 'center',
adjustment: { x: -0.1, y: -0.1, z: 0.1 }
}
var proptions = {
obj: './models/er.glb',
type: 'gltf',
scale: 2.7,
units: 'meters',
rotation: { x: 90, y: 0, z: 0 }, //default rotation
anchor: 'center',
adjustment: { x: 0, y: 0, z: 0.4 }
}
allNos.forEach((element) => { //For each one of a list that i fill first
//center of where the objects are
var place = [element.lng, element.lat, 0];
//cylinder as "base" for each one of the 3d Models
**//in here i cant do the Tooltip for the object**
const geometry = new THREE.CylinderGeometry(0.6, 0.6, 0.15, 32);
const material = new THREE.MeshLambertMaterial({ color: 0x5B5B5B });
const cylinder = new THREE.Mesh(geometry, material);
var baseOptions = {
obj: cylinder,
anchor: 'center',
adjustment: { x: 0, y: 0, z: -0.4 }
}
let base = tb.Object3D(baseOptions);
base.setCoords(place);
base.setRotation({ x: 90, y: 0, z: 0 })
//The text is just for the test
base.addTooltip("A radar in the middle of nowhere", true);
// base.castShadow = true;
window.tb.add(base);
//next i check what type of element it is
//it can only be one at the same time, so i use different models for each type
if (element.tipo === "p") {
window.tb.loadObj(poptions, function (model) {
model.setCoords(place);
model.addTooltip("A radar in the middle of nowhere", true);
model.setRotation({ x: 0, y: 0, z: Math.floor(Math.random() * 100) })
// model.castShadow = true;
window.tb.add(model);
});
}
if (element.tipo === "er") {
window.tb.loadObj(eroptions, function (model) {
model.setCoords(place);
model.addTooltip("A radar in the middle of nowhere", true);
model.setRotation({ x: 0, y: 0, z: Math.floor(Math.random() * 100) })
// model.castShadow = true;
window.tb.add(model);
});
}
if (element.tipo === "pr") {
window.tb.loadObj(proptions, function (model) {
model.setCoords(place);
model.addTooltip("A radar in the middle of nowhere", true);
model.setRotation({ x: 0, y: 0, z: Math.floor(Math.random() * 100) })
// model.castShadow = true;
window.tb.add(model);
});
}
});
},
render: function (gl, matrix) {
window.tb.setSunlight(date, origin.center);
window.tb.update();
}
})
map.addLayer(createCompositeLayer());
map.on('SelectedFeatureChange', onSelectedFeatureChange);
});
EDIT
I downloaded the page you shared in the chat, and I found many different issues and mistakes in your code.
1. You're using the wrong property to enable the selection of 3D objects, you use enableSelectingFeatures: true, //change this to false to disable fill-extrusion features selection, that is for Mapbox fill-extrusions features as said in the comment, but not for 3D models and objects, you have to use enableSelectingObjects: true. Only adding this, your problem with the tooltips on mouse over will be solved.
tb = new Threebox(
map,
mbxContext,
{
realSunlight: true,
enableSelectingObjects: true, //enable 3D models over/selection
enableTooltips: true // enable default tooltips on fill-extrusion and 3D models
}
);
But I have found other issues...
2. Your models scale initialization is too small, so you are hiding them below the big shapes you have created. The scale of your bus stop is scale: 0.01 and you define a place which is on the ground var place = [element.lng, element.lat, 0];, so it's hidden inside this CylinderGeometry
If you use scale: 1 you will see how your bus stops raises from the cylinder.
3. Same with the bus, you initialize them with scale: 1, which make them be hidden below the tubes and cylinders you have created. If you initialize them with scale: 10, and you elevate them 5 meters from the floor let truck = model.setCoords([lngB, latB, 4]); then you will see them raising.
4. Your models have a wrong initialization params mixing anchor and adjustment. anchor: center will center the pivotal center of your object properly, but then you apply negative values to x and y (which means decenter the object), and a z value that elevates the pivotal center adjustment: { x: -0.1, y: -0.1, z: 0.4 }. If you want your model on altitude use the 3rd coord in setCoords.
5. Your Cylinders and Tubes for the bus stops and bus lines are huge, and also they have the wrong init params, as you set them below the ground level -0.4 units adjustment: { x: 0, y: 0, z: -0.4 } (something supported by Mapbox but very bad resolved and producing weird effects. My recommendation would be to make them almost flat and at the ground level with no adjustment param. const geometry = new THREE.CylinderGeometry(0.6, 0.6, 0.01, 32);.
Summarizing, check all of these changes and let me know if it works.

GSAP + three js

I was trying to get smooth effect while rotating or increasing in size a figure.
welcomeWrapper.addEventListener('mousedown', () => {
cube.scale.set(1.5, 1.5, 1.5);
TweenMax.to(cube.scale, 1, { ease: Power4.easeInOut });
});
//or
welcomeWrapper.addEventListener('mousemove', e => {
TweenLite.to(cube.scale, 1, {
css: {
x: e.movementX,
y: e.movementY,
z: e.movementX
}
});
cube.rotation.x += e.movementX / 250;
cube.rotation.y += e.movementY / 250;
cube.rotation.z += e.movementX / 250;
});
//tried this but it won't work
controls.enableDamping = true;
controls.minPolarAngle = 0.8;
controls.maxPolarAngle = 2.4;
controls.dampingFactor = 0.07;
controls.rotateSpeed = 0.07;
But every time I get:
Invalid property css set to {x: 0, y: 0, z: 0} Missing plugin? gsap.registerPlugin()
In order to scale the cube, for example to (2, 2, 2), you'll need to do something like this:
TweenMax.to(cube.scale, 1, { x: 2, y: 2, z: 2, ease: Power4.easeInOut });
There's no reason to use css:{} with a cube, since 3D objects in THREE.js don't follow CSS attributes.

Triangle Strip artifacts

I'm playing with draw groups in THREE.TriangleStripDrawMode draw mode, and I'm seeing some odd artifacts. I think it might be a bug, but I'd like someone else to confirm that I'm not doing something wrong before submitting a report.
In the snippet below, I create 4 squares composed of 4 triangles each. They're all indexed for right-hand rendering. (I realize that triangle strips don't technically need indexing, but I could potentially have complex shapes with re-used vertices.) Their other properties and results are as follows:
Red Square
Two groups: 0-9 and 9-12
Standard front-side rendering
Black artifact on the back-face of triangle index 1
Green Square
One group: 0-12
Standard front-side rendering
Black artifacts on the back-face of triangles index 1 & 3
The same thing happens when I don't use grouping at all
Blue Square
Two groups: 0-9 and 9-12
Double-side rendering
Renders as expected
Yellow Square
One group: 0-12
Double-side rendering
Triangle index 3 renders black on both sides
So did I miss something, or should I submit this as a bug in three.js?
var renderer, scene, camera, controls, stats;
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight,
FOV = 35,
NEAR = 1,
FAR = 1000;
function createShapes(){
var bg = new THREE.BufferGeometry();
bg.addAttribute("position", new THREE.BufferAttribute(new Float32Array([
-1, 1, 0,
-1, -1, 0,
0, 1, 0,
0, -1, 0,
1, 1, 0,
1, -1, 0
]), 3));
bg.addAttribute("normal", new THREE.BufferAttribute(new Float32Array([
0, 0, 1,
0, 0, 1,
0, 0, 1,
0, 0, 1,
0, 0, 1,
0, 0, 1
]), 3));
bg.setIndex(new THREE.BufferAttribute(new Uint32Array([
0, 1, 2,
3, 2, 1,
2, 3, 4,
5, 4, 3
]), 1));
var group1 = bg.clone(),
group2 = bg.clone(),
group3 = bg.clone(),
group4 = bg.clone();
/**/
group1.clearGroups();
group1.addGroup(0, 9, 0);
group1.addGroup(9, 3, 0);
group2.clearGroups();
group2.addGroup(0, 12, 0);
group3.clearGroups();
group3.addGroup(0, 9, 0);
group3.addGroup(9, 3, 0);
group4.clearGroups();
group4.addGroup(0, 12, 0);
/**/
var mat1 = new THREE.MeshPhongMaterial({color: "red"}),
mat2 = new THREE.MeshPhongMaterial({color: "green"}),
mat3 = new THREE.MeshPhongMaterial({color: "blue", side: THREE.DoubleSide}),
mat4 = new THREE.MeshPhongMaterial({color: "yellow", side: THREE.DoubleSide});
var m1 = new THREE.Mesh(group1, [mat1]),
m2 = new THREE.Mesh(group2, [mat2]),
m3 = new THREE.Mesh(group3, [mat3]),
m4 = new THREE.Mesh(group4, [mat4]);
m1.drawMode = THREE.TriangleStripDrawMode;
m2.drawMode = THREE.TriangleStripDrawMode;
m3.drawMode = THREE.TriangleStripDrawMode;
m4.drawMode = THREE.TriangleStripDrawMode;
m1.position.set(-2, 2, 0);
m2.position.set(2, 2, 0);
m3.position.set(-2, -2, 0);
m4.position.set(2, -2, 0);
scene.add(m1, m2, m3, m4);
}
function init() {
document.body.style.backgroundColor = "slateGray";
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
document.body.appendChild(renderer.domElement);
document.body.style.overflow = "hidden";
document.body.style.margin = "0";
document.body.style.padding = "0";
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 20;
scene.add(camera);
controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.dynamicDampingFactor = 0.5;
controls.rotateSpeed = 3;
var light = new THREE.PointLight(0xffffff, 1, Infinity);
camera.add(light);
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0';
document.body.appendChild(stats.domElement);
resize();
window.onresize = resize;
// POPULATE EXAMPLE
createShapes();
animate();
}
function resize() {
WIDTH = window.innerWidth;
HEIGHT = window.innerHeight;
if (renderer && camera && controls) {
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
controls.handleResize();
}
}
function render() {
renderer.render(scene, camera);
}
function animate() {
requestAnimationFrame(animate);
render();
controls.update();
stats.update();
}
function threeReady() {
init();
}
(function () {
function addScript(url, callback) {
callback = callback || function () { };
var script = document.createElement("script");
script.addEventListener("load", callback);
script.setAttribute("src", url);
document.head.appendChild(script);
}
addScript("https://threejs.org/build/three.js", function () {
addScript("https://threejs.org/examples/js/controls/TrackballControls.js", function () {
addScript("https://threejs.org/examples/js/libs/stats.min.js", function () {
threeReady();
})
})
})
})();
r86 (the problem has actually been around for a while)
I had an "AHA!" moment while looking at some other examples. My indexing was off, which is also throwing off my grouping.
The indexing should be:
bg.setIndex(new THREE.BufferAttribute(new Uint32Array([
0, 1, 2, 3, 4, 5
]), 1));
Which makes sense, because that's how the triangle strip defines the steps of its vertices.
Then to draw a full square, I needed a single group:
group1.addGroup(0, 6, 0);
Which means start at the group index of 0, for 6 group indices (which covers all of them).
There's still a problem when trying to render an (odd index) individual triangle. Because the winding order for odd triangles is backwards, creating a group that starts with an odd triangle will not be lit correctly (renders black). But that's for another question...

How to move camera along a simple path

How can I move the camera along a simple path (created by an array of vertices/points)? I need to move it automatically, not by keyboard/mouse events, like in a first person shoter game?
Looked for this example: http://threejs.org/examples/webgl_geometry_extrude_splines.html and it's realy good (and complex), but I need something simple, as a person who is just starting to learn Three.js
So, the best and simplest solution (based on my errors and researches - maybe you can find even a simpler solution) is to use PathControls; you can find the library here: https://github.com/mrdoob/three.js/blob/master/examples/js/controls/PathControls.js
This simple solution is based on the book: Learning Three.js: "The JavaScript 3D Library for WebGL"; it's very good to learn something on Three and I recommend you to read it; First we add the PathControls.js to our document:
<script src="js/PathControls.js"></script>
then we add some variables, before out init() function:
var controls;
var clock = new THREE.Clock();
var pathControls;
now we need some work to do on our init() function, after creating the scene, camera, lights, etc:
controls = new function () {
this.numberOfPoints = 5;
this.segments = 64;
this.radius = 3;
this.radiusSegments = 5;
this.closed = false;
this.points = getPoints();
//you can take out this last one: it shows you the path on which the camera is moving
generatePoints(this.points, this.segments, this.radius, this.radiusSegments, this.closed);
}
pathControls = new THREE.PathControls(camera);
// configure the controller
pathControls.duration = 70
//speed, so you will not have the dash effect on a curve
pathControls.useConstantSpeed = true;
pathControls.lookSpeed = 0.1;
pathControls.lookVertical = true;
pathControls.lookHorizontal = true;
pathControls.verticalAngleMap = {srcRange: [ 0, 2 * Math.PI ], dstRange: [ 1.1, 3.8 ]};
pathControls.horizontalAngleMap = {srcRange: [ 0, 2 * Math.PI ], dstRange: [ 0.3, Math.PI - 0.3 ]};
pathControls.lon = 300;
pathControls.lat = 40;
// add the path
controls.points.forEach(function(e) {
pathControls.waypoints.push([e.x, e.y, e.z]) });
// when done configuring init the control
pathControls.init();
// add the animationParent to the scene and start the animation
scene.add(pathControls.animationParent);
pathControls.animation.play(true, 0 );
Lastly you need this 3 lines in you animate() function so the camera actually will move based on time:
var delta = clock.getDelta();
THREE.AnimationHandler.update(delta);
pathControls.update(delta);
As regards the support functions (I have this one that is just an array on 5 points, but you can use many more and more complex: it's up to you):
function getPoints() {
var points = [];
points.push(new THREE.Vector3(0, 20, 0));
points.push(new THREE.Vector3(100, 25, 0));
points.push(new THREE.Vector3(100, 20, 100));
points.push(new THREE.Vector3(0, 25, 100));
points.push(new THREE.Vector3(0, 20, 0));
return points;
}
And those are the functions to show/draw a tube on the path you picked so you can see how actually the camera is moving (not needed for the whole code to work):
function generatePoints(points, segments, radius, radiusSegments, closed) {
var tube = new THREE.TubeGeometry(new THREE.SplineCurve3(points), segments, radius, radiusSegments, false, closed);
var tubeMesh = createMesh(tube);
scene.add(tubeMesh);
}
function createMesh(geom) {
mesh = THREE.SceneUtils.createMultiMaterialObject( geom, [
new THREE.MeshLambertMaterial({color: 0x00ff00, transparent: true}),
new THREE.MeshBasicMaterial({color: 0x000000, opacity: 0.3, wireframe: true, transparent: true})]);
return mesh;
}
Hope it will be useful to someone; for the whole code, you'll find it here: https://github.com/MarcinKwiatkowski1988/learningThreeJs/blob/master/movingCameraOnPath/myTry1_simply.html

Resources