I'm writing and animation loop using three.js and all the examples (mrdoob, stemkoski) I see online use unprotected globals at the beginning of the script. I tried to enclose these in the init() function and then pass them as arguments through the animation loop. However, the renderer is coming up undefined.
I'm not sure what I'm missing below. My primary question is how to understand best practice for setting up an animation loop with good closure (protecting variables that would otherwise be global). Thanks!
// THE MAIN ANIMATION LOOP:
// UPDATE the scene
function update(keyboard, controls, stats, clock) {
// delta = change in time since last call (in seconds)
var delta = clock.getDelta();
// functionality provided by THREEx.KeyboardState.js
if ( keyboard.pressed("z") )
{
// do something
}
controls.update();
stats.update();
};
// RENDER the scene
function render(renderer, scene, camera) {
renderer.render(scene, camera);
};
// ANIMATE the scene
function animate(scene, camera, renderer, controls, stats, keyboard, clock) {
requestAnimationFrame(animate);
render(renderer, scene, camera);
update(keyboard, controls, stats, clock);
};
// *********************
// INITIALIZES THE SCENE
function init(images) { // `images` is passed by a callback from loadImages
// standard global variables, held privately
var container, scene, camera, renderer, controls, stats;
var keyboard = new THREEx.KeyboardState();
var clock = new THREE.Clock();
///////////
// SCENE //
///////////
scene = new THREE.Scene();
////////////
// CAMERA //
////////////
// set the view size in pixels (custom or according to window size)
var SCREEN_WIDTH = 1920, SCREEN_HEIGHT = 1080;
// var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight;
// camera attributes
var VIEW_ANGLE = 20, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
// set up camera
camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
// add the camera to the scene
scene.add(camera);
// the camera defaults to position (0,0,0)
// so pull it back (z = 400) and up (y = 100) and set the angle towards the scene origin
// (x,y,z)
camera.position.set(0,150,1000);
camera.lookAt(scene.position);
//////////////
// RENDERER //
//////////////
// create and start the renderer; choose antialias setting.
if (Detector.webgl)
renderer = new THREE.WebGLRenderer( {antialias:true} );
else
renderer = new THREE.CanvasRenderer();
renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
// attach div element to variable to contain the renderer
container = document.getElementById( 'ThreeJS' );
// attach renderer to the container div
container.appendChild( renderer.domElement );
///////////
// STATS //
///////////
// displays current and past frames per second attained by scene
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.bottom = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
///////////
// LIGHT //
///////////
// create a light
var light = new THREE.PointLight(0xffffff);
light.position.set(100,250,0);
scene.add(light);
////////////
// IMAGES //
////////////
var element1 = THREE.ImageUtils.loadTexture(images.dresser10);
var element2 = THREE.ImageUtils.loadTexture(images.dresser14);
var element1Material = new THREE.SpriteMaterial( { map: element1, useScreenCoordinates: true, alignment: THREE.SpriteAlignment.topLeft } );
var sprite = new THREE.Sprite(element1Material);
sprite.position.set( 50, 50, 0 );
sprite.scale.set( 64, 64, 1.0 ); // imageWidth, imageHeight
scene.add(sprite);
animate(container, scene, camera, renderer, controls, stats, keyboard, clock);
};
// ********************************************************
// CHECKS TO SEE IF THE WINDOW HAS LOADED BEFORE PROCEEDING
// Once the window is loaded, calls the init function
window.addEventListener ("load", eventWindowLoaded, false);
function eventWindowLoaded() {
loadImages(init); // calls to initialize the scene once the images are loaded
}