Smallthing = Smallthing || {}

MANAGER3D = null;
__OBJECT_ID = 0;

/*

        /////////////////////////////////////////////////////////////////////////////////
        // create proton
        var proton = new Proton();
        proton.onUpdate = function(dtime)
        {
            this.update();
        }
        AUTOUPDATE.push(proton);

        /////////////////////////////////////////////////////////////////////////////////

        /////////////////////////////////////////////////////////////////////////////////
        // test proton
        var emitter = new Proton.Emitter();

        //setRate
        emitter.rate = new Proton.Rate(new Proton.Span(4, 16), new Proton.Span(.01));

        //addInitialize
        emitter.addInitialize(new Proton.Position(new Proton.PointZone(0, 0, 0)));
        emitter.addInitialize(new Proton.Mass(10));
        emitter.addInitialize(new Proton.Radius(1, 12));
        emitter.addInitialize(new Proton.Life(1,5));
        emitter.addInitialize(new Proton.V(1, new Proton.Vector3D(0, 1, 0), 10));

        //addBehaviour
        emitter.addBehaviour(new Proton.Alpha(1, 0));
        emitter.addBehaviour(new Proton.Scale(.1, .2));

        var color1 = new THREE.Color();
        var color2 = new THREE.Color(0xFF);
        var colorBehaviour = new Proton.Color(color1, color2);
        emitter.addBehaviour(colorBehaviour);
        emitter.emit();

        //add emitter
        proton.addEmitter(emitter);

        //add renderer
        proton.addRender(new Proton.SpriteRender(getManager3D().current_scene));
        /////////////////////////////////////////////////////////////////////////////////

*/


function detectWebGLContext () {

    // Create canvas element. The canvas is not added to the
    // document itself, so it is never displayed in the
    // browser window.
    var canvas = document.createElement("canvas3d");

    // Create canvas element. The canvas is not added to the
    // document itself, so it is never displayed in the
    // browser window.
    var canvas = document.createElement("canvas");
    // Get WebGLRenderingContext from canvas element.
    var gl = canvas.getContext("webgl")
      || canvas.getContext("experimental-webgl");
    // Report the result.
    if (gl && gl instanceof WebGLRenderingContext) {
        return true;
    } else {
        return false;
    }
}

sceneUpdate = function(dtime)
{

};

