TrackballControls with Collada kfAnimation Three JS - three.js

Hi guys I am working with kfAnimation animation from a Collada model. I want to include a trackball controls but when I do I exceed the size stack I dont know why my code is the following
function init() {
initRender();
// Camera
initCamera();
// Scene
initScene();
//controlls
initControls();
initGrid();
loadObjectAnimatedFrames();
window.addEventListener( 'resize', onWindowResize, false );
}
function loadObjectAnimatedFrames(){
loader = loader.load( 'sources/crack-animated.dae', function ( collada ) {
model = collada.scene;
animations = collada.animations;
kfAnimationsLength = animations.length;
//model.scale.x = model.scale.y = model.scale.z = 0.125; // 1/8 scale, modeled in cm
for ( var i = 0; i < kfAnimationsLength; ++i ) {
var animation = animations[ i ];
var kfAnimation = new THREE.KeyFrameAnimation( animation );
kfAnimation.timeScale = 1;
kfAnimations.push( kfAnimation );
}
start();
animate( lastTimestamp );
scene.add( model );
});
}
function initControls(){
controls = new THREE.TrackballControls(camera);
controls.minDistance = 100;
controls.maxDistance = 1800;
controls.addEventListener('change',render);
}
function animate( timestamp ) {
var frameTime = ( timestamp - lastTimestamp ) * 0.001;
if ( progress >= 0 && progress < 48 ) {
for ( var i = 0; i < kfAnimationsLength; ++i ) {
kfAnimations[ i ].update( frameTime );
}
} else if ( progress >= 48 ) {
for ( var i = 0; i < kfAnimationsLength; ++i ) {
kfAnimations[ i ].stop();
}
progress = 0;
start();
}
//pointLight.position.copy( camera.position );
progress += frameTime;
lastTimestamp = timestamp;
render();
}
I tried to put controls update inside animate and start function but I think it consumed a lot of resources. Also I tried to put inside render but same results.
Thanks for your help I hope some more experimented can help me.

Finally I found the solution and it was an option enableDamping and dampingFactor
function initControls(){
controls = new THREE.TrackballControls( camera, renderer.domElement );
//controls.addEventListener( 'change', render ); // add this only if there is no animation loop (requestAnimationFrame)
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.enableZoom = false;
}
after this I just add controls.update();
renderer.render( scene, camera );
requestAnimationFrame( animate );

Related

GLB animation in three.js is too fast

