Strange artifact on OBJLoader - three.js

I'm trying to load obj model.
Why does a strange boundary artifact appear on surface?
What I'm doing wrong? I'm creating the obj model using 3D Scanner.
Thank you.
/examples/webgl_loader_obj.html
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 200000 );
camera.position.z = 100;
// scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight( 0x101030 );
scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0xffeedd );
directionalLight.position.set( 1, 1, 1 );
scene.add( directionalLight );
//var directionalLight = new THREE.DirectionalLight( 0xffeedd );
//directionalLight.position.set( 0, 0, -1 );
//scene.add( directionalLight );
// texture
var manager = new THREE.LoadingManager();
manager.onProgress = function ( item, loaded, total ) {
console.log( item, loaded, total );
};
var texture = new THREE.Texture();
var loader = new THREE.ImageLoader( manager );
loader.load( 'textures/UV_Grid_Sm.jpg', function ( image ) {
texture.image = image;
texture.needsUpdate = true;
} );
// model
var loader = new THREE.OBJLoader( manager );
//loader.load( 'obj/male02/male02.obj', function ( object ) {
loader.load( '/ramgl/models/textured_mesh.obj', function ( object ) {
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.material.map = texture;
//child.material.side = THREE.DoubleSide;
}
} );
//object.position.y = - 80;
object.scale.x = object.scale.y = object.scale.z = 1000;
object.position.y = 300;
object.position.x = -500;
object.position.z = -100;
object.rotation.x = 180 * Math.PI / 180;
object.rotation.y = 30 * Math.PI / 180;
scene.add( object );
} );
//
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}
/examples/webgl_loader_obj_mtl.html
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 20000 );
camera.position.z = 100;
// scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight( 0x444444 );
scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0xffeedd );
//directionalLight.intensity = 0.5;
directionalLight.position.set( 1, 0, 1 ).normalize();
//directionalLight.onlyShadow = false;
scene.add( directionalLight );
var directionalLight = new THREE.DirectionalLight( 0xffeedd );
directionalLight.position.set( 0, 0, -1 ).normalize();
scene.add( directionalLight );
// model
var loader = new THREE.OBJMTLLoader();
//loader.load( 'obj/male02/male02.obj', 'obj/male02/male02_dds.mtl', function ( object ) {
//loader.load( '/ramgl/models/textured_mesh.obj', '/ramgl/models/textured_mesh.mtl', function ( object ) {
loader.load( '/ramgl/models/textured_mesh.obj', '', function ( object ) {
object.scale.x = object.scale.y = object.scale.z = 1000;
object.position.y = 300;
object.position.x = -500;
object.position.z = -100;
object.rotation.x = 180 * Math.PI / 180;
object.rotation.y = 30 * Math.PI / 180;
scene.add( object );
} );
//
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColorHex( 0x333333, 1 );
container.appendChild( renderer.domElement );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}

Related

Three.js DeviceOrientationControl makes scene disappear