Smallthing.Manager3d = function()
{	
    isWebGL = detectWebGLContext();

    this.container = document.getElementById( 'playable' );

    if(typeof STAGE === 'undefined' || STAGE == null)
    {
        console.warn('detectWebGLContext failed');
        MANAGER3D = null;
        return;
    }

    MANAGER3D = this;
    M3D = MANAGER3D;

    getManager3D = function() { return M3D };
	if ( isWebGL )
		this.renderer = new THREE.WebGLRenderer( {antialias:true, alpha: true } );
	else
		this.renderer = new THREE.CanvasRenderer({antialias:true, alpha: true }); 

	this.current_scene = new THREE.Scene();    

	this.renderer.setPixelRatio( window.devicePixelRatio );
	this.renderer.setSize( window.innerWidth, window.innerHeight );
    this.renderer.setClearColor(0x000000,0xFF);

    this.renderer.shadowMap.enabled = false;
    this.renderer.shadowMap.type = THREE.BasicShadowMap;//PCFShadowMap;//THREE.PCFSoftShadowMap; // BasicShadowMap;

    this.width = window.innerWidth;
    this.height = window.innerHeight;

    if ( this.current_camera ) {

        this.current_camera.aspect = this.width / this.height;
        this.current_camera.updateProjectionMatrix();
    }

	this.container.appendChild( this.renderer.domElement );
    this.container.children[0].style.position = 'absolute';

    // prepare dds loader
    THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() );

    this.PerspectiveCamera = function(fov, width, height, near, far)
    {
        var ratio = window.innerWidth / window.innerHeight;
        if(fov == null)
            fov = 70;

        if(near == null)
            near = 0.1;

        if(far == null)
            far = 1000;

        if(width != null && height != null)
        {
            ratio = width / height;
        }

	    var camera = new THREE.PerspectiveCamera( fov, ratio, near, far );
        
        return camera;
    }

    this.setBackgroundColor = function(r, g, b)
    {
        if(this.current_scene != null)
            this.current_scene.background = new THREE.Color().setHSL( r, g, b );
    }

    this.setFog = function(near, far)
    {
        if(this.current_scene != null)
		    this.current_scene.fog = new THREE.Fog( this.current_scene.background, near, far );        
    }

    this.getObjectByName = function(name)
    {
        var o = this.current_scene.getObjectByName(name);
        return o;
    }

    this.setScene = function(scene)
    {
        var camera = scene.getObjectByName('PerspectiveCamera');
        if(camera != null)
        {
            console.log('CAMERA in scene found');
            this.setCamera(camera);
        }
        else 
        {
            console.log('CAMERA in scene loaded not found. Do you have define a "PerspectiveCamera" object?');
        }

        this.loc_center = scene.getObjectByName('_loc_world_center_');
        this.current_scene = scene;

        // hide every locators
        for(var i in scene.children)
        {
            var child = scene.children[i];
            if(child != null)
            {
                if(child.name.includes('_loc_'))
                {
                    child.visible = false;
                }
            }

            child.manager3d = getManager3D();
            Common3D_set(child);
        }
    }

    this.setCamera = function(camera)
    {
        this.current_camera = camera;
        this.current_camera.aspect = this.width / this.height;
        this.current_camera.near = 0.1;
        this.current_camera.far = 10000;
        this.current_camera.updateProjectionMatrix();
    }

    this.createCameraHelper = function(camera)
    {
        var helper = new THREE.CameraHelper( camera );
        this.current_scene.add( helper );
        return helper;
    }

    this.addLightAmbient = function(color, intensity)
    {
        var light = new THREE.AmbientLight(color, intensity);
	    this.current_scene.add(light);
	    return light;        
    }

    this.addLightPoint = function( color, intensity, distance, decay )
    {
        var light = new THREE.PointLight(color, intensity, distance, decay);
	    this.current_scene.add(light);
	    return light;        
    }

    this.addLightDirectional = function( color, intensity )
    {
        var light = new THREE.DirectionalLight(color, intensity);
	    this.current_scene.add(light);
	    return light;        
    }

    this.addShadowMapViewer = function(light)
    {
        if(this.shadowMapViewers == null)
            this.shadowMapViewers = [];

        var smw = new THREE.ShadowMapViewer( light );
        smw.size.set( 256, 256 );
        smw.position.set( 276, 10 );
        smw.update();

        this.shadowMapViewers.push(smw);        
    }    

    //AUTOUPDATE.push(this);

    this.getCurrentCamera = function()
    {
        return this.current_camera;
    };

    this.getCurrentScene = function()
    {
        return this.current_scene;
    };

    this.addToScene = function(o)
    {
        if(this.current_scene != null)
            this.current_scene.add(o);
    }

    // shaders
    this.composer = new THREE.EffectComposer( this.renderer );

    // prepare bloom
    this.bloom = {};

    this.bloom.params = {
				exposure: 1.0,
				bloomStrength: 0,
				bloomThreshold: .9,
				bloomRadius: 0.5
			};

    this.bloom.bloomPass = new THREE.UnrealBloomPass( new THREE.Vector2( window.innerWidth, window.innerHeight ), 1.5, 0.4, 0.85 );

    this.bloom.bloomPass.renderToScreen = true;
    // prepare motion blur

    this.motion_blur = {};

    this.motion_blur.params = {	
        enable:true
    };


    this.motion_blur.afterimagePass = new THREE.AfterimagePass(0.7);
    this.motion_blur.afterimagePass.renderToScreen = true;   

    this.bloom.bloomPass.enabled = true;
    this.motion_blur.afterimagePass.enabled = false;

    this.__resize();

    animate();
};

Smallthing.Manager3d.prototype.createCubeCamera = function()
{
    this.cubeCamera = new THREE.CubeCamera(0.1, 1000, 256); 
    this.cubeCamera.renderTarget.texture.mapping = THREE.CubeRefractionMapping;
    this.current_scene.add(this.cubeCamera);    
}

Smallthing.Manager3d.prototype.addComposers = function(scene, camera)
{
    this.renderScenePass = new THREE.RenderPass( scene, camera );
	this.composer.addPass( this.renderScenePass );
    this.composer.addPass( this.bloom.bloomPass );
    this.composer.addPass( this.motion_blur.afterimagePass );    

    this.renderScenePass.renderToScreen = true;

    this.renderScenePass.setSize(256,256);

    this.composer.render();
}