I have uploaded a glb file with an animation, and the animation is moving extremely fast, and I do not know why.
This is my character's animation code:
class MainChar extends THREE.Object3D {
constructor() {
super();
this.object = new THREE.Object3D();
this.object.position.set(0, 1, 50);
this.object.scale.x=20;
this.object.scale.y=20;
this.object.scale.z=20;
//load house model form blender file
/*
loader.setPath('../../models/characters/');
const gltf = loader.load('Douglas.glb', (gltf) => {
gltf.scene.traverse(c => {
c.castShadow = true;
});
this.object.add( gltf.scene);
});
*/
const loader = new THREE.GLTFLoader();
loader.setPath('../../models/characters/');
const gltf = loader.load('walk.glb', (gltf) => {
gltf.scene.traverse(c => {
c.castShadow = true;
});
this.mixer = new THREE.AnimationMixer( gltf.scene );
this.mixer.timeScale=1/5;
var action = this.mixer.clipAction( gltf.animations[ 0 ] );
action.play();
this.object.add( gltf.scene );
});
//save keyboard bindings
this.keyboard = new THREEx.KeyboardState();
/*
//creating a box (need to change it to a character with animations)
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
this.object = new THREE.Mesh( geometry, material );
this.object.scale.x=5;
this.object.scale.y=10;
this.object.scale.z=5;
//starting position for character
this.object.position.set(0, 10, 50);
*/
this.update = function (time) {
if ( this.mixer ){
this.mixer.update( time );
console.log(time);
}
//MOVEMENT OF BOX
//speed
var moveDistance = 0.5 ;
// var rotateAngle = Math.PI / 2 * 0.05;
// move forwards/backwards/left/right
if ( this.keyboard.pressed("W") ){
this.object.translateZ( -moveDistance );
}
if ( this.keyboard.pressed("S") ){
this.object.translateZ( moveDistance );
}
if ( this.keyboard.pressed("A") ){
this.object.translateX( -moveDistance );
}
if ( this.keyboard.pressed("D") ){
this.object.translateX( moveDistance );
}
// move forwards/backwards/left/right
if ( this.keyboard.pressed("up") ){
this.object.translateZ( -moveDistance );
}
if ( this.keyboard.pressed("down") ){
this.object.translateZ( moveDistance );
}
if ( this.keyboard.pressed("left") ){
this.object.translateX( -moveDistance );
}
if ( this.keyboard.pressed("right") ){
this.object.translateX( moveDistance );
}
// FOR CAMERA ROTATIONS
//this.object.rotateOnAxis( new THREE.Vector3(0,1,0), -rotateAngle);
//this.object.rotateOnAxis( new THREE.Vector3(0,1,0), rotateAngle);
//var rotation_matrix = new THREE.Matrix4().identity();
if ( this.keyboard.pressed("Z") )
{
this.object.position.set(0, 1, 50);
this.object.rotation.set(0,0,0);
}
/*
// global coordinates
if ( this.keyboard.pressed("left") )
this.object.position.x -= moveDistance;
if ( this.keyboard.pressed("right") )
this.object.position.x += moveDistance;
if ( this.keyboard.pressed("up") )
this.object.position.z -= moveDistance;
if ( this.keyboard.pressed("down") )
this.object.position.z += moveDistance;
*/
};
}
}
This is the time class that allows the game to be paused, as well as returns the delta time:
class Time {
constructor(){
this.is_pause = false;
this.accumalated_run_time = 0;
this.clock = new THREE.Clock();
this.pause_clock = new THREE.Clock();
}
getRunTime()
{
this.accumalated_run_time += this.clock.getDelta();
return this.accumalated_run_time
}
pause()
{
this.is_pause = true;
}
unpause()
{
this.is_pause = false;
this.clock.getDelta();
}
}
This is the sceneManager that calls up my character for updating animations:
class SceneManager {
constructor(canvas) {
//this entire function renders a scene where you can add as many items as you want to it (e.g. we can create the house and add as
//many items as we want to the house). It renders objects from other javascript files
//------------------------------------------------------------------------------------------------------------------------------------------
//These are supposed to act like constants. DO NOT CHANGE
this.GAME_PAUSE = "pause";
this.GAME_RUN = "run";
//------------------------------------------------------------------------------------------------------------------------------------------
//we use (this) to make variables accessible in other classes
this.time = new Time();
this.game_state = this.GAME_RUN;
this.screenDimensions = {
width: canvas.width,
height: canvas.height
};
//the essentials for rendering a scene
this.scene = this.buildScene();
this.renderer = this.buildRender(this.screenDimensions);
this.camera = this.buildCamera(this.screenDimensions);
this.managers = this.createManagers();
this.loadToScene(this.managers[0].entities);
//allow camera to orbit target (player)
this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
this.controls.target.set(0, 20, 0);
this.controls.update();
}
loadToScene(entities)
{
for (let i = 0 ; i < entities.length ; i++)
{
console.log("before" +i.toString());
this.scene.add(entities[i].object);
console.log("after");
}
}
//this function creates our scene
buildScene() {
//create a new scene
const scene = new THREE.Scene();
//set the scene's background-> in this case it is our skybox
const loader = new THREE.CubeTextureLoader();
//it uses different textures per face of cube
const texture = loader.load([
'../skybox/House/posx.jpg',
'../skybox/House/negx.jpg',
'../skybox/House/posy.jpg',
'../skybox/House/negy.jpg',
'../skybox/House/posz.jpg',
'../skybox/House/negz.jpg'
]);
scene.background = texture;
//if we wanted it to be a colour, it would have been this commented code:
//scene.background = new THREE.Color("#000");
return scene;
}
//this creates a renderer for us
buildRender({ width, height }) {
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true, alpha: true
});
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
return renderer;
}
//create a camera for the screen
buildCamera({ width, height }) {
//SETTING FIELD OF VIEW, ASPECT RATIO (which should generally be width/ height), NEAR AND FAR (anything outside near/ far is clipped)
const aspectRatio = width / height;
const fieldOfView = 60;
const nearPlane = 1;
const farPlane = 1000;
//there are 2 types of cameras: orthographic and perspective- we will use perspective (more realistic)
const camera = new THREE.PerspectiveCamera(fieldOfView, aspectRatio, nearPlane, farPlane);
//set where the camera is
camera.position.set(-50, 50, 70);
return camera;
}
//add subjects to the scene
createManagers() {
const managers=[new EntityManager()];
//can be altered so we can add multiple entities, and depending on which position
//it is, certain ones won't be paused, and some will be
managers[0].register(new GeneralLights());
managers[0].register(new House());
managers[0].register(new MainChar());
managers[0].register(new SceneSubject())
return managers;
}
//this updates the subject/model every frame
update() {
//won't call this loop if it's paused-> only for objects that need to be paused (managers that need to be paused)
if (this.game_state == this.GAME_RUN)
{
const runTime = this.time.getRunTime();
this.managers[0].update(runTime);
}
//update orbit controls
this.controls.update();
this.renderer.render(this.scene, this.camera);
}
//this resizes our game when screen size changed
onWindowResize() {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
}
pause(){ //when pause mode is entered. The pause menu needs to be rendered.
this.game_state = this.GAME_PAUSE;
this.time.pause();
}
unpause(){
this.game_state = this.GAME_RUN;
this.time.unpause();
}
}
I think the issue is with your AnimationMixer.update() call. If you look at the docs, update is expecting a time-delta in seconds, but it looks like you're passing the total running time. This means it should receive the time passed since the last frame. You can fix this by using clock.getDelta(); as the argument:
this.update = function (time) {
if ( this.mixer ){
const delta = this.clock.getDelta();
this.mixer.update(delta);
}
// ...
}