I have a glb model which loads successfully and reacts to mouse movements. However when I add DeviceOrientationControl.js which is intended for mobile devices, the scene disappears. No errors visible in the console. Funny enough, when I change
controls = new DeviceOrientationControls( camera, renderer.domElement );
to
controls = new DeviceOrientationControls( group, renderer.domElement );
it works, but has strangely displaces the model around the axis, which I am not able to readjust manually. Can someone help me find a solution? Why doesn't it work with camera but with group (model directly)?
Also when I add
camera.lookAt(scene.position);
and disable DeviceOrientationControls, the model works however the desired controls are not, which is my main issue.
Here is my code:
import * as THREE from '../node_modules/three/build/three.module.js';
import {OrbitControls} from '../node_modules/three/examples/jsm/controls/OrbitControls.js';
import {DeviceOrientationControls} from '../node_modules/three/examples/jsm/controls/DeviceOrientationControls.js';
import {GLTFLoader} from '../node_modules/three/examples/jsm/loaders/GLTFLoader.js';
import { DRACOLoader } from '../node_modules/three/examples/jsm/loaders/DRACOLoader.js';
import Stats from '../node_modules/three/examples/jsm/libs/stats.module.js';
var camera, scene, renderer, group, stats, controls, windowHalfX = window.innerWidth / 2,
windowHalfY = window.innerHeight / 2,
mouseX = 0,
mouseY = 0;
var renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true;
renderer.shadowMapSoft = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
var width = window.innerWidth;
var height = window.innerHeight;
window.onload = function () {
init();
animate();
$(".loadmain").fadeOut(500);
}
var startButton = document.getElementById( 'startButton' );
startButton.addEventListener( 'click', function () {
$("#overlay").fadeOut(700, function(){
$(this).remove();
});
document.body.className += "loaded";
document.querySelector("canvas").className += " load";
}, false );
const gltfLoader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/js/libs/draco/');
dracoLoader.setDecoderConfig({ type: 'js' });
gltfLoader.setDRACOLoader(dracoLoader);
function init() {
camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);
controls = new DeviceOrientationControls( camera, renderer.domElement );
camera.position.set(0, 0, 7);
camera.zoom = 1;
camera.updateProjectionMatrix();
scene = new THREE.Scene();
scene.updateMatrixWorld();
var directionalLight = new THREE.DirectionalLight(0xffffff, 3);
directionalLight.color.setHSL(0.1, 1, 0.95);
directionalLight.position.set(0, 1, 1);
directionalLight.position.multiplyScalar(10);
scene.add(directionalLight);
directionalLight.shadow.mapSize.width = 2048;
directionalLight.shadow.mapSize.height = 2048;
var spotLight1 = new THREE.DirectionalLight( 0xff4000 );
spotLight1.position.set( -15, 3, -4 );
spotLight1.target.position.set( 0, 1, 0 );
spotLight1.castShadow = true;
scene.add( spotLight1 );
var spotLight2 = new THREE.DirectionalLight( 0xff0aea );
spotLight2.position.set( 15, 3, -4 );
spotLight2.target.position.set( 0, 1, 0 );
spotLight2.intensity = 1.2;
spotLight2.castShadow = true;
scene.add( spotLight2 );
group = new THREE.Group();
group.position.x = 0;
scene.add( group );
scene.add( camera );
gltfLoader.load('../public/res/3D/model.glb', (gltf) => {
const root = gltf.scene;
root.rotateY(-89.55);
root.position.x = 0;
root.position.y = -0.7;
root.castShadow = true;
group.add(root);
});
renderer = new THREE.WebGLRenderer({ antialias: true, canvas: document.querySelector('canvas'), alpha: true, });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild( renderer.domElement );
window.addEventListener( 'resize', onWindowResize, false );
window.addEventListener('mousemove', onDocumentMouseMove, false);
}
function onDocumentMouseMove(event) {
event.preventDefault();
mouseX = (event.clientX / window.innerWidth) * 2 - 1;
mouseY = - (event.clientY / window.innerHeight) * 2 + 1;
}
function animate() {
window.requestAnimationFrame( animate );
if (group) {
group.rotation.y = mouseX * .5;
group.rotation.x = mouseY * -.5;
}
controls.update();
render();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function render() {
//camera.lookAt(scene.position);
renderer.render( scene, camera );
}

Improve Three.js performance with SSR (Node,js next,js)

I'm trying to find best solution to improve my website with three.js performance.
I would like to ask someone who have tried three.js with Server Side Rendering and if the
performance improve.
My three.js project include 5 glb files in the scene. Each file is about 1MB.
So far I improved.
Speed Index First
Meaningful Paint
by using webpack
** minify and include all javascript into one file.
** use next generation image wep for all textures.
** able gzip to my sever
Also, to improve loading speed instead of load all 5 glb models at the beginning,
load it 1 or two first, then load rest of models when user needed.
To reduce fan sound( the CPU cause)
I did animate render only user interact
window.addEventListener("wheel", function(e) {
animate();
}, true);
var scene, renderer, camera, model, mixer;
function init() {
var container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 1, 2, - 3 );
camera.lookAt( 0, 1, 0 );
clock = new THREE.Clock();
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xa0a0a0 );
scene.fog = new THREE.Fog( 0xa0a0a0, 10, 50 );
var hemiLight = new THREE.HemisphereLight( 0xffffff, 'rgb(100,100,100)' );
hemiLight.position.set( 0, 20, 0 );
scene.add( hemiLight );
var mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2000, 2000 ), new THREE.MeshPhongMaterial( { color: 0x999999, depthWrite: false } ) );
mesh.rotation.x = - Math.PI / 2;
mesh.receiveShadow = true;
scene.add( mesh );
var grid = new THREE.GridHelper( 2000, 20, 0x000000, 0x000000 );
grid.material.opacity = 0.2;
grid.material.transparent = true;
scene.add( grid );
var textload = new THREE.TextureLoader();
var text = textload.load( require('./material/text.jpg') );
//text.jpg = 80kb;
text.flipY = false;
var textloadbbump = new THREE.TextureLoader();
var textbump = textloadbbump.load( require('./material/textbump.jpg') );
//textbump.jpg = 50kb;
textbump.flipY = false;
var loader = new GLTFLoader();
//character.glb is 1.1MB
loader.load( require('./material/character.glb'), function ( gltf ) {
model = gltf.scene;
model.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.material.roughness = 1;
if(child.name=='body'){
child.material.map = text;
child.material.bumpMap = textbump;
child.material.bumpScale = 0.001;
}
}});
scene.add( model );
mixer = new THREE.AnimationMixer( model );
mixer.clipAction( gltf.animations[ 0 ] ).play();
});
renderer = new THREE.WebGLRenderer( {
// antialias: true
powerPreference: 'low-power',
//precision: 'lowp'
} );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.shadowMap.enabled = false;
container.appendChild( renderer.domElement );
}
window.onresize = function () {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
};
init();
window.addEventListener("wheel", function(e) {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}, true);