Smallthing.Manager3d.prototype.addBloom = function()
{
    //this.bloom.bloomPass.enabled = true;
    this.bloom.params.exposure = 1.0;
    this.bloom.params.bloomStrength = 1.0;
}

Smallthing.Manager3d.prototype.removeBloom = function()
{
    //this.bloom.bloomPass.enabled = false;
    this.bloom.params.exposure = 0.0;
    this.bloom.params.bloomStrength = 0.0;
};

Smallthing.Manager3d.prototype.addMotionBlur = function(t)
{
    this.motion_blur.afterimagePass.enabled = true;	
    if(t != null)
    {
        createTimeEvent(t, function(){
            this.motion_blur.afterimagePass.enabled = false;	
        }, this);
    }
};

Smallthing.Manager3d.prototype.removeMotionBlur = function()
{
    this.motion_blur.afterimagePass.enabled = false;   
};

Smallthing.Manager3d.prototype.destroy = function()
{

};

Smallthing.Manager3d.prototype.enableCameraNavigator = function()
{
    this.mouseX = 0;
    this.mouseY = 0;

    this.onDocumentMouseMove  = function (event) {
        M3D.mouseX = event.clientX - getManager3D().windowHalfX;
        M3D.mouseY = event.clientY - getManager3D().windowHalfY;
    };

    this.cameraNavigator = function(dtime)
    {
        var camera = this.getCurrentCamera();
        var scene = this.getCurrentScene();

        camera.position.x += ( M3D.mouseX - camera.position.x ) * 0.05;
        camera.position.y += ( - M3D.mouseY - camera.position.y ) * 0.05 + 15;
        camera.lookAt( scene.position );
    };

    document.addEventListener( 'mousemove', this.onDocumentMouseMove, false );
};

Smallthing.Manager3d.prototype.__resize = function()
{
    this.windowHalfX = window.innerWidth / 2;
	this.windowHalfY = window.innerHeight / 2;    

    if(this.current_camera == null)
        return;

    if(this.renderer == null)
        return;

	this.current_camera.aspect = window.innerWidth / window.innerHeight;
	this.current_camera.updateProjectionMatrix();
    this.renderer.setPixelRatio( window.devicePixelRatio );
	this.renderer.setSize( window.innerWidth, window.innerHeight );

    if(this.shadowMapViewers != null)
    {
        for(var i in this.shadowMapViewers)
        {
            var smv = this.shadowMapViewers[i];
            smv.updateForWindowResize();
        }
    }

    var pixelRatio = this.renderer.getPixelRatio();
    var newWidth  = Math.floor( window.innerWidth / pixelRatio ) || 1;
    var newHeight = Math.floor( window.innerHeight / pixelRatio ) || 1;
    if(this.composer != null)
    {
        var pixelRatio = window.devicePixelRatio || 1;
        this.composer.setSize(window.innerWidth * pixelRatio, window.innerHeight * pixelRatio);        
    }

    //this.composer.setSize( window.innerWidth, window.innerHeight );
};

Smallthing.Manager3d.prototype.orientationchange = function()
{
    this.__resize();
};

Smallthing.Manager3d.prototype.resize = function()
{
    this.__resize();
};

Smallthing.Manager3d.prototype.add = function(o)
{
    this.current_scene.add(o);
};

var onProgress = function ( xhr ) {

    if ( xhr.lengthComputable ) {
        
        var percentComplete = xhr.loaded / xhr.total * 100;
        console.log( Math.round( percentComplete, 2 ) + '% downloaded' );
    }
};

var onError = function ( xhr ) { 

    console.warn(xhr);
};

Smallthing.Manager3d.prototype.applyCubeMap = function(o)
{
    if(this.cubeCamera != null)
    {
        var t = this.cubeCamera.renderTarget.texture; // material.envMap = cubeCamera.renderTarget.texture;
        o.traverse( function ( child ) {
            if ( child instanceof THREE.Mesh ) {
                child.material.envMap = t;
            }   
        });         
    }

};