animation gltf 3d object in three.js

I have a working GLTF object and trying to apply animation without AnimationMixer, but in the end I have an error:Identifier 'cube' has already been declared. Moreover, if I work with an Mesh object, everything is fine. what's happening? Thanks.
let cubes, cube;
let loader = new THREE.GLTFLoader();
loader.load(
'model/cubes.glb',
function ( gltf ) {
cubes = gltf.scene;
});
cube = new three.Mesh(cubes);
scene.add(cube);
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
var lastFrameTime = new Date().getTime() / 1000;
var totalGameTime = 0;
function update(dt, t) {
setTimeout(function() {
var currTime = new Date().getTime() / 1000;
var dt = currTime - (lastFrameTime || currTime);
totalGameTime += dt;
update(dt, totalGameTime);
lastFrameTime = currTime;
}, 0);
}
function render() {
renderer.render(scene, camera);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
requestAnimFrame(render);
}
render();
update(0, totalGameTime);

MTLLoader breaks other 3js code and isn't loading texture right

I am having trouble getting the MTLLoader to work correctly. I have already been able to use the OBJLoader by itself to load the 3d object and I have a camera that works properly too in my scene. When I try and use the MTLLoader with the OBJLoader the object loads but it breaks my other three.js code. I get these errors:
three.js:24415 Uncaught TypeError: Cannot set property 'value' of undefined
at initMaterial (three.js:24415)
at setProgram (three.js:24493)
at WebGLRenderer.renderBufferDirect (three.js:23552)
at renderObject (three.js:24269)
at renderObjects (three.js:24239)
at WebGLRenderer.render (three.js:24037)
at render (demo2.html:480)
The object loads and it looks like some textures load but the textures don't look right, the camera breaks. I have been having trouble with this and could really use some guidance.
Here is my MTLLoader code by itself
var mesh = null;
var mtlLoader = new THREE.MTLLoader();
mtlLoader.load( 'dapHouseGood5.mtl', function( materials ) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials( materials );
objLoader.load( 'dapHouseGood5.obj', function ( object ) {
mesh = object;
scene.add( mesh );
} );
Here is the rest of my three.js code for reference
<script>
/* global THREE */
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const renderer2 = new THREE.WebGLRenderer({canvas});
var kitchenCameraActive = false;
document.getElementById("roomSelect").addEventListener("change", changeIt);
function changeIt(e) {
document.getElementById(e.target.value).click();
console.log(e);
}
var fov = 45;
var aspect = 2; // the canvas default
var near = 0.1;
var far = 100;
var camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(-97.570, 5.878, -5.289);
camera.rotation.set(0,0,0);
const controls = new THREE.OrbitControls(camera, canvas);
controls.target.set(0, 5, 0);
controls.update();
document.getElementById("kitchen").addEventListener("click", changeCamera);
document.getElementById("bathroom").addEventListener("click", changeCamera);
document.getElementById("deck").addEventListener("click", changeCamera);
document.getElementById("livingRoom").addEventListener("click", changeCamera);
document.getElementById("bedRoom").addEventListener("click", changeCamera);
document.getElementById("walkway").addEventListener("click", changeCamera);
document.getElementById("sideHouse").addEventListener("click", changeCamera);
document.getElementById("frontPorch").addEventListener("click", changeCamera);
document.getElementById("garageDoor").addEventListener("click", changeCamera);
document.getElementById("insideGarage").addEventListener("click", changeCamera);
function changeCamera(e) {
camera.rotation.set(e.toElement.attributes[5].nodeValue,
e.toElement.attributes[6].nodeValue, e.toElement.attributes[7].nodeValue);
camera.fov = e.toElement.attributes[4].nodeValue;
camera.position.set(e.toElement.attributes[1].nodeValue,
e.toElement.attributes[2].nodeValue, e.toElement.attributes[3].nodeValue);
camera.updateProjectionMatrix();
if (e.target.id == "walkway" || e.target.id == "frontPorch" || e.target.id ==
"garageDoor" || e.target.id == "insideGarage")
{
controls.target.set(0, 5, 0);
controls.update();
}
if(e.target.id == "kitchen"){
controls.target.set(7, 6, 7);
}
if(e.target.id == "bathroom"){
controls.target.set(-9,15,-7);
}
if(e.target.id == "deck"){
controls.target.set(31, 7, 1);
}
if(e.target.id == "livingRoom"){
controls.target.set(-12.5, 1.5, -18.5);
}
if(e.target.id == "bedRoom"){
controls.target.set(-15.7, 14, -21);
}
if(e.target.id == "insideGarage"){
controls.target.set(24.405, 6.733, -6.425);
}
controls.update();
console.log(e);
}
const scene = new THREE.Scene();
scene.background = new THREE.Color('black');
{
const planeSize = 40;
}
{
const skyColor = 0xB1E1FF; // light blue
const groundColor = 0xB97A20; // brownish orange
const intensity = 1;
const light = new THREE.HemisphereLight(skyColor, groundColor, intensity);
scene.add(light);
}
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(5, 10, 2);
scene.add(light);
scene.add(light.target);
}
var mesh = null;
var mtlLoader = new THREE.MTLLoader();
mtlLoader.load( 'dapHouseGood5.mtl', function( materials ) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials( materials );
objLoader.load( 'dapHouseGood5.obj', function ( object ) {
mesh = object;
scene.add( mesh );
} );
} );
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render() {
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
function onPositionChange(o) {
console.log("position changed in object");
console.log(o);
console.log('camera_default: '+camera.position.x+', '+camera.position.y+',
'+camera.position.z);
console.log('camera_default: '+camera.rotation.x+', '+camera.rotation.y+',
'+camera.rotation.z);
console.log(camera.fov);
console.log('quaternion_default: '+camera.quaternion.x+', '+
camera.quaternion.y+', '+camera.quaternion.z+', '+camera.quaternion.w);
}
controls.addEventListener('change', onPositionChange);
var mouse = new THREE.Vector2();
var raycaster, mouse = { x : 0, y : 0};
init();
function init () {
//Usual setup code here.
raycaster = new THREE.Raycaster();
renderer.domElement.addEventListener( 'click', raycast, false );
}
function raycast ( e ) {
//1. sets the mouse position with a coordinate system where the center
// of the screen is the origin
mouse.x = ( e.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( e.clientY / window.innerHeight ) * 2 + 1;
//2. set the picking ray from the camera position and mouse coordinates
raycaster.setFromCamera( mouse, camera );
//var mouse3D = new THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1, -(
event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
//raycaster.setFromCamera( mouse3D, camera );
//3. compute intersections
var intersects = raycaster.intersectObjects( scene.children, true );
for ( var i = 0; i < intersects.length; i++ ) {
console.log( intersects[ i ].object.name );
}
}
}
main();
</script>

Can Not See 3d Obj. Model When Loaded With THREE.js

I load an obj file from an external resource with three.js. From onProgress callback function I can see that object is loaded without any error. However I can not see object on the screen.
I tried diffrent textures and different camera postion but still can not see the object.
Interestingly, obj file can be easily seen with Windows Object VÄ°ewer without any settings.
Here the boj file I used and CAD program settings while exporting obj:
Obj files and related files with obj file: https://ufile.io/e3oplk29
Obj File export options on the CAD program: https://pasteboard.co/Ieu9226.jpg
Here the code I use:
////************HERE LIGHT AND SCENE AND CAMERA****************////
var directionalLightIntensity = 1;
var ambientLightIntensity = 0.05;
var ambiColor = "#ffffff";
var metalValue = 0;
var roughValue = 1;
var kumas = "<?php echo CUSTOMSHIRT_PLUGIN_DIR_URL.'customshirt/';?>"+"kumas/kumas9.jpg";
var kumasNormal = "<?php echo CUSTOMSHIRT_PLUGIN_DIR_URL.'customshirt/';?>"+"kumas/kumas9_NORMAL.jpg";
var container = document.getElementById('cloth-container');
if(window.innerWidth > 767 ){
var render_height = document.documentElement.clientHeight - 8;
var render_width = document.documentElement.clientWidth - 130;
}else{
var render_height = document.documentElement.clientHeight - 95;
var render_width = document.documentElement.clientWidth;
}
const scene = new THREE.Scene();
var light = new THREE.DirectionalLight('#ffffff', directionalLightIntensity);
var ambientLight = new THREE.AmbientLight(ambiColor, ambientLightIntensity);
light.position.set(0,0,1);
scene.add(light);
scene.add(ambientLight);
const camera = new THREE.PerspectiveCamera(75, render_width/render_height,0.1,1000);
camera.position.z = 1.8 ;
camera.position.y = 1.2;
camera.position.x = 0;
camera.lookAt( 0,1.2,0);
const renderer = new THREE.WebGLRenderer({ alpha: true , antialias:true });
renderer.setSize(render_width, render_height);
renderer.setClearColor( 0xffffff, 0);
container.appendChild(renderer.domElement);
const objLoader = new THREE.OBJLoader();
const mtlLoader = new THREE.MTLLoader();
mtlLoader.setMaterialOptions({side:THREE.DoubleSide});
////************HERE OBJ LOAD WITH THREE.JS****************////
mtlLoader.load( "<?php echo CUSTOMSHIRT_PLUGIN_DIR_URL.'customshirt/yaka6-n4/';?>"+'yaka6-n4.mtl', function( materials ) {
materials.preload();
objLoader.setMaterials( materials );
objLoader.load( "<?php echo CUSTOMSHIRT_PLUGIN_DIR_URL.'customshirt/yaka6-n4/';?>"+'yaka6-n4.obj', function ( obj ) {
collar_obj = obj;
obj.position.set( obj_pos_x, obj_pos_y, obj_pos_z );
obj.rotation.y = 0;
// texture
texture = textureLoader.load(kumas);
textureNormal= textureLoader.load(kumasNormal);
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
textureNormal.wrapS = textureNormal.wrapT = THREE.RepeatWrapping;
texture.repeat.x = textureXRepeat;
texture.repeat.y = textureYRepeat;
textureNormal.repeat.x = textureXRepeat;
textureNormal.repeat.y = textureYRepeat;
obj.traverse( function ( child ) {
//if ( child.isMesh ) child.material.map = texture;
if ( child.isMesh ) child.material = new THREE.MeshStandardMaterial({
//color: 0x996633,
//specular: 0x050505,
//shininess: my_shine_value,
metalness: metalValue,
roughness: roughValue,
map: texture,
normalMap: textureNormal,
//side: THREE.DoubleSide
});
});
scene.add( obj );
},
// onProgress callback
function ( xhr ) {
console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
},
// onError callback
function ( err ) {
console.log( 'An error happened' );
});
});
////************HERE RENDERER****************////
function render(){
requestAnimationFrame(render);
renderer.render(scene,camera);
}
render();
Any idea is appreciated.
Thanks
It seems your object's geometry is translated. Since the asset is composed of multiple meshes, I suggest the following code to center your OBJ.
const box = new THREE.Box3().setFromObject( object );
const center = box.getCenter( new THREE.Vector3() );
object.position.x += ( object.position.x - center.x );
object.position.y += ( object.position.y - center.y );
object.position.z += ( object.position.z - center.z );
I've added this code in the onLoad() callback of OBJLoader in the following official example and was able to see the object (a shirt collar).
https://threejs.org/examples/webgl_loader_obj_mtl
three.js R 104

Object loaded with ObjLoader does not receive shadows in three.js

The model casts shadows on a ground-plane but it does NOT receive shadows from another small plane in front (which does cast shadows on the ground-plane), let alone from itself. Any ideas ???
var light = new THREE.DirectionalLight( 0xffefee, 1.2 );
light.position.set( 0, 40, 100 );
light.shadow.camera.left = -100;
light.shadow.camera.right = 100;
light.shadow.camera.top = 100;
light.shadow.camera.bottom = -100;
light.shadow.camera.far = 1000;
light.shadow.camera.near = 1;
light.shadow.mapSize.width = 512;
light.shadow.mapSize.height = 512;
light.castShadow = true;
light.shadowDarkness = 0.5;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
var femModel = null;
mtlLoader.setMaterialOptions({ side:THREE.DoubleSide });
mtlLoader.load( 'female02.mtl', function( materials ) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials( materials );
objLoader.setPath( 'female02/' );
objLoader.load( 'female02.obj', function ( object ) {
femModel = object;
femModel.position.set( 0, -80, 0 );
sceneRTTA.add( femModel );
}, onProgress, onError );
});
The setup function is called after it has loaded completely:
function setupmodel(){
femModel.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.castShadow = true;
child.receiveShadow = true;
}
} );
}
three.js 0.91.0

Resources