Applying postprocessing steps for specific objects

Based on this example i try to create a scene where several objects get the bloom, and other objects dont.
The white cube in the middle is supposed to be just white (without the bloom)
I'm confused on how to get the result that i want. I tried for example adding a 2nd scene with the white cube but it seems i cant get the order right. Maybe my approch with different scenes is wrong?
Whats the "best" way to achieve this behaviour? I always end up just seeing one scene, just the white cube or the 4 colored ones. (example below shows everything atm)
myFiddle: https://jsfiddle.net/qwertasyx/8qw3ys4z/16/
var scene,scene2,camera, controls, pointLight, stats;
var composer, renderer, mixer;
var params = {
exposure: 1,
bloomStrength: 1.5,
bloomThreshold: 0,
bloomRadius: 0
};
var objects = [];
var clock = new THREE.Clock();
var container = document.getElementById( 'container' );
stats = new Stats();
//container.appendChild( stats.dom );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.toneMapping = THREE.ReinhardToneMapping;
container.appendChild( renderer.domElement );
scene = new THREE.Scene();
//scene2 = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 100 );
camera.position.set( 2.5,2.5, 10 );
scene.add( camera );
// scene2.add( camera );
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.maxPolarAngle = Math.PI * 0.5;
controls.minDistance = 1;
controls.maxDistance = 10;
controls.target.set(2.5,2.5,0)
controls.update()
// scene.add( new THREE.AmbientLight( 0x404040 ) );
pointLight = new THREE.PointLight( 0xffffff, 1 );
// camera.add( pointLight );
var renderScene = new THREE.RenderPass( scene, camera );
//var renderScene2 = new THREE.RenderPass( scene2, camera );
var bloomPass = new THREE.UnrealBloomPass( new THREE.Vector2( window.innerWidth, window.innerHeight ), 1.5, 0.4, 0.85 );
bloomPass.renderToScreen = true;
bloomPass.threshold = params.bloomThreshold;
bloomPass.strength = params.bloomStrength;
bloomPass.radius = params.bloomRadius;
composer = new THREE.EffectComposer( renderer );
composer.setSize( window.innerWidth, window.innerHeight );
composer.addPass( renderScene );
composer.addPass( bloomPass );
//composer.addPass( renderScene2 );
//objects
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
var cube = new THREE.Mesh( geometry, material );
cube.vrz = 0.01;
cube.position.x += 5
scene.add( cube );
objects.push(cube)
var material = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
var cube = new THREE.Mesh( geometry, material );
cube.vrz = 0.01;
cube.position.x += 5
cube.position.y += 5
scene.add( cube );
objects.push(cube)
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var cube = new THREE.Mesh( geometry, material );
cube.vrz = 0.01;
cube.position.y += 5
scene.add( cube );
objects.push(cube)
var material = new THREE.MeshBasicMaterial( { color: 0x0000ff } );
var cube = new THREE.Mesh( geometry, material );
cube.vrz = 0.01;
scene.add( cube );
objects.push(cube)
// cube thats supposed to be not bloomy
var material = new THREE.MeshBasicMaterial( { color: 0xffffff } );
var cube = new THREE.Mesh( geometry, material );
cube.vrz = 0.01;
cube.position.y += 2.5
cube.position.x += 2.5
scene.add( cube );
objects.push(cube)
var gui = new dat.GUI();
gui.add( params, 'exposure', 0.1, 2 ).onChange( function ( value ) {
renderer.toneMappingExposure = Math.pow( value, 4.0 );
} );
gui.add( params, 'bloomThreshold', 0.0, 1.0 ).onChange( function ( value ) {
bloomPass.threshold = Number( value );
} );
gui.add( params, 'bloomStrength', 0.0, 3.0 ).onChange( function ( value ) {
bloomPass.strength = Number( value );
} );
gui.add( params, 'bloomRadius', 0.0, 1.0 ).step( 0.01 ).onChange( function ( value ) {
bloomPass.radius = Number( value );
} );
window.onresize = function () {
var width = window.innerWidth;
var height = window.innerHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize( width, height );
composer.setSize( width, height );
};
function animate() {
requestAnimationFrame( animate );
objects.forEach(function(obj){
obj.rotation.z += obj.vrz;
});
stats.update();
composer.render();
}
animate();
I had a similar problem once. An example from this comment helped me.
Note that in that example there are 2 scenes and 2 composers (the final composer gets output of the previous composer as its input)
ppoFinal.blendPass.uniforms.tAdd.value = ppoRGB.composer.renderTarget2.texture;
and render() is called on both composers.
ppoRGB.composer.render();
ppoFinal.composer.render();
This pattern allows you to apply postprocessing effects selectively and it works well. The problem is the scalability of the method and probably performance. Because when you want to apply another object with yet different effect, you need to introduce 3rd scene and 3rd composer. For my little project in the past I ended up with 4 scenes and 4 composers...