Smallthing.Manager3d.prototype.__receivedShadow = function(o){

    if(!this.renderer.shadowMap.enabled)
        return;

    o.traverse( function ( child ) {
        if ( child instanceof THREE.Mesh ) {
            child.castShadow = true;
            child.receiveShadow = true; 
        }   
    });    
}

Smallthing.Manager3d.prototype.createShadowMesh = function(obj, light)
{
    // GROUND
    if(this.plane_shadownmesh == null) {

        var normalVector = new THREE.Vector3( 0, 1, 0 );
        var planeConstant = 0.01; // this value must be slightly higher than the groundMesh's y position of 0.0
        this.plane_shadownmesh = new THREE.Plane( normalVector, planeConstant );

        // GROUND
        /*
        var groundGeometry = new THREE.BoxBufferGeometry( 30, 0.01, 40 );
        var groundMaterial = new THREE.MeshLambertMaterial( { color: 'rgb(0,130,0)' } );
        groundMesh = new THREE.Mesh( groundGeometry, groundMaterial );
        groundMesh.position.y = 0.0; //this value must be slightly lower than the planeConstant (0.01) parameter above
        this.current_scene.add( groundMesh );        
        */
    }

    var shadow = new THREE.ShadowMesh( obj );
    this.current_scene.add( shadow );
    if(this.shadowMeshes == null)
    {
        this.shadowMeshes = [];
    }

    this.shadowMeshes.push({mesh:shadow});
}

Smallthing.Manager3d.prototype.addOrbitControls = function(camera)
{
    this.orbitControls = new THREE.OrbitControls( camera, this.renderer.domElement );
    this.orbitControls.target.copy( new THREE.Vector3(0,0,0) );
    this.orbitControls.update();
}

__import_children = function(object, animations, receivedShadow)
{
    for(var i = 0; i < object.children.length; i++) 
    {

        var o = object.children[i];
        if(o.type === 'Mesh') {

            o.group = object;
            o.manager3d = getManager3D();

            // if(receivedShadow)
            //     getManager3D().__receivedShadow(o);

            Common3D_set(o);
        }
        else if(o.type === 'Object3D') {

            if(o.name === '')
            {
                o.name = file;
            }

            o.group = object;
            o.manager3d = getManager3D();

            // if(receivedShadow)
            //     getManager3D().__receivedShadow(o);

            Common3D_set(o);

            // rotate
            if(animations != null)
                o.isAnimated = true;
        }                
        else if(o.type === 'PerspectiveCamera')
        {
            getManager3D().current_camera = o;
            getManager3D().current_camera.near = 10.1;
            getManager3D().current_camera.far = 10.0;
        }
        else if(o.type === 'PointLight')
        {

        }
    }    
}

__add_animations = function(object, animations, autoupdate)
{
    if(animations != null && animations.length != null) {

        object.animation_mixer = new THREE.AnimationMixer( object );
        object.animation_actions = [];
        object.animation_clips = animations;

        if(autoupdate)
                AUTOUPDATE.push(object.animation_mixer);

        object.animation_mixer.onUpdate = function(dt)
        {
            this.update(dt);                    
        };

        object.animation_mixer.destroy = function()
        {
                                
        };  

        object.playAnimationByIndex = function(index)
        {
            try {
                this.current_action = object.animation_mixer.clipAction(object.animation_clips[index]);
                if(this.current_action != null)
                {
                    this.current_action.play();
                }
                else {

                    console.warn('error play animation, action not found');
                }
            }
            catch(error)
            {
                console.warn('error play animation', error);
            }
        }                      
    }
}

Smallthing.Manager3d.prototype.importFBX = function(file, loaded, loaded_this, receivedShadow)
{
/*
        getManager3D().importFBX('dance',  function(object){

            this.isDancerReady = true;

            var o = OBJECTMANAGER.getObjectByName('Armature');
            o.setScaleLocal(0.5, 0.5, 0.5);

            object.playAnimationByIndex(0);
            //object.setScale(0.5,0.5,0.5);

        }, this);
*/
    
    var loader = new THREE.FBXLoader();
    var fileraw = PRELOAD.get(file+'_FBX');

    loader.load(fileraw.src, function(fbx) {

        var object = fbx;

        object.traverse( function ( child ) {

            if ( child.isMesh ) {

                child.castShadow = true;
                child.receiveShadow = false;
            }
        } );

        __import_children(object, object.animations, receivedShadow);

        __add_animations(object, object.animations, false);

        Common3D_set(object);

        getManager3D().current_scene.add( object );

        GAME.Tween.get(this).call(loaded, [object], loaded_this);

    }, function(xhr) { 
            console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' ); 
    }, function(error) {
            console.warn(error);
    });
}