How to rotate the plane using three.js?

Hey guys I am using three.js and have tried to rotate this plane below but to avail. Any idea how to do it please? Below is the code which renders the plane using three.js. How can I rotate it, please?. Thanks!
var container;
var camera, scene, renderer;
var plane;
var mouse, raycaster, isShiftDown = false;
var cubeGeometry = new THREE.BoxBufferGeometry( 50, 50, 50 );
var cubeMaterial = new THREE.MeshLambertMaterial( { color: 0x00ff80, overdraw: 0.5 } );
var objects = [];
init();
render();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
var info = document.createElement( 'div' );
info.style.position = 'absolute';
info.style.top = '10px';
info.style.width = '100%';
info.style.textAlign = 'center';
info.innerHTML = 'three.js - voxel painter<br><strong>click</strong>: add voxel, <strong>shift + click</strong>: remove voxel, save .png';
container.appendChild( info );
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set( 500, 800, 1300 );
camera.lookAt( 0, 0, 0 );
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xf0f0f0 );
// Grid
var gridHelper = new THREE.GridHelper( 1000, 20 );
scene.add( gridHelper );
//
raycaster = new THREE.Raycaster();
mouse = new THREE.Vector2();
//PlaneBufferGeometry: platform to lay cubes
var geometry = new THREE.PlaneBufferGeometry( 10000, 10000 );
geometry.rotateX( - Math.PI / 2 );
plane = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { visible: false } ) );
scene.add( plane );
objects.push( plane );
// Lights
var ambientLight = new THREE.AmbientLight( 0x606060 );
scene.add( ambientLight );
var directionalLight = new THREE.DirectionalLight( 0xffffff );
directionalLight.position.x = Math.random() - 0.5;
directionalLight.position.y = Math.random() - 0.5;
directionalLight.position.z = Math.random() - 0.5;
directionalLight.position.normalize();
scene.add( directionalLight );
var directionalLight = new THREE.DirectionalLight( 0x808080 );
directionalLight.position.x = Math.random() - 0.5;
directionalLight.position.y = Math.random() - 0.5;
directionalLight.position.z = Math.random() - 0.5;
directionalLight.position.normalize();
scene.add( directionalLight );
renderer = new THREE.CanvasRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild(renderer.domElement);
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
//document.addEventListener( 'keydown', onDocumentKeyDown, false );
//document.addEventListener( 'keyup', onDocumentKeyUp, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
render();
}
function onDocumentMouseDown( event ) {
event.preventDefault();
mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( objects );
if ( intersects.length > 0 ) {
var intersect = intersects[ 0 ];
if ( isShiftDown ) {
if ( intersect.object != plane ) {
scene.remove( intersect.object );
objects.splice( objects.indexOf( intersect.object ), 1 );
}
} else {
var voxel = new THREE.Mesh( cubeGeometry, cubeMaterial );
voxel.position.copy( intersect.point ).add( intersect.face.normal );
voxel.position.divideScalar( 50 ).floor().multiplyScalar( 50 ).addScalar( 25 );
scene.add( voxel );
objects.push( voxel );
}
render();
}
}
function render() {
renderer.render( scene, camera );
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/renderers/CanvasRenderer.js"></script>
<script src="https://threejs.org/examples/js/renderers/Projector.js"></script>
Look at the Plane here
I recommend to use THREE.OrbitControls.
Add orbit control:
orbitControls = new THREE.OrbitControls(camera);
And animate your scene continuously:
function render() {
renderer.render( scene, camera );
requestAnimationFrame(render);
}
Further you have to manage how THREE.OrbitControls and the mouse event (onDocumentMouseDown) interact together.
A possibility would be to handle only the left mouse button in onDocumentMouseDown:
function onDocumentMouseDown( event ) {
if ( event.button != 0 ) // 0 means left mouse button
return;
And to rotate the scene by the right mouse button. This can be done by setting the .mouseButtons property of OrbitControls:
orbitControls.mouseButtons = {
LEFT: THREE.MOUSE.RIGHT,
MIDDLE: THREE.MOUSE.MIDDLE,
RIGHT: THREE.MOUSE.LEFT
}
Further note, that you can enable or disable pane, zoom and rotate by setting .enablePan, .enableZoom respectively .enableRotate.
See the following example, where the boxes are positioned by the left mouse button and the scene can be rotated by holding the right mouse button:
var container;
var camera, scene, renderer;
var plane;
var mouse, raycaster, isShiftDown = false;
var cubeGeometry = new THREE.BoxBufferGeometry( 50, 50, 50 );
var cubeMaterial = new THREE.MeshLambertMaterial( { color: 0x00ff80, overdraw: 0.5 } );
var objects = [];
init();
render();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set( 500, 800, 1300 );
camera.lookAt( 0, 0, 0 );
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xf0f0f0 );
var gridHelper = new THREE.GridHelper( 1000, 20 );
scene.add( gridHelper );
raycaster = new THREE.Raycaster();
mouse = new THREE.Vector2();
//PlaneBufferGeometry: platform to lay cubes
var geometry = new THREE.PlaneBufferGeometry( 10000, 10000 );
geometry.rotateX( - Math.PI / 2 );
plane = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { visible: false } ) );
scene.add( plane );
objects.push( plane );
// Lights
var ambientLight = new THREE.AmbientLight( 0x606060 );
scene.add( ambientLight );
var directionalLight = new THREE.DirectionalLight( 0xffffff );
directionalLight.position.x = Math.random() - 0.5;
directionalLight.position.y = Math.random() - 0.5;
directionalLight.position.z = Math.random() - 0.5;
directionalLight.position.normalize();
scene.add( directionalLight );
var directionalLight = new THREE.DirectionalLight( 0x808080 );
directionalLight.position.x = Math.random() - 0.5;
directionalLight.position.y = Math.random() - 0.5;
directionalLight.position.z = Math.random() - 0.5;
directionalLight.position.normalize();
scene.add( directionalLight );
renderer = new THREE.CanvasRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild(renderer.domElement);
orbitControls = new THREE.OrbitControls(camera);
orbitControls.mouseButtons = {
LEFT: THREE.MOUSE.RIGHT,
MIDDLE: THREE.MOUSE.MIDDLE,
RIGHT: THREE.MOUSE.LEFT
}
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseDown( event ) {
if ( event.button != 0 )
return;
event.preventDefault();
mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( objects );
if ( intersects.length > 0 ) {
var intersect = intersects[ 0 ];
if ( isShiftDown ) {
if ( intersect.object != plane ) {
scene.remove( intersect.object );
objects.splice( objects.indexOf( intersect.object ), 1 );
}
} else {
var voxel = new THREE.Mesh( cubeGeometry, cubeMaterial );
voxel.position.copy( intersect.point ).add( intersect.face.normal );
voxel.position.divideScalar( 50 ).floor().multiplyScalar( 50 ).addScalar( 25 );
scene.add( voxel );
objects.push( voxel );
}
}
}
function render() {
renderer.render( scene, camera );
requestAnimationFrame(render);
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/renderers/CanvasRenderer.js"></script>
<script src="https://threejs.org/examples/js/renderers/Projector.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

How can I make three.js update a scene after adding an object or group of objects?

I am fairly new to three.js and have a problem I can't readily find an answer for.
Here is a codepen that should sum up the situation: http://codepen.io/anon/pen/PPYPzO
var container, stats;
var camera, controls, scene, renderer, raycaster, mouse;
init();
animate();
add_world();
var indie_render = true;
for(var j = 0; j < 20; j++){
add_objects(20);
indie_render = !indie_render;
console.log("adding more objects...");
if(!indie_render){render();}
}
function add_world(){
var geometry = new THREE.BoxGeometry( 1000, 1000, 1000);
var mesh = new THREE.MeshBasicMaterial( {color: 0xf5f5dc, wireframe: false, opacity: 0.2, transparent:true } );
var world = new THREE.Mesh( geometry, mesh );
scene.add( world );
render();
}
function add_objects(num, indiv){
var geometry = new THREE.SphereGeometry( 5, 32,32 );
var material = new THREE.MeshBasicMaterial( { shading: THREE.FlatShading } );
material.color.setRGB( Math.random(), Math.random(), Math.random() );
for ( var i = 0; i < num; i ++ ) {
var mesh = new THREE.Mesh( geometry, material );
mesh.position.x = ( Math.random() - 0.5 ) * 1000;
mesh.position.y = ( Math.random() - 0.5 ) * 1000;
mesh.position.z = ( Math.random() - 0.5 ) * 1000;
mesh.updateMatrix();
mesh.matrixAutoUpdate = false;
scene.add( mesh );
if(indie_render){
console.log("individual render");
render();
}
}
}
function init() {
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.set(500, 500, -1000);
camera.up.set( 0, 1, 0 );
camera.lookAt(500,500,500);
controls = new THREE.OrbitControls( camera );
controls.addEventListener( 'change', render );
//world
scene = new THREE.Scene();
// lights
light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 1, 1, 1 );
scene.add( light );
light = new THREE.DirectionalLight( 0x002288 );
light.position.set( -1, -1, -1 );
scene.add( light );
light = new THREE.AmbientLight( 0x222222 );
scene.add( light );
renderer = new THREE.WebGLRenderer( { antialias: false } );
renderer.setClearColor( 0x000000, 1 );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.sortObjects = false;
container = document.getElementById( 'container' );
container.appendChild( renderer.domElement );
mouse = new THREE.Vector2();
raycaster = new THREE.Raycaster();
container.addEventListener( 'mousemove', onMouseMove, false );
container.addEventListener( 'mousedown', onMouseDown, false );
window.addEventListener( 'resize', onWindowResize, false );
}
function animate() {
requestAnimationFrame( animate );
controls.update();
}
function render() {
renderer.render( scene, camera );
}
function onMouseMove( e ) {
mouse.x = ( e.clientX / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( e.clientY / renderer.domElement.height ) * 2 + 1;
}
function onMouseDown( e ) {
mouse.x = ( e.clientX / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( e.clientY / renderer.domElement.height ) * 2 + 1;
if(e.button == 2){ //right button
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( scene.children, true );
if ( intersects.length > 0 ) {
var geometry = new THREE.SphereGeometry( 5, 32,32 );
var material = new THREE.MeshBasicMaterial( { color:0xff0000, shading: THREE.FlatShading } );
var mesh = new THREE.Mesh( geometry, material );
mesh.position.set(intersects[0].point.x, intersects[0].point.y, intersects[0].point.z);
scene.add(mesh);
render();
}
}
}
In this demo, I init() and animate() a blank scene, and then add a translucent cube, following what seems to be convention. Then I add groups of spheres to the scene in a nested for loop, randomly placing the spheres inside the cube and making a render() call after every scene.add() call.
Currently, the scene adds all the spheres and only then is visible to the user, even though I can add individual objects after the for-loop objects are added (by right-clicking on the cube). I need for the user to be able to watch as spheres are added, rather than waiting for the whole thing to be done.
I realize this may not be the most efficient way to render the scene, but it would be quite helpful if, for example, the info on the objects to be animated is arriving asynchronously from a server. Does anyone have a suggestion?
Thanks
1) First: move call render() to animate:
function animate() {
requestAnimationFrame( animate );
render();
controls.update();
}
2) Call add_objects asynchronously: setTimeout( add_objects, 0, 20 );
http://codepen.io/anon/pen/bVbEEP

Resources