Smallthing.Manager3d.prototype.importGLTF = function(file, loaded, loaded_this, receivedShadow)
{

/*
        getManager3D().importGLTF('dance',  function(object){

            var camera = getManager3D().current_camera;

           // object.playAnimationByName('animation_0');

            camera.position.copy(new THREE.Vector3( 25, 25, 25 ));
            camera.lookAt(0,0,0);
            camera.near = 0.0001;
            camera.far = 10000.0;

            getManager3D().addOrbitControls(camera);

            var monster = OBJECTMANAGER.getObjectByName('Armature');
            object.playAnimationByIndex(0);
            monster.setScaleLocal(0.5, 0.5, 0.5);
        }, this);
*/

    var loader = new THREE.GLTFLoader();
    var fileraw = PRELOAD.get(file);

    // Load a glTF resource
    loader.load(

        // resource URL
        fileraw.src,

        // called when the resource is loaded
        function ( gltf ) {

            object = gltf.scene
            object.name = file;

            var animations = gltf.animations; // Array<THREE.AnimationClip>
            object.scene; // THREE.Scene
            object.scenes; // Array<THREE.Scene>
            object.cameras; // Array<THREE.Camera>
            object.asset; // Object

            // Common3D_set(object);
            object.traverse( function ( child ) {

                if ( child.isMesh ) {

                    child.castShadow = true;
                    child.receiveShadow = false;

                }
            } );

            __import_children(object, gltf.animations, receivedShadow);

            __add_animations(object, animations, true);

            //Common3D_set(object);
            //object.setRotation(0,0,0);
            getManager3D().current_scene.add( object );

            GAME.Tween.get(this).call(loaded, [object], loaded_this);
        },
        // called while loading is progressing
        function ( xhr ) {

            console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );

        },
        // called when loading has errors
        function ( error ) {

            console.log( 'An error happened', error );

        }
    );

};

Smallthing.Manager3d.prototype.import = function(file, loaded, loaded_this, loader, receivedShadow) {

    if(loader != null) {

        try {

            var fileraw = PRELOAD.get(file);
            loader.load( fileraw.src, function ( object ) {

                for(var i = 0; i < object.children.length; i++) {

                    var o = object.children[i];
                    if(o.type === 'Mesh') {

                        o.group = object;
                        o.manager3d = getManager3D();

                        // if(receivedShadow)
                        //     getManager3D().__receivedShadow(o);

                        Common3D_set(o);
                    }
                    else if(o.type === 'PerspectiveCamera')
                    {
                        getManager3D().current_camera = o;
                        getManager3D().current_camera.near = 10.1;
                        getManager3D().current_camera.far = 10.0;
                    }
                    else if(o.type === 'PointLight')
                    {

                    }

                }

                object.name = file;

                getManager3D().addToScene(object);                    

                GAME.Tween.get(this).call(loaded, [object], loaded_this);
            });
        }
        catch(e)
        {
            console.warn(e);
        }

        return;
    }

    var mtlLoader = new THREE.MTLLoader();
    var mtl = PRELOAD.get(file+'_MTL');

    mtlLoader.load(mtl.src, function(materials) {

        materials.preload();
        
        var objLoader = new THREE.OBJLoader();
        objLoader.setMaterials(materials);
        objLoader.load(PRELOAD.get(file+'_OBJ').src, function(object) {

            object.name = file;
            object.manager3d = getManager3D();

            // if(receivedShadow)
            //     getManager3D().__receivedShadow(object);

            Common3D_set(object);

            getManager3D().addToScene(object);

            GAME.Tween.get(this).call(loaded, [object], loaded_this);                

        }, onProgress, onError);
    });
}

Smallthing.Manager3d.prototype.createDefaultScene = function()
{
    this.current_scene = new THREE.Scene();
    this.current_scene.background = new THREE.Color( 0xa0a0a0 );
    this.current_scene.fog = new THREE.Fog( 0xa0a0a0, 200, 500 );    
}

Smallthing.Manager3d.prototype.importScene = function(file, c, cs)
{
    var loader = new THREE.FileLoader();
    loader.load( PRELOAD.get(file).src, function ( text ) {

        var sceneLoader = new THREE.SceneLoader();
        sceneLoader.parse(JSON.parse(text), 
            function (scene) 
            {
                getManager3D().setScene(scene);
                GAME.Tween.get(this).call(c, [scene], cs);
            }, '.');
    });
};

Smallthing.Manager3d.prototype.importApp = function (file, c, cs) 
{
    var loader = new THREE.FileLoader();

    this.callback_app_loaded = c;
    this.callback_app_loaded_scope = cs;

    loader.load( PRELOAD.get(file).src, function ( text ) {

        var m3d = getManager3D();
        m3d.currentSceneEditor = new Smallthing.Manager3d.SceneEditor();
        m3d.currentSceneEditor.load( JSON.parse( text ), function(){

        }, this );
    });
};

Smallthing.Manager3d.SceneEditor = function() {

    var loader = new THREE.ObjectLoader();
    var camera, scene, renderer;

    var events = {};

    this.load = function ( json, c, cs ) {

        renderer = getManager3D().renderer;

        var project = json.project;

        if ( project.gammaInput ) renderer.gammaInput = true;
        if ( project.gammaOutput ) renderer.gammaOutput = true;
        if ( project.shadows ) renderer.shadowMap.enabled = true;
        if ( project.vr ) renderer.vr.enabled = true;

        this.setScene( loader.parse( json.scene ) );
        this.setCamera( loader.parse( json.camera ) );

        events = {
            init: [],
            start: [],
            stop: [],
            keydown: [],
            keyup: [],
            mousedown: [],
            mouseup: [],
            mousemove: [],
            touchstart: [],
            touchend: [],
            touchmove: [],
            update: []
        };

        var scriptWrapParams = 'player,renderer,scene,camera';
        var scriptWrapResultObj = {};

        for ( var eventKey in events ) {

            scriptWrapParams += ',' + eventKey;
            scriptWrapResultObj[ eventKey ] = eventKey;

        }

        var scriptWrapResult = JSON.stringify( scriptWrapResultObj ).replace( /\"/g, '' );

        for ( var uuid in json.scripts ) {

            var object = scene.getObjectByProperty( 'uuid', uuid, true );

            if ( object === undefined ) {

                console.warn( 'APP.Player: Script without object.', uuid );
                continue;
            }

            var scripts = json.scripts[ uuid ];

            for ( var i = 0; i < scripts.length; i ++ ) {

                object.disableUpdate = true; // disable internal update, becuse we have a editor scritp

                var script = scripts[ i ];

                var functions = ( new Function( scriptWrapParams, script.source + '\nreturn ' + scriptWrapResult + ';' ).bind( object ) )( this, renderer, scene, camera );

                for ( var name in functions ) {

                    if ( functions[ name ] === undefined ) continue;

                    if ( events[ name ] === undefined ) {

                        console.warn( 'APP.Player: Event type not supported (', name, ')' );
                        continue;

                    }

                    events[ name ].push( functions[ name ].bind( object ) );

                }

            }

        }

        dispatch( events.init, arguments );

        if(getManager3D().callback_app_loaded != null)
        {
            GAME.Tween.get(this).call(getManager3D().callback_app_loaded, [getManager3D()], getManager3D().callback_app_loaded_scope);
        }

    };

    this.setCamera = function ( value ) {

        camera = value;
        camera.aspect = this.width / this.height;
        camera.updateProjectionMatrix();

        if ( renderer.vr.enabled ) {

            dom.appendChild( WEBVR.createButton( renderer ) );

        }

        getManager3D().setCamera(camera);
    };

    this.setScene = function ( value ) {

        scene = value;
        getManager3D().setScene(value);
    };

    function dispatch( array, event ) {

        for ( var i = 0, l = array.length; i < l; i ++ ) {

            array[ i ]( event );
        }

    }

    var prevTime;

    this.play = function () {

        prevTime = performance.now();

        dispatch( events.start, arguments );
    };

    this.stop = function () {

        dispatch( events.stop, arguments );
    };

    this.dispose = function () {

        while ( dom.children.length ) {

            dom.removeChild( dom.firstChild );

        }

        renderer.dispose();

        camera = undefined;
        scene = undefined;
        renderer = undefined;

    };

    //

    function onDocumentKeyDown( event ) {

        dispatch( events.keydown, event );

    }

    function onDocumentKeyUp( event ) {

        dispatch( events.keyup, event );

    }

    function onDocumentMouseDown( event ) {

        dispatch( events.mousedown, event );

    }

    function onDocumentMouseUp( event ) {

        dispatch( events.mouseup, event );

    }

    function onDocumentMouseMove( event ) {

        dispatch( events.mousemove, event );

    }

    function onDocumentTouchStart( event ) {

        dispatch( events.touchstart, event );

    }

    function onDocumentTouchEnd( event ) {

        dispatch( events.touchend, event );

    }

    function onDocumentTouchMove( event ) {

        dispatch( events.touchmove, event );

    }
}

function Create3D() {
    return new Smallthing.Manager3d();
}

lastTime = null;

function animate(time) {

    var m = getManager3D();
    var dtime = 0;
	if (lastTime) 
    {
        dtime = time - lastTime;
        
        if (1000 / dtime < 30 && createjs.Ticker.framerate > 10) {

            createjs.Ticker.framerate = 10;
        } else if (createjs.Ticker.framerate === 10) {

            createjs.Ticker.framerate = 40;
        }
	}

	lastTime = time;
    
    m.onUpdate(dtime);

    requestAnimationFrame( animate );    
}

Smallthing.Manager3d.prototype.onUpdate = function(dtime)
{
    if(this.current_camera == null)
        return;

    if(this.current_scene == null)
        return;
        
    if(this.cameraNavigator != null)
    {
        this.cameraNavigator();
    }

    if(this.shadowMapViewers != null) {

        for(var i in this.shadowMapViewers)
        {
            var smv = this.shadowMapViewers[i];
            smv.render(this.renderer);
            smv.update();
        }    
    }

    if(this.shadowMeshes != null) {

        var lightPosition4D = {};

        lightPosition4D.x = 5;
        lightPosition4D.y = 7;
        lightPosition4D.z = -1;
        lightPosition4D.w = 0.001; // more of a directional Light value


        for(var i in this.shadowMeshes)
        {
            var o = this.shadowMeshes[i];
            o.mesh.update(this.plane_shadownmesh, lightPosition4D);
        }
    }


    //this.composer.addPass( this.renderScenePass );
    //this.composer.addPass( this.bloom.bloomPass );
    //this.composer.addPass( this.motion_blur.afterimagePass );    

    if(this.cubeCamera != null)
    {
        this.cubeCamera.update(this.renderer, this.current_scene);
    }

    if(this.orbitControls != null) {
        this.orbitControls.update();
    }

    if(this.renderScenePass != null) {

        if(this.bloom.bloomPass.enabled && !this.motion_blur.afterimagePass.enabled) {
            this.renderScenePass.renderToScreen = false;
            this.motion_blur.afterimagePass.renderToScreen = false;
            this.bloom.bloomPass.renderToScreen = true;
        }

        else if(!this.bloom.bloomPass.enabled && this.motion_blur.afterimagePass.enabled) {
            this.renderScenePass.renderToScreen = false;
            this.motion_blur.afterimagePass.renderToScreen = true;
            this.bloom.bloomPass.renderToScreen = false;
        }

        else if(this.bloom.bloomPass.enabled && this.motion_blur.afterimagePass.enabled) {
            this.renderScenePass.renderToScreen = false;
            this.motion_blur.afterimagePass.renderToScreen = true;
            this.bloom.bloomPass.renderToScreen = false;
        }
        else {

            this.renderScenePass.renderToScreen = true;
            this.motion_blur.afterimagePass.renderToScreen = false;
            this.bloom.bloomPass.renderToScreen = false;
        }

        this.bloom.bloomPass.threshold = this.bloom.params.bloomThreshold;
        this.bloom.bloomPass.strength = this.bloom.params.bloomStrength;
        this.bloom.bloomPass.radius = this.bloom.params.bloomRadius;
                
        this.composer.render();
    }
    else {

        this.renderer.render( this.current_scene, this.current_camera );
    }

    //
};
