"use strict";

// Copyright 2012 United States Government, as represented by the Secretary of Defense, Under
// Secretary of Defense (Personnel & Readiness).
// 
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software distributed under the License
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
// or implied. See the License for the specific language governing permissions and limitations under
// the License.

    function rebuildAllMaterials( obj )
    {
        
        if( obj === undefined )
        {
            for(var i in this.state.scenes)
            {
                rebuildAllMaterials.call( this, this.state.scenes[i].threeScene );
            }
        } else {
            if(obj && obj.material)
            {
                obj.material.needsUpdate = true;
            }
            if(obj && obj.children)
            {
               for(var i in obj.children)
                rebuildAllMaterials.call( this, obj.children[i] );
            }
        }
    }

    function matCpy( mat )
    {
        var ret = [];
        for ( var i =0; i < mat.length; i++ )
            ret.push( mat[i] );

        // I don't think there is any reason we need to copy the return array
        return ret;
        // return ret.slice(0);    
    }
    
    
define( [ "module", 
          "vwf/model", 
          "vwf/utility", 
          "vwf/utility/color", 
          "jquery" 
        ], 

    function( module, model, utility, Color, $ ) {

    var self;

    var checkLights = true;
    var sceneCreated = false;

    return model.load( module, {

        // == Module Definition ====================================================================

        // -- initialize ---------------------------------------------------------------------------

        initialize: function() {
            
            self = this;

            checkCompatibility.call(this);

            this.state.scenes = {}; // id => { glgeDocument: new GLGE.Document(), glgeRenderer: new GLGE.Renderer(), glgeScene: new GLGE.Scene() }
            this.state.nodes = {}; // id => { name: string, glgeObject: GLGE.Object, GLGE.Collada, GLGE.Light, or other...? }
            this.state.prototypes = {}; 
            this.state.kernel = this.kernel.kernel.kernel; 
            this.state.lights = {};           
 
            this.state.setMeshPropertyRecursively = function( threeObject, propertyName, value ) {
                if ( !threeObject ) {
                    return;
                }
                threeObject[ propertyName ] = value;
                var meshes = findAllMeshes( threeObject );
                for ( var i = 0; i < meshes.length; i++ ) {
                    meshes[ i ][ propertyName ] = value;
                }
            }

            this.state.setGeometryPropertyRecursively = function( threeObject, propertyName, value ) {
                if ( !threeObject ) {
                    return;
                }
                threeObject[ propertyName ] = value;
                var geoList = findAllGeometries( threeObject );
                for ( var i = 0; i < geoList.length; i++ ) {
                    geoList[ i ][ propertyName ] = value;
                }
            }

            // turns on logger debugger console messages 
            this.debug = {
                "creation": false,
                "initializing": false,
                "parenting": false,
                "deleting": false,
                "properties": false,
                "setting": false,
                "getting": false,
                "prototypes": false
            };
        },


        // == Model API ============================================================================

        // -- creatingNode ------------------------------------------------------------------------
        
        creatingNode: function( nodeID, childID, childExtendsID, childImplementsIDs,
                                childSource, childType, childIndex, childName, callback ) {
            self = this;

            // If the parent nodeID is 0, this node is attached directly to the root and is therefore either 
            // the scene or a prototype.  In either of those cases, save the uri of the new node
            var childURI = ( nodeID === 0 ? childIndex : undefined );
            var appID = this.kernel.application();

            if ( this.debug.creation  ) {
                this.logger.infox( "creatingNode", nodeID, childID, childExtendsID, childImplementsIDs, childSource, childType, childName );
            }

            // If the node being created is a prototype, construct it and add it to the array of prototypes,
            // and then return
            var prototypeID = utility.ifPrototypeGetId( appID, this.state.prototypes, nodeID, childID );
            if ( prototypeID !== undefined ) {
                
                if ( this.debug.prototypes ) {
                    this.logger.infox( "prototype: ", prototypeID );
                }

                this.state.prototypes[ prototypeID ] = {
                    parentID: nodeID,
                    ID: childID,
                    extendsID: childExtendsID,
                    implementsID: childImplementsIDs,
                    source: childSource, 
                    type: childType,
                    uri: childURI,
                    name: childName,
                };
                return;                
            }
            
            var node = undefined;
            var parentNode;
            var threeChild;
            var threeParent;
            var waiting = false;
           
            if ( nodeID )
            {
                parentNode = this.state.nodes[ nodeID ];

                // If parent is not a node, see if it is a scene
                if ( !parentNode )
                    parentNode = this.state.scenes[ nodeID ];

                if ( parentNode )
                {
                    threeParent = parentNode.threeObject ? parentNode.threeObject : parentNode.threeScene;
                    if ( threeParent && childName )
                    {
                        threeChild = FindChildByName.call( this,threeParent,childName,childExtendsID,false );
                    }
                }               
            }
            var kernel = this.kernel.kernel.kernel;
            
            var protos = getPrototypes.call( this, kernel, childExtendsID );
            if ( isSceneDefinition.call(this, protos) && childID == this.kernel.application() )
            {
                var sceneNode = CreateThreeJSSceneNode( nodeID, childID, childExtendsID );
                this.state.scenes[ childID ] = sceneNode;
                this.state.cameraInUse = sceneNode.camera.defaultCamera;

                sceneCreated = true;

                if ( childImplementsIDs && childImplementsIDs.length > 0 ) {
                    for ( var i = 0; i < childImplementsIDs.length; i++ ) {
                        switch ( childImplementsIDs[ i ] ) {
                            case "http://vwf.example.com/threejs/fogExp2.vwf":
                                sceneNode.threeScene.fog = new THREE.FogExp2( 0x000000 );
                                break;

                            case "http://vwf.example.com/threejs/fog.vwf":
                                sceneNode.threeScene.fog = new THREE.Fog( 0x000000 );
                                break;

                        }
                    }
                }
            }
            
            if ( protos && isCameraDefinition.call( this, protos ) ) {

                var sceneID = this.kernel.application();
                var camName = this.kernel.name( childID );
                var sceneNode = this.state.scenes[ sceneID ];
                node = this.state.nodes[ childID ] = {
                    name: childName,
                    threeObject: threeChild,
                    ID: childID,
                    parentID: nodeID,
                    sceneID: this.kernel.application(),
                    threeScene: sceneNode ? sceneNode.threeScene : undefined,
                    type: childExtendsID,
                    sourceType: childType,
                    prototypes: protos
                };
                // if there was not a preexisting object, then you have to make a new camera
                if ( node.threeObject === undefined ) {
                    if ( nodeID === sceneID && childName === "camera" ) {
                        node.threeObject = sceneNode.camera.defaultCamera;
                        if ( sceneNode.camera.ID !== undefined ) {
                            sceneNode.camera.ID = childID;    
                        }
                    } else {
                        createCamera.call( this, nodeID, childID, childName );    
                    }
                }
             
            } else if(protos && isLightDefinition.call( this, protos )) {
                
                node = this.state.nodes[ childID ] = this.state.lights[ childID ] = {
                    name: childName,
                    threeObject: threeChild,
                    ID: childID,
                    parentID: nodeID,
                    type: childExtendsID,
                    sourceType: childType,
                };

                if ( !node.threeObject ) {
                    createLight.call( this, nodeID, childID, childExtendsID, childName );
                } else {
                    if ( !( node.threeObject instanceof THREE.Light ) ) {

                        if ( node.threeObject.children ) {
                            var child = undefined;
                            var light = undefined;
                            for ( var j = 0; light === undefined && 
                                  j < node.threeObject.children.length; j++ ) {
                                
                                child = node.threeObject.children[ j ];
                                switch ( childExtendsID ) {
                    
                                    case "http://vwf.example.com/directionallight.vwf":
                                        if ( child instanceof THREE.DirectionalLight ) {
                                            light = child;    
                                        }
                                        break;

                                    case "http://vwf.example.com/spotlight.vwf":
                                        if ( child instanceof THREE.SpotLight ) {
                                            light = child;    
                                        }
                                        break;

                                    case "http://vwf.example.com/hemispherelight.vwf":
                                        if ( child instanceof THREE.HemisphereLight ) {
                                            light = child;    
                                        }
                                        break;

                                    case "http://vwf.example.com/pointlight.vwf":
                                    default:
                                        if ( child instanceof THREE.PointLight ) {
                                            light = child;    
                                        }
                                        break;    
                                }                            
                            }
                            if ( light !== undefined ) {
                                node.threeObject = light;    
                            }
                        }
                    }
                }
            
            } else if ( protos && isMaterialDefinition.call( this, protos ) ) {

                var mat;
                var matDef = undefined;

                if ( parentNode && parentNode.threeObject ) {
                    mat = GetMaterial( parentNode.threeObject, childName );    
                }
                node = this.state.nodes[childID] = {
                    name: childName,
                    threeObject: mat,
                    ID: childID,
                    parentID: nodeID,
                    type: childExtendsID,
                    sourceType: childType
                };

                if ( childType !== undefined ) {
                    
                    if ( childType !== "material/definition" ) {

                        // define childType to be one of the material types
                        // then the uniform properties and be set in the 
                        // uniforms component as a child of this component
                        matDef = { "type": childType };
                    }
                } else if ( !node.threeObject ) {
                    matDef = { "type": "MeshPhongMaterial" };
                }

                if ( matDef !== undefined ) {
                    node.threeObject = createMaterial( matDef );
                    if ( node.threeObject ) {
                        if ( parentNode && parentNode.threeObject ) {
                            SetMaterial( parentNode.threeObject, node.threeObject, childName );    
                        } else {
                            console.info( "unable to find: " + nodeID );
                        }
                    }
                }

            } else if ( protos && isShaderMaterialDefinition.call( this, protos ) ) {

                node = this.state.nodes[ childID ] = {
                    name: childName,
                    //threeObject: GetMaterial( parentNode.threeObject, childName ),
                    threeObject: undefined,
                    ID: childID,
                    parentID: nodeID,
                    type: childExtendsID,
                    sourceType: childType
                };

                if ( childType !== undefined ) {
                    
                    if ( childType !== "shader/definition" ) {

                        // define childType to be one of the preexisting shaderTypes
                        // then the uniform properties and be set in the 
                        // uniforms component as a child of this component
                        node.threeObject = createMaterial( { "type": 'ShaderMaterial', "shaderType": childType } );
                        node.shaderType = childType;
                    }

                } else {
                    node.threeObject = new THREE.ShaderMaterial();                   
                }

                if ( node.threeObject ) {
                    if ( parentNode && parentNode.threeObject ) {
                        SetMaterial( parentNode.threeObject, node.threeObject, childName );    
                    }
                }

            } else if ( protos && isShaderUniformsDefinition.call( this, protos ) ) {
                
                var mat = this.state.nodes[ nodeID ];

                node = this.state.nodes[ childID ] = {
                    name: childName,
                    threeObject: undefined,
                    ID: childID,
                    parentID: nodeID,
                    type: childExtendsID,
                    sourceType: childType,
                    isUniformObject: true
                };

                if ( mat ) {
                    node.threeObject = mat.threeObject.uniforms;
                }

            } else if ( protos && isTextureDefinition.call( this, protos ) ) {

                var mat = this.state.nodes[ nodeID ];

                node = this.state.nodes[ childID ] = {
                    name: childName,
                    threeObject: undefined,
                    ID: childID,
                    parentID: nodeID,
                    type: childExtendsID,
                    sourceType: childType,
                    isUniformObject: true
                };

                if ( mat ) {
                    node.threeObject = mat.threeObject.map;
                }

            } else if ( protos && isParticleDefinition.call( this, protos ) ) {
                
                node = this.state.nodes[childID] = {
                    name: childName,
                    threeObject: threeChild,
                    ID: childID,
                    parentID: nodeID,
                    type: childExtendsID,
                    sourceType: childType,
                };
                
                if(!node.threeObject)
                {   
                    CreateParticleSystem.call(this,nodeID,childID,childName);
                }
            } else if ( protos && isNodeDefinition.call( this, protos ) && childName !== undefined ) {
                
                var sceneNode = this.state.scenes[ this.kernel.application() ];
                
                if ( supportedFileType( childType ) ) {
                    
                    // Most often this callback is used to suspend the queue until the load is complete
                    callback( false );

                    node = this.state.nodes[ childID ] = {
                        name: childName,  
                        threeObject: threeChild,
                        source: utility.resolveURI( childSource, this.kernel.uri( childID, true ) ),
                        ID: childID,
                        parentID: nodeID,
                        sourceType: childType,
                        type: childExtendsID,
                        // Hang on to the callback and call it again in assetLoaded with ready=true
                        loadingCallback: callback,
                        sceneID: this.kernel.application()
                    };
                    loadAsset.call( this, parentNode, node, childType, notifyDriverOfPrototypeAndBehaviorProps );     
                }
                else if ( childType == "mesh/definition" ) {
                    
                    //callback( false );
                    node = this.state.nodes[ childID ] = {
                        name: childName,  
                        source: utility.resolveURI( childSource, this.kernel.uri( childID, true ) ),
                        ID: childID,
                        parentID: nodeID,
                        sourceType: childType,
                        type: childExtendsID,
                        sceneID: this.kernel.application(),
                        prototypes: protos,
                    };
                    node.threeObject = new THREE.Object3D(); 
                    node.threeObject.name = childName; 
                    if ( threeParent !== undefined ) {
                        threeParent.add( node.threeObject ); 
                    } 
                } else {     

                    node = this.state.nodes[childID] = {
                        name: childName,  
                        threeObject: threeChild,
                        source: utility.resolveURI( childSource, this.kernel.uri( childID, true ) ),
                        ID: childID,
                        parentID: nodeID,
                        sourceType: childType,
                        type: childExtendsID,
                        //no load callback, maybe don't need this?
                        //loadingCallback: callback,
                        sceneID: this.kernel.application(),
                        prototypes: protos,
                    };
                    if( !node.threeObject )
                        node.threeObject = findThreeObjectInParent.call(this,childName,nodeID);
                    //The parent three object did not have any childrent with the name matching the nodeID, so make a new group
                    if( !node.threeObject ) {
                        // doesn't this object need to be added to the parent node
                        node.threeObject = new THREE.Object3D(); 
                        node.threeObject.name = childName;
                        if ( threeParent !== undefined ) {
                            threeParent.add( node.threeObject ); 
                        } 
                    }
                }
            
                if ( node && node.threeObject )
                {
                    if ( !node.threeObject.vwfID )
                        node.threeObject.vwfID = childID;
                    if ( !node.threeObject.name )
                        node.threeObject.name = childName;
                }
            
            }

            updateStoredTransform( node );

            // If we do not have a load a model for this node, then we are almost done, so we can update all
            // the driver properties w/ the stop-gap function below.
            // Else, it will be called at the end of the assetLoaded callback
            if ( ! supportedFileType( childType ) ) {
                notifyDriverOfPrototypeAndBehaviorProps();
            }

            // Since prototypes are created before the object, it does not get "setProperty" updates for
            // its prototype (and behavior) properties.  Therefore, we cycle through those properties to
            // notify the drivers of the property values so they can react accordingly
            // TODO: Have the kernel send the "setProperty" updates itself so the driver need not
            // NOTE: Identical code exists in GLGE driver, so if an change is necessary, it should be made
            //       there, too
            function notifyDriverOfPrototypeAndBehaviorProps() {
                var ptPropValue;
                var protos = getPrototypes.call( this, kernel, childExtendsID );
                protos.forEach( function( prototypeID ) {
                    for ( var propertyName in kernel.getProperties( prototypeID ) ) {
                        ptPropValue = kernel.getProperty( childExtendsID, propertyName );
                        if ( ptPropValue !== undefined && ptPropValue !== null && childID !== undefined && childID !== null) {
                            self.settingProperty( childID, propertyName, ptPropValue );
                        }
                    }
                } );
                childImplementsIDs.forEach( function( behaviorID ) {
                    for ( var propertyName in kernel.getProperties( behaviorID ) ) {
                        ptPropValue = kernel.getProperty( behaviorID, propertyName );
                        if ( ptPropValue !== undefined && ptPropValue !== null && childID !== undefined && childID !== null) {
                            self.settingProperty( childID, propertyName, ptPropValue );
                        }
                    }
                } );
            };

        },

        initializingNode: function( nodeID, childID, childExtendsID, childImplementsIDs,
            childSource, childType, childIndex, childName ) {
            var myNode = this.state.nodes[childID];
            
            if ( this.debug.initializing ) {
                this.logger.infox( "initializingNode", nodeID, childID, childExtendsID, childImplementsIDs, childSource, childType, childName );
            } 

            if ( myNode && !( myNode.threeObject instanceof THREE.Material ) ) {
                generateNodeMaterial.call( this, childID, myNode );//Potential node, need to do node things!
            }
        },
         
        // -- deletingNode -------------------------------------------------------------------------

        deletingNode: function( nodeID ) {

            if ( this.debug.deleting ) {
                this.logger.infox( "deletingNode", nodeID );
            }

            if(nodeID)
            {
                var childNode = this.state.nodes[nodeID];
                if(childNode)
                {
                    var threeObject = childNode.threeObject;
                    if(threeObject && threeObject.parent)
                    {
                        threeObject.parent.remove(threeObject);
                    }
                    delete this.state.nodes[childNode];
                }               
            }
        },

        // -- addingChild ------------------------------------------------------------------------
        
        addingChild: function( nodeID, childID, childName ) {

            var threeObjParent = getThreeObject.call( this, nodeID );
            var threeObjChild = getThreeObject.call( this, childID ); 
            
            if ( threeObjParent && threeObjChild ) { 
                if ( threeObjParent instanceof THREE.Object3D ) {

                    if ( !( threeObjChild instanceof THREE.Material ) ) {
                        var childParent = threeObjChild.parent;
                        if ( childParent !== threeObjParent ) {
                            // what does vwf do here?  add only if parent is currently undefined
                            if ( childParent ) {
                                childParent.remove( threeObjChild )   
                            } 
                            threeObjParent.add( threeObjChild );
                        }
                    } else {
                        // TODO
                        // this is adding of a material
                    }
                }
            }

        },

        // -- movingChild ------------------------------------------------------------------------
        
        movingChild: function( nodeID, childID, childName ) {

            var threeObjParent = getThreeObject.call( this, nodeID );
            var threeObjChild = getThreeObject.call( this, childID ); 
            
            if ( threeObjParent && threeObjChild && ( threeObjParent instanceof THREE.Object3D ) ){
                var childParent = threeObjChild.parent;
                // do we only move if there is currently a parent
                if ( childParent && ( childParent !== threeObjParent ) ) {
                    childParent.remove( threeObjChild );
                    threeObjParent.add( threeObjChild );   
                } 
            } 

        },

        // -- removingChild ------------------------------------------------------------------------
        
        removingChild: function( nodeID, childID, childName ) {
            
            var threeObjParent = getThreeObject.call( this, nodeID );
            var threeObjChild = getThreeObject.call( this, childID );
            if ( threeObjParent && threeObjChild && ( threeObjParent instanceof THREE.Object3D ) ){    

                var childParent = threeObjChild.parent;
                if ( childParent === threeObjParent ) {
                    childParent.remove( threeObjChild )   
                } 
            } 
            
        },

        // -- creatingProperty ---------------------------------------------------------------------

        creatingProperty: function( nodeID, propertyName, propertyValue ) {
            if ( this.debug.properties ) {
                this.logger.infox( "C === creatingProperty ", nodeID, propertyName, propertyValue );
            }
            return this.initializingProperty( nodeID, propertyName, propertyValue );
        },

        // -- initializingProperty -----------------------------------------------------------------

        initializingProperty: function( nodeID, propertyName, propertyValue ) {

            var value = undefined;

            if ( this.debug.properties ) {
                this.logger.infox( "  I === initializingProperty ", nodeID, propertyName, propertyValue );
            }

            if ( propertyValue !== undefined ) {
                var node = this.state.nodes[ nodeID ];
                if ( node === undefined ) node = this.state.scenes[ nodeID ];
                if ( node !== undefined ) {
                    
                    var objectType, objectDef;

                    switch ( propertyName ) {
                        
                        case "meshDefinition":
                            createMesh.call( this, node, propertyValue, true );
                            value = propertyValue; 
                            break;

                        case "shaderDefinition":
                            objectType = propertyValue.type || propertyValue.shaderType || propertyValue;
                            objectDef = { "type": 'ShaderMaterial', "shaderType": objectType };
                            if ( propertyValue instanceof Object ) {
                                for ( var prop in propertyValue ) {
                                    switch ( prop ) {
                                        case "type":
                                        case "shaderType":
                                            break;

                                        default:
                                            objectDef[ prop ] = propertyValue[ prop ];
                                            break;    
                                    }
                                }   
                            }

                            node.threeObject = createMaterial( objectDef );
                            value = propertyValue; 
                            if ( node.threeObject ) {
                                var parentNode = this.state.nodes[ node.parentID ];
                                if ( parentNode && parentNode.threeObject ) {
                                    SetMaterial( parentNode.threeObject, node.threeObject, node.name );
                                }    
                            }
                            break;

                        case "materialDefinition":
                            objectType = propertyValue.type || propertyValue;
                            objectDef = { "type": objectType };
                            if ( propertyValue instanceof Object ) {
                                for ( var prop in propertyValue ) {
                                    switch ( prop ) {
                                        
                                        case "type":
                                            break;

                                        case "color":
                                        case "specular":
                                        case "emissive":
                                            objectDef[ prop ] = new THREE.Color( propertyValue[ prop ] );
                                            break;

                                        case "shininess":
                                        case "bumpScale":
                                        case "reflectivity":                                        
                                        case "wireframeLinewidth":
                                        case "refractionRatio":
                                        case "opacity":
                                        case "linewidth":
                                        case "scale":
                                        case "dashSize":
                                        case "gapSize":
                                        case "overdraw":
                                        case "alphaTest":
                                        case "polygonOffsetFactor":
                                        case "polygonOffsetUnits":
                                        case "size":
                                            objectDef[ prop ] = parseFloat( propertyValue[ prop ] );
                                            break;

                                        case "map":
                                        case "specularMap":
                                        case "normalMap":
                                        case "alphaMap":
                                        case "bumpMap":
                                        case "lightMap":
                                            objectDef[ prop ] = loadTexture( undefined, propertyValue[ prop ] );
                                            break;

                                        case "envMap":
                                            objectDef[ prop ] = THREE.ImageUtils.loadTextureCube( propertyValue[ prop ] );
                                            break;

                                        case "normalScale":
                                        case "uvOffset":
                                        case "uvScale":
                                            objectDef[ prop ] = new THREE.Vector2( propertyValue[ prop ][ 0 ], propertyValue[ prop ][ 1 ] );
                                            break;

                                        case "wrapRGB":
                                            objectDef[ prop ] = new THREE.Vector3( propertyValue[ prop ][ 0 ], propertyValue[ prop ][ 1 ], propertyValue[ prop ][ 2 ] );
                                            break;

                                        case "wrapAround":
                                        case "metal":
                                        case "fog":
                                        case "skinning":
                                        case "morphTargets":
                                        case "morphNormals":
                                        case "wireframe":
                                        case "depthTest":
                                        case "depthWrite":
                                        case "transparent":
                                        case "polygonOffset":
                                        case "visible":
                                        case "lights":
                                            objectDef[ prop ] = Boolean( propertyValue[ prop ] );
                                            break;

                                        case "vertexColors":
                                            switch ( propertyValue[ prop ] ) {

                                                case "true":
                                                    objectDef[ prop ] = true;
                                                    break;    

                                                case "false":
                                                    objectDef[ prop ] = false;
                                                    break; 

                                                case 1:
                                                case "1":
                                                case "face":
                                                    objectDef[ prop ] = THREE.FaceColors;
                                                    break;

                                                case 2:
                                                case "2":
                                                case "vertex":
                                                    objectDef[ prop ] = THREE.VertexColors;
                                                    break;

                                                case 0:
                                                case "0":
                                                case "no":
                                                default:
                                                    objectDef[ prop ] = THREE.NoColors;
                                                    break;
                                            }                                            
                                            break;

                                        case "blendSrc":
                                        case "blendDst":
                                            switch ( propertyValue[ prop ] ) {

                                                case 200:
                                                case "200":
                                                case "zero":
                                                    objectDef[ prop ] = THREE.ZeroFactor;
                                                    break;

                                                case 201:
                                                case "201":
                                                case "one":
                                                    objectDef[ prop ] = THREE.OneFactor;
                                                    break;

                                                case 202:
                                                case "202":
                                                case "srcColor":
                                                    objectDef[ prop ] = THREE.SrcColorFactor;
                                                    break;

                                                case 203:
                                                case "203":
                                                case "oneMinusSrcColor":
                                                    objectDef[ prop ] = THREE.OneMinusSrcColorFactor;
                                                    break;

                                                case 204:
                                                case "204":
                                                case "srcAlpha":
                                                    objectDef[ prop ] = THREE.SrcAlphaFactor;
                                                    break;

                                                case 205:
                                                case "205":
                                                case "oneMinusSrcAlpha":
                                                    objectDef[ prop ] = THREE.OneMinusSrcAlphaFactor;
                                                    break;

                                                case 206:
                                                case "206":
                                                case "dstAlpha":
                                                    objectDef[ prop ] = THREE.DstAlphaFactor;
                                                    break;

                                                case 207:
                                                case "207":
                                                case "oneMinusDstAlpha":
                                                    objectDef[ prop ] = THREE.OneMinusDstAlphaFactor;
                                                    break;

                                                case 208:
                                                case "208":
                                                case "dstColor":
                                                    objectDef[ prop ] = THREE.DstColorFactor;
                                                    break;

                                                case 209:
                                                case "209":
                                                case "oneMinusDstColor":
                                                    objectDef[ prop ] = THREE.OneMinusDstColorFactor;
                                                    break;

                                                case 210:
                                                case "210":
                                                case "srcAlphaSaturate":
                                                    objectDef[ prop ] = THREE.SrcAlphaSaturateFactor;
                                                    break;

                                            }
                                            break;

                                        case "blendEquation":
                                            switch ( propertyValue[ prop ] ) {

                                                case 100:
                                                case "100":
                                                case "add":
                                                    objectDef[ prop ] = THREE.AddEquation;
                                                    break;

                                                case 101:
                                                case "101":
                                                case "sub":
                                                case "subtract":
                                                    objectDef[ prop ] = THREE.SubtractEquation;
                                                    break;

                                                case 102:
                                                case "102":
                                                case "revSub":
                                                case "revSubtract":
                                                    objectDef[ prop ] = THREE.ReverseSubtractEquation;
                                                    break;

                                                case 103:
                                                case "103":
                                                case "min":
                                                    objectDef[ prop ] = THREE.MinEquation;
                                                    break;

                                                case 104:
                                                case "104":
                                                case "max":
                                                    objectDef[ prop ] = THREE.MaxEquation;
                                                    break;
                                            }
                                            break;

                                        case "combine":
                                            switch ( propertyValue[ prop ] ) {

                                                case 1:
                                                case "1":
                                                case "mix":
                                                    objectDef[ prop ] = THREE.MixOperation;
                                                    break;

                                                case 2:
                                                case "2":
                                                case "add":
                                                    objectDef[ prop ] = THREE.AddOperation;
                                                    break;

                                                case 0:
                                                case "0":
                                                case "mult":
                                                case "multiply":
                                                default:
                                                    objectDef[ prop ] = THREE.MultiplyOperation;
                                                    break;
                                            }
                                            break;

                                        case "shading":
                                            switch ( propertyValue[ prop ] ) {

                                                case 1:
                                                case "1":
                                                case "flat":
                                                    objectDef[ prop ] = THREE.FlatShading;
                                                    break;

                                                case 2:
                                                case "2":
                                                case "smooth":
                                                    objectDef[ prop ] = THREE.SmoothShading;
                                                    break;

                                                case 0:
                                                case "0":
                                                case "no":
                                                default:
                                                    objectDef[ prop ] = THREE.NoShading;
                                                    break;
                                            }
                                            break;

                                        case "blending":
                                            switch ( propertyValue[ prop ] ) {

                                                case 1:
                                                case "1":
                                                case "normal":
                                                    objectDef[ prop ] = THREE.NormalBlending;
                                                    break;

                                                case 2:
                                                case "2":
                                                case "add":
                                                case "additive":
                                                    objectDef[ prop ] = THREE.AdditiveBlending;
                                                    break;

                                                case 3:
                                                case "3":
                                                case "sub":
                                                case "subtractive":
                                                    objectDef[ prop ] = THREE.SubtractiveBlending;
                                                    break;

                                                case 4:
                                                case "4":
                                                case "mult":
                                                case "multiply":
                                                    objectDef[ prop ] = THREE.MultiplyBlending;
                                                    break;

                                                case 5:
                                                case "5":
                                                case "custom":
                                                    objectDef[ prop ] = THREE.CustomBlending;
                                                    break;

                                                case 0:
                                                case "0":
                                                case "no":
                                                default:
                                                    objectDef[ prop ] = THREE.NoBlending;
                                                    break;
                                            }
                                            break;

                                        case "side":
                                            switch ( propertyValue[ prop ] ) {

                                                case 2:
                                                case "2":
                                                case "double":
                                                    objectDef[ prop ] = THREE.DoubleSide;
                                                    break;

                                                case 1:
                                                case "1":                        
                                                case "back":
                                                    objectDef[ prop ] = THREE.BackSide;
                                                    break;

                                                case 0:
                                                case "0":                        
                                                case "front":
                                                default:
                                                    objectDef[ prop ] = THREE.FrontSide;
                                                    break;
                                            }
                                            break;

                                        case "linecap":
                                        case "linejoin": 
                                        default:
                                            objectDef[ prop ] = propertyValue[ prop ];
                                            break;    
                                    }
                                }   
                            }

                            node.threeObject = createMaterial( objectDef );
                            value = propertyValue; 
                            if ( node.threeObject ) {
                                var parentNode = this.state.nodes[ node.parentID ];
                                if ( parentNode && parentNode.threeObject ) {
                                    SetMaterial( parentNode.threeObject, node.threeObject, node.name );
                                }    
                            }
                            break;

                        default:
                            value = this.settingProperty( nodeID, propertyName, propertyValue );                  
                            break;

                    }

                }
            }

            return value;
            
        },

        // -- settingProperty ----------------------------------------------------------------------

        settingProperty: function( nodeID, propertyName, propertyValue ) {

            if ( this.debug.properties || this.debug.setting ) {
                this.logger.infox( "    S === settingProperty ", nodeID, propertyName, propertyValue );
            }

            var node = this.state.nodes[ nodeID ]; // { name: childName, glgeObject: undefined }
            if( node === undefined ) node = this.state.scenes[ nodeID ]; // { name: childName, glgeObject: undefined }
            var value = undefined;

            //this driver has no representation of this node, so there is nothing to do.
            if(!node) return;

            var parentNode = this.state.nodes[ node.parentID ];
            if ( parentNode === undefined ) {
                parentNode = this.state.scenes[ node.parentID ];    
            }
            var threeObject = node.threeObject;
            if ( !threeObject )
                threeObject = node.threeScene;

            //There is not three object for this node, so there is nothing this driver can do. return
            if(!threeObject) return value;    
          
            if ( propertyValue !== undefined ) 
            {
                self = this;
                if ( threeObject instanceof THREE.Object3D )
                {
                    // Function to make the object continuously look at a position or node
                    // (for use when setting 'transform' or 'lookAt')
                    // An almost identical function is copied in view/threejs.js, so if any modifications are made here, they 
                    // should be made there, also
                    var lookAt = function( lookAtValue ) {

                        // Function to make the object look at a particular position
                        // (For use in the following conditional)
                        var lookAtWorldPosition = function( targetWorldPos ) {
                            
                            // Get the eye position
                            var eye = new THREE.Vector3();
                            var worldTransform = getWorldTransform( node );
                            eye.setFromMatrixPosition( worldTransform );

                            var look = new THREE.Vector3();
                            look.subVectors( targetWorldPos, eye );
                                
                            if ( look.length() > 0 ) {
                                look.normalize();

                                // Set the up vector to be z
                                var roughlyUp = new THREE.Vector3();
                                roughlyUp.set( 0, 0, 1 );

                                var right = new THREE.Vector3();
                                right.crossVectors( look, roughlyUp );
                                if ( right.length() == 0 ) {
                                    look.x += 0.0001;
                                    right.crossVectors( look, roughlyUp );
                                }
                                right.normalize();

                                var up = new THREE.Vector3();
                                up.crossVectors( right, look );

                                var worldTransformArray = worldTransform.elements;
                                worldTransformArray[ 0 ] = right.x;  
                                worldTransformArray[ 1 ] = right.y; 
                                worldTransformArray[ 2 ] = right.z; 
                                worldTransformArray[ 4 ] = look.x;
                                worldTransformArray[ 5 ] = look.y; 
                                worldTransformArray[ 6 ] = look.z; 
                                worldTransformArray[ 8 ] = up.x;
                                worldTransformArray[ 9 ] = up.y;
                                worldTransformArray[ 10 ] = up.z;

                                setWorldTransform( node, worldTransform );
                            }
                        }

                        // The position for the object to look at - to be set in the following conditional
                        var targetWorldPos = new THREE.Vector3();

                        //Threejs does not currently support auto tracking the lookat,
                        //instead, we'll take the position of the node and look at that.
                        if ( utility.isString( lookAtValue ) ) {
                            
                            // We use '' to denote that there is no object to look at.
                            // Therefore, we only care if it is something other than that.
                            if ( lookAtValue != '' ) {
                                var lookatNode = self.state.nodes[ lookAtValue ];
                                
                                if ( lookatNode )
                                {
                                    node.lookatval = lookAtValue;
                                    var targetWorldTransform = getWorldTransform( lookatNode );
                                    targetWorldPos.setFromMatrixPosition( targetWorldTransform );
                                    lookAtWorldPosition( targetWorldPos );                         
                                } else {
                                    self.logger.errorx( "Lookat node does not exist: '" + lookAtValue + "'" );
                                }
                            }
                        
                        } else if ( lookAtValue instanceof Array ) {
                            node.lookatval = lookAtValue;
                            targetWorldPos.set( lookAtValue[0], lookAtValue[1], lookAtValue[2] );
                            lookAtWorldPosition( targetWorldPos );   
                        } else if ( !lookAtValue ) {
                            node.lookatval = null;
                        } else {
                            self.logger.errorx( "Invalid lookat property value: '" + lookAtValue + "'" );
                        }
                        return node.lookatval;
                    }

                    // Begin handling properties

                    if ( propertyName == 'transform' && node.transform ) {

                        //console.info( "setting transform of: " + nodeID + " to " + Array.prototype.slice.call( propertyValue ) );
                        var transformMatrix = goog.vec.Mat4.createFromArray( propertyValue || [] );
                        if( threeObject instanceof THREE.PointCloud )
                        {   
                            threeObject.updateTransform(propertyValue);
                        }
                        // Store the value locally
                        // It must be stored separately from the threeObject so the view can change the
                        // threeObject's transform to get ahead of the model state without polluting it
                        node.transform.elements = matCpy( transformMatrix );

                        value = propertyValue;

                        //because threejs does not do auto tracking of lookat, we must do it manually.
                        //after updating the matrix for an ojbect, if it's looking at something, update to lookat from
                        //the new position
                        if ( node.lookatval ) {
                            lookAt( node.lookatval );
                        }

                        setTransformsDirty( threeObject );
                    }
                    else if ( propertyName == 'lookAt' ) {
                        value = lookAt( propertyValue );
                    }
                    else if ( propertyName == 'visible' )
                    {
                        value = Boolean( propertyValue );
                        // this was the old style of recursively setting visible
                        // self.state.setMeshPropertyRecursively( threeObject, "visible", value );
                        SetVisible( threeObject, value );
                        // SetVisible will only set visible on the children
                        // that the driver has NOT binding to, bad/good, was the old way better?
                    }
                    else if ( propertyName == 'castShadows' )
                    {
                        value = Boolean( propertyValue );

                        // TODO: We should call setMeshPropertyRecursively here instead of repeating code
                        threeObject.castShadow = value;
                        var meshes = findAllMeshes.call( this, threeObject );
                        for(var i = 0, il = meshes.length; i < il; i++) {
                            meshes[i].castShadow = value;
                        }
                    }
                    else if ( propertyName == 'receiveShadows' )
                    {
                        value = Boolean( propertyValue );

                        // TODO: We should call setMeshPropertyRecursively here instead of repeating code
                        threeObject.receiveShadow = value;
                        var meshes = findAllMeshes.call( this, threeObject );
                        for(var i = 0, il = meshes.length; i < il; i++) {
                            meshes[i].receiveShadow = value;
                        }
                    }

                    //This can be a bit confusing, as the node has a material property, and a material child node. 
                    //setting the property does this, but the code in the component is ambigious
                    else if ( propertyName == 'material' )
                    {
                        var material = GetMaterial(node.threeObject);
                        if(!material)
                        {   
                            material = new THREE.MeshPhongMaterial();
                            SetMaterial(node.threeObject,material);
                        }
                        if(propertyValue == 'red')
                            material.color.setRGB(1,0,0);
                        if(propertyValue == 'green')
                            material.color.setRGB(0,1,0);
                        if(propertyValue == 'blue')
                            material.color.setRGB(0,0,1);
                        if(propertyValue == 'purple')
                            material.color.setRGB(1,0,1);
                        if(propertyValue == 'orange')
                            material.color.setRGB(1,.5,0);
                        if(propertyValue == 'yellow')
                            material.color.setRGB(1,1,0);   
                        if(propertyValue == 'gray')
                            material.color.setRGB(.5,.5,.5);
                        if(propertyValue == 'white')
                            material.color.setRGB(1,1,1);
                        if(propertyValue == 'black')
                            material.color.setRGB(0,0,0);                           
                        material.ambient.setRGB( material.color.r,material.color.g,material.color.b);

                        value = propertyValue;
                    }

                    else if ( propertyName == "animationTimeUpdated" ) {

                        // Keyframe Animations
                        if ( node.threeObject.kfAnimations && node.threeObject.kfAnimations.length && propertyValue !== undefined ) {
                            for ( var i = 0; i < node.threeObject.kfAnimations.length; i++ ) {
                                node.threeObject.kfAnimations[i].stop()
                                node.threeObject.kfAnimations[i].play( false, 0 );
                                node.threeObject.kfAnimations[i].update( propertyValue );
                            } 
                        }
                        
                        // Both JSON and Collada models can be skinned mesh animations, but the Collada loader does not support bones
                        // therefore Collada models will fall in the Morph Target conditional if applicable.

                        // Skeletal Animations (takes precedence over Morph Target)
                        if ( node.threeObject.bones && node.threeObject.bones.length > 0 ) {
                            var animRate = this.state.kernel.getProperty( nodeID, "animationRate" ) || 1;
                            THREE.AnimationHandler.update(animRate);
                        } 
                        // Morph Target Animations
                        else if ( node.threeObject.animatedMesh && node.threeObject.animatedMesh.length && propertyValue !== undefined ) {
                            var fps = this.state.kernel.getProperty( nodeID, "animationFPS" ) || 30;
                            for( var i = 0; i < node.threeObject.animatedMesh.length; i++ ) {
                                if ( node.threeObject.animatedMesh[i].morphTargetInfluences ) {
                                    for( var j = 0; j < node.threeObject.animatedMesh[i].morphTargetInfluences.length; j++ ) {
                                        node.threeObject.animatedMesh[i].morphTargetInfluences[j] = 0;
                                    }
                                    node.threeObject.animatedMesh[i].morphTargetInfluences[ Math.floor(propertyValue * fps) ] = 1;
                                }
                            }
                        }

                        // the transform is being stored locally which is probably the 
                        // main source of the problem( animated transforms are incorrect while 
                        // being animated ), I'm not sure why this has been done
                        
                        //updateStoredTransform( node );

                        // calling updateStoredTransform here seemed to be
                        // too big of a performance hit, so setting a flag
                        // to be checked in the getter before getting the transform 
                        // node.storedTransformDirty = true;
                        setTransformsDirty( node.threeObject );

                    }

                    else if ( propertyName == "animationDuration" ) {
                        if( node.threeObject.animatedMesh && node.threeObject.animatedMesh.length || node.threeObject.kfAnimations ) {
                            value = this.gettingProperty( nodeID, "animationDuration" );
                        }
                    }

                    else if ( propertyName == "animationFPS" ) {
                        if( node.threeObject.animatedMesh && node.threeObject.animatedMesh.length || node.threeObject.kfAnimations ) {
                            value = this.gettingProperty( nodeID, "animationFPS" );
                        }
                    }
                }
                if( threeObject instanceof THREE.PointCloud )
                {
                    var ps = threeObject;
                    var particles = ps.geometry;

                    switch( propertyName ) {
                        case 'emitterSize':
                        case 'emitterType':
                        case 'gravity':
                        case 'gravityCenter':
                        case 'velocityMode':
                        case 'damping':
                        case 'maxRate':
                            ps[propertyName] = propertyValue;
                            if( ps.material == ps.shaderMaterial_analytic ) {
                                ps.rebuildParticles();
                            }
                            break;
                        case 'size':
                            ps[propertyName] = propertyValue;
                            for( var i = 0; i < ps.material.attributes.size.value.length; i++ ) {
                                ps.material.attributes.size.value[i] = propertyValue;
                            }
                            ps.material.attributes.size.needsUpdate = true;
                            break;
                        case 'particleCount':
                            ps.setParticleCount(propertyValue);
                            break;
                        case 'startSize':
                            ps[propertyName] = propertyValue;
                            ps.shaderMaterial_analytic.uniforms.startSize.value = propertyValue;
                            // ps.material.uniforms.startSize.value = propertyValue;
                            break;
                        case 'endSize':
                            ps[propertyName] = propertyValue;
                            ps.shaderMaterial_analytic.uniforms.endSize.value = propertyValue;
                            // ps.material.uniforms.endSize.value = propertyValue;
                            break;
                        case 'sizeRange':
                            ps[propertyName] = propertyValue;
                            ps.shaderMaterial_analytic.uniforms.sizeRange.value = propertyValue;
                            // ps.material.uniforms.sizeRange.value = propertyValue;
                            break;
                        case 'maxSpin':
                            ps[propertyName] = propertyValue;
                            ps.shaderMaterial_analytic.uniforms.maxSpin.value = propertyValue;
                            // ps.material.uniforms.maxSpin.value = propertyValue;
                            break;
                        case 'minSpin':
                            ps[propertyName] = propertyValue;
                            ps.shaderMaterial_analytic.uniforms.minSpin.value = propertyValue;
                            // ps.material.uniforms.minSpin.value = propertyValue;
                            break;
                        case 'textureTiles':
                            ps[propertyName] = propertyValue;
                            ps.shaderMaterial_analytic.uniforms.textureTiles.value = propertyValue;
                            // ps.material.uniforms.textureTiles.value = propertyValue;
                            break;
                        case 'maxOrientation':
                            ps[propertyName] = propertyValue;
                            ps.shaderMaterial_analytic.uniforms.maxOrientation.value = propertyValue;
                            // ps.material.uniforms.maxOrientation.value = propertyValue;
                            break;
                        case 'minOrientation':
                            ps[propertyName] = propertyValue;
                            ps.shaderMaterial_analytic.uniforms.minOrientation.value = propertyValue;
                            // ps.material.uniforms.minOrientation.value = propertyValue;
                            break;
                        case 'colorRange':
                            ps[propertyName] = propertyValue;
                            ps.shaderMaterial_analytic.uniforms.colorRange.value.x = propertyValue[0];
                            ps.shaderMaterial_analytic.uniforms.colorRange.value.y = propertyValue[1];
                            ps.shaderMaterial_analytic.uniforms.colorRange.value.z = propertyValue[2];
                            ps.shaderMaterial_analytic.uniforms.colorRange.value.w = propertyValue[3];
                            break;
                        case 'startColor':
                            ps[propertyName] = propertyValue;
                            ps.shaderMaterial_analytic.uniforms.startColor.value.x = propertyValue[0];
                            ps.shaderMaterial_analytic.uniforms.startColor.value.y = propertyValue[1];
                            ps.shaderMaterial_analytic.uniforms.startColor.value.z = propertyValue[2];
                            ps.shaderMaterial_analytic.uniforms.startColor.value.w = propertyValue[3];
                            break;
                        case 'endColor':
                            ps[propertyName] = propertyValue;
                            ps.shaderMaterial_analytic.uniforms.endColor.value.x = propertyValue[0];
                            ps.shaderMaterial_analytic.uniforms.endColor.value.y = propertyValue[1];
                            ps.shaderMaterial_analytic.uniforms.endColor.value.z = propertyValue[2];
                            ps.shaderMaterial_analytic.uniforms.endColor.value.w = propertyValue[3];
                            break;
                        case 'solver':
                            ps[propertyName] = propertyValue;
                            ps.setSolverType(propertyValue);
                            break;
                        case 'image':
                            ps[propertyName] = propertyValue;
                            ps.shaderMaterial_default.uniforms.texture.value = loadTexture( undefined, propertyValue );
                            ps.shaderMaterial_default.uniforms.useTexture.value = 1.0;
                            ps.shaderMaterial_analytic.uniforms.texture.value = loadTexture( undefined, propertyValue );
                            ps.shaderMaterial_analytic.uniforms.useTexture.value = 1.0;
                            break;
                        case 'additive':
                            ps[propertyName] = propertyValue;
                            if ( Boolean( propertyValue ) )
                            {
                                ps.shaderMaterial_default.blending = THREE.AdditiveBlending;
                                ps.shaderMaterial_default.transparent = true;
                                ps.shaderMaterial_analytic.blending = THREE.AdditiveBlending;
                                ps.shaderMaterial_analytic.transparent = true;
                                ps.shaderMaterial_interpolate.blending = THREE.AdditiveBlending;
                                ps.shaderMaterial_interpolate.transparent = true;
                            }
                            else
                            {
                                ps.shaderMaterial_default.blending = THREE.NormalBlending;  
                                ps.shaderMaterial_default.transparent = true;
                                ps.shaderMaterial_analytic.blending = THREE.NormalBlending; 
                                ps.shaderMaterial_analytic.transparent = true;
                                ps.shaderMaterial_interpolate.blending = THREE.NormalBlending; 
                                ps.shaderMaterial_interpolate.transparent = true;
                            }

                            ps.shaderMaterial_default.needsUpdate = true;   
                            ps.shaderMaterial_analytic.needsUpdate = true;   
                            ps.shaderMaterial_interpolate.needsUpdate = true; 
                            break;
                        case 'depthTest':
                            ps[propertyName] = propertyValue;
                            ps.shaderMaterial_default.depthTest = propertyValue;
                            ps.shaderMaterial_analytic.depthTest = propertyValue;   
                            ps.shaderMaterial_interpolate.depthTest = propertyValue;
                            break;
                        case 'depthWrite':
                            ps[propertyName] = propertyValue;
                            ps.shaderMaterial_default.depthWrite = propertyValue;
                            ps.shaderMaterial_analytic.depthWrite = propertyValue;
                            ps.shaderMaterial_interpolate.depthWrite = propertyValue;
                            break;
                        case 'minAcceleration':
                        case 'maxAcceleration':
                            ps[propertyName] = propertyValue;
                            if(!ps.minAcceleration) ps.minAcceleration = [0,0,0];
                            if(!ps.maxAcceleration) ps.maxAcceleration = [0,0,0];
                            
                            for(var i = 0; i < particles.vertices.length; i++)
                            {
                                particles.vertices[i].acceleration.x = ps.minAcceleration[0] + (ps.maxAcceleration[0] - ps.minAcceleration[0]) * Math.random();
                                particles.vertices[i].acceleration.y = ps.minAcceleration[1] + (ps.maxAcceleration[1] - ps.minAcceleration[1]) * Math.random();
                                particles.vertices[i].acceleration.z = ps.minAcceleration[2] + (ps.maxAcceleration[2] - ps.minAcceleration[2]) * Math.random();
                            }
                            if( ps.material == ps.shaderMaterial_analytic ) {
                                ps.rebuildParticles();
                            }
                            break;
                        case 'minVelocity':
                        case 'maxVelocity':
                            ps[propertyName] = propertyValue;
                            if(!ps.minVelocity) ps.minVelocity = [0,0,0];
                            if(!ps.maxVelocity) ps.maxVelocity = [0,0,0];
                            
                            for(var i = 0; i < particles.vertices.length; i++)
                            {
                                particles.vertices[i].velocity.x = ps.minVelocity[0] + (ps.maxVelocity[0] - ps.minVelocity[0]) * Math.random();
                                particles.vertices[i].velocity.y = ps.minVelocity[1] + (ps.maxVelocity[1] - ps.minVelocity[1]) * Math.random();
                                particles.vertices[i].velocity.z = ps.minVelocity[2] + (ps.maxVelocity[2] - ps.minVelocity[2]) * Math.random();
                            }
                            if( ps.material == ps.shaderMaterial_analytic ) {
                                ps.rebuildParticles();
                            }
                            break;
                        case 'minLifeTime':
                        case 'maxLifeTime':
                            ps[propertyName] = propertyValue;
                            if(ps.minLifeTime === undefined) ps.minLifeTime = 0;
                            if(ps.maxLifeTime === undefined) ps.maxLifeTime = 1;
                            
                            for(var i = 0; i < particles.vertices.length; i++)
                            {   
                                particles.vertices[i].lifespan = ps.minLifeTime + (ps.maxLifeTime - ps.minLifeTime) * Math.random();
                            }
                            break;
                    }

                }
                if(threeObject instanceof THREE.Camera)
                {
                    if(propertyName == "fovy")
                    {
                        if(propertyValue)
                        {
                            value = parseFloat(propertyValue);
                            threeObject.fov = value;
                            threeObject.updateProjectionMatrix();
                        }
                    }
                    if(propertyName == "near")
                    {
                        if(propertyValue)
                        {
                            value = parseFloat(propertyValue);
                            threeObject.near = value;
                            threeObject.updateProjectionMatrix();
                        }
                    }
                    if(propertyName == "aspect")
                    {
                        if(propertyValue)
                        {
                            value = parseFloat(propertyValue);
                            threeObject.aspect = value; 
                            threeObject.updateProjectionMatrix();                           
                        }
                    }
                    if(propertyName == "far")
                    {
                        if(propertyValue)
                        {
                            value = parseFloat(propertyValue);
                            threeObject.far = value;                    
                            threeObject.updateProjectionMatrix();
                        }
                    }
                    if(propertyName == "cameraType")
                    {   
                        if(propertyValue == 'perspective')
                        {
                            value = propertyValue;
                            var parent = threeObject.parent;
                            if(parent && threeObject && !(threeObject instanceof THREE.PerspectiveCamera))
                            {
                                var sceneNode = this.state.scenes[ this.kernel.application() ];
                                parent.remove(threeObject);
                                var cam = new THREE.PerspectiveCamera(35,$(document).width()/$(document).height() ,.01,10000);
                                cam.far = threeObject.far;
                                cam.near = threeObject.near;
                                cam.matrix.elements = matCpy(threeObject.matrix.elements);
                                cam.matrixAutoUpdate = false;
                                if ( threeObject.fov )
                                    cam.fov = threeObject.fov;
                                if ( threeObject.aspect )
                                    cam.aspect = threeObject.aspect;  

                                // If the camera we are replacing, is the active camera,
                                // set the active camera  
                                if ( this.state.cameraInUse == threeObject )
                                    this.state.cameraInUse = cam;

                                threeObject.updateProjectionMatrix();   
                                node.threeObject = cam;
                                parent.add(node.threeObject);
                            }
                        }
                        if(propertyValue == 'orthographic')
                        {
                            value = propertyValue;
                            var parent = threeObject.parent;
                            if(parent && threeObject && !(threeObject instanceof THREE.OrthographicCamera))
                            {
                                
                                var sceneNode = this.state.scenes[ this.kernel.application() ];
                                parent.remove(threeObject);
                                var offset  = threeObject.far * Math.cos(threeObject.fov/2 * 0.0174532925);
                                offset = offset/2;
                                var aspect = threeObject.aspect;
                                var cam = new THREE.OrthographicCamera(-offset,offset,offset/aspect,-offset/aspect,threeObject.near,threeObject.far);
                                cam.far = threeObject.far;
                                cam.near = threeObject.near;
                                cam.matrix = threeObject.matrix;
                                cam.matrixAutoUpdate = false;
                                if ( threeObject.fov )
                                    cam.fov = threeObject.fov;
                                if ( threeObject.aspect )
                                    cam.aspect = threeObject.aspect;

                                // If the camera we are replacing, is the active camera, 
                                // set the active camera     
                                if ( this.state.cameraInUse == threeObject )
                                    this.state.cameraInUse = cam;

                                node.threeObject = cam;
                                parent.add(node.threeObject);
                            }
                        }
                    }
                }
                if ( threeObject instanceof THREE.Material ) {

                    value = setMaterialProperty( threeObject, propertyName, propertyValue );

                    if ( value !== undefined ) {
                        if ( this.state.nodes[ node.parentID ] && this.state.nodes[ node.parentID ].threeObject ) {
                            var obj3 = this.state.nodes[ node.parentID ].threeObject;
                            this.state.setGeometryPropertyRecursively( obj3, "uvsNeedUpdate", true );
                        }                          
                    }

                }
                if ( threeObject instanceof THREE.Texture ) {

                    value = setTextureProperty( threeObject, propertyName, propertyValue );

                    if ( value !== undefined ) {
                        if ( this.state.nodes[ node.parentID ] && this.state.nodes[ node.parentID ].threeObject ) {
                            var obj3 = this.state.nodes[ node.parentID ].threeObject;
                            this.state.setGeometryPropertyRecursively( obj3, "uvsNeedUpdate", true );
                        }                          
                    }

                }
                if ( threeObject instanceof THREE.ShaderMaterial ) {
                    
                    if ( utility.validObject( propertyValue ) ) {
                        
                        if ( propertyName === "uniforms" ) {
                            value = propertyValue;
                            threeObject.uniforms = value;
                        }
                        if ( propertyName === "vertexShader" ) {
                            value = propertyValue;
                            threeObject.vertexShader = value;
                        }
                        if ( propertyName === "fragmentShader" ) {
                            value = propertyValue;
                            threeObject.fragmentShader = value;
                        }
                        if ( propertyName === "updateFunction" ) {
                            value = propertyValue;
                            threeObject.updateFunction = value;
                            threeObject.update = function() {
                                eval( this.updateFunction );
                            }
                        }
                    }
                }
                if ( node.isUniformObject ) {
                    value = setUniformProperty( threeObject, propertyName, propertyValue.type, propertyValue.pValue );
                }
                if( threeObject instanceof THREE.Scene )
                {
                    if(propertyName == 'activeCamera')
                    {
                        if ( this.state.nodes[ propertyValue ] !== undefined ) {
                            // Update the model's activeCamera
                            this.state.scenes[ this.kernel.application() ].camera.ID = propertyValue;
                            value = propertyValue;                            
                        }
                    }
                    if( propertyName == 'ambientColor' )
                    {
                        var lightsFound = 0;
                        var vwfColor = new utility.color( propertyValue );
                        
                        if ( vwfColor ) {

                            for( var i = 0; i < threeObject.children.length; i++ )
                            {
                                if( threeObject.children[i] instanceof THREE.AmbientLight )
                                {
                                    threeObject.children[i].color.setRGB( vwfColor.red()/255, vwfColor.green()/255, vwfColor.blue()/255 );
                                    lightsFound++;
                                }
                            
                            }

                            if ( lightsFound == 0 ) {
                                node.ambientlight = new THREE.AmbientLight( '#000000' );
                                node.ambientlight.color.setRGB( vwfColor.red()/255, vwfColor.green()/255, vwfColor.blue()/255 );
                                node.threeScene.add( node.ambientlight );
                                this.state.lights[ node.nodeID ] = node.ambientlight;
                            }
                            value = vwfColor.toString();
                        }
                    }

                    // backgroundColor, enableShadows, shadowMapCullFace and shadowMapType are dependent 
                    // on the renderer object, but if they are set in a prototype,
                    // the renderer is not available yet, so store them until it is ready.
                    if ( propertyName == 'backgroundColor' )
                    {
                        if ( node && node.renderer ) {
                            var vwfColor = new utility.color( propertyValue );
                            if ( vwfColor ) {
                                node.renderer.setClearColor( vwfColor.getHex(), vwfColor.alpha() );
                                value = vwfColor.toString();
                            }
                        }
                        else if(node) {
                            node.rendererProperties["backgroundColor"] = propertyValue;
                        }
                    }
                    if(propertyName == 'enableShadows')
                    {
                        if ( node && node.renderer ) {
                            value = Boolean( propertyValue );
                            node.renderer.shadowMapEnabled = value;
                        }
                        else if(node) {
                            node.rendererProperties["enableShadows"] = propertyValue;
                        }

                        // Need to reset the viewport or you just get a blank screen
                        this.state.kernel.dispatchEvent( nodeID, "resetViewport" );
                    }
                    if ( propertyName == 'shadowMapCullFace') {
                        var shadowMapCullFace;
                        switch(propertyValue) {
                            case "none":
                                shadowMapCullFace = 0;
                                value = propertyValue;
                                break;
                            case "back":
                                shadowMapCullFace = 1;
                                value = propertyValue;
                                break;
                            case "front":
                                shadowMapCullFace = 2;
                                value = propertyValue;
                                break;
                            case "both":
                                shadowMapCullFace = 3;
                                value = propertyValue;
                                break;
                        }
                        if ( node && node.renderer ) {
                            node.renderer.shadowMapCullFace = shadowMapCullFace;
                        }
                        else if ( node ) {
                            node.rendererProperties["shadowMapCullFace"] = shadowMapCullFace;
                        }
                    }
                    if ( propertyName == 'shadowMapType') {
                        var shadowMapType;
                        switch(propertyValue) {
                            case "basic":
                                shadowMapType = 0;
                                value = propertyValue;
                                break;
                            case "PCF":
                                shadowMapType = 1;
                                value = propertyValue;
                                break;
                            case "PCFSoft":
                                shadowMapType = 2;
                                value = propertyValue;
                                break;
                        }                        
                        if ( node && node.renderer ) {
                            node.renderer.shadowMapType = shadowMapType;
                        }
                        else if ( node ) {
                            node.rendererProperties["shadowMapType"] = shadowMapType;
                        }
                    }
                    if ( propertyName === 'fogexp_color' ) {
                        if ( threeObject.fog && threeObject.fog instanceof THREE.FogExp2 ) {
                            var vwfColor = new utility.color( propertyValue );
                            if ( vwfColor ) {
                                threeObject.fog.color.setRGB( vwfColor.red()/255, vwfColor.green()/255, vwfColor.blue()/255 );
                            }                            
                        }
                    }
                    if ( propertyName === 'fogexp_density' ) {
                        if ( threeObject.fog && threeObject.fog instanceof THREE.FogExp2 ) {
                            threeObject.fog.density = parseFloat( propertyValue );    
                        }
                    }
                    if ( propertyName === 'fog_color' ) {
                        if ( threeObject.fog && threeObject.fog instanceof THREE.Fog ) {
                            var vwfColor = new utility.color( propertyValue );
                            if ( vwfColor ) {
                                threeObject.fog.color.setRGB( vwfColor.red()/255, vwfColor.green()/255, vwfColor.blue()/255 );
                            }      
                        }
                    }
                    if ( propertyName === 'fog_near' ) {
                        if ( threeObject.fog && threeObject.fog instanceof THREE.Fog ) {
                            threeObject.fog.fog_near = parseFloat( propertyValue );    
                        }
                    }
                    if ( propertyName === 'fog_far' ) {
                        if ( threeObject.fog && threeObject.fog instanceof THREE.Fog ) {
                            threeObject.fog.fog_far = parseFloat( propertyValue );    
                        }
                    }
                }   
                if(threeObject instanceof THREE.PointLight || threeObject instanceof THREE.DirectionalLight 
                    || threeObject instanceof THREE.SpotLight || threeObject instanceof THREE.HemisphereLight )
                {
                    if(propertyName == 'lightType')
                    {
                        var newlight;
                        var parent = threeObject.parent;
                        var currProps = {
                            "name": threeObject.name,
                            "distance": threeObject.distance,
                            "color":  threeObject.color,
                            "groundColor": threeObject.groundColor,
                            "intensity": threeObject.intensity,
                            "castShadow": threeObject.castShadow,
                            "shadowCameraLeft": threeObject.shadowCameraLeft,
                            "shadowCameraRight": threeObject.shadowCameraRight,
                            "shadowCameraTop": threeObject.shadowCameraTop,
                            "shadowCameraBottom": threeObject.shadowCameraBottom,
                            "shadowCameraNear": threeObject.shadowCameraNear,
                            "shadowCameraFar": threeObject.shadowCameraFar,
                            "shadowDarkness": threeObject.shadowDarkness,
                            "shadowMapHeight": threeObject.shadowMapHeight,
                            "shadowMapWidth": threeObject.shadowMapWidth,
                            "shadowBias": threeObject.shadowBias,
                            "target": threeObject.target,
                            "position": threeObject.position,
                            "clone": function( newObj ) {
                                newObj.name = this.name;
                                newObj.distance = this.distance;
                                //console.info( "light.clone.color = " + JSON.stringify( this.color ) )
                                newObj.color.setRGB( this.color.r, this.color.g, this.color.b );
                                if (this.groundColor !== undefined) {
                                    newObj.groundColor = new THREE.Color().setRGB( this.groundColor.r, this.groundColor.g, this.groundColor.b );
                                }
                                newObj.intensity = this.intensity;
                                newObj.castShadow = this.castShadow;
                                newObj.shadowCameraLeft = this.shadowCameraLeft;
                                newObj.shadowCameraRight = this.shadowCameraRight;
                                newObj.shadowCameraTop = this.shadowCameraTop;
                                newObj.shadowCameraBottom = this.shadowCameraBottom;
                                newObj.shadowCameraNear = this.shadowCameraNear;
                                newObj.shadowCameraFar = this.shadowCameraFar;
                                newObj.shadowDarkness = this.shadowDarkness;
                                newObj.shadowMapHeight = this.shadowMapHeight;
                                newObj.shadowMapWidth = this.shadowMapWidth;
                                newObj.shadowBias = this.shadowBias;
                                if ( this.target ) {
                                    newObj.target = this.target;
                                }
                                newObj.position.set( this.position.x, this.position.y, this.position.z );
                            }
                        };

                        if(propertyValue == 'point' && !(threeObject instanceof THREE.PointLight))
                        {
                            newlight = new THREE.PointLight('FFFFFF',1,0);
                            currProps.clone( newlight );                            
                            //newlight.matrixAutoUpdate = false;
                            parent.remove( node.threeObject );
                            parent.add( newlight );
                            node.threeObject = newlight;
                            rebuildAllMaterials.call(this);
                        }
                        if(propertyValue == 'directional' && !(threeObject instanceof THREE.DirectionalLight))
                        {
                            newlight = new THREE.DirectionalLight( 'FFFFFF' );
                            currProps.clone( newlight );                            
                            //newlight.matrixAutoUpdate = false;
                            parent.remove( node.threeObject );
                            parent.add( newlight );
                            node.threeObject = newlight;
                            rebuildAllMaterials.call(this);
                        }
                        if(propertyValue == 'spot' && !(threeObject instanceof THREE.SpotLight))
                        {
                            newlight = new THREE.SpotLight('FFFFFF',1,0);
                            currProps.clone( newlight );
                            //newlight.matrixAutoUpdate = false;
                            parent.remove( node.threeObject );
                            parent.add( newlight );
                            node.threeObject = newlight;
                            rebuildAllMaterials.call(this);
                        }
                        if(propertyValue == 'hemisphere' && !(threeObject instanceof THREE.HemisphereLight))
                        {
                            newlight = new THREE.HemisphereLight('FFFFFF','FFFFFF',1);
                            currProps.clone( newlight );                            
                            //newlight.matrixAutoUpdate = false;
                            parent.remove( node.threeObject );
                            parent.add( newlight );
                            node.threeObject = newlight;
                            rebuildAllMaterials.call(this);
                        }

                        if ( propertyValue == 'point' || propertyValue == 'directional' || propertyValue == 'spot' || propertyValue == 'hemisphere' ) {
                            value = propertyValue;                        
                        }
                    }
                    //if(propertyName == 'diffuse')
                    //{
                    //    threeObject.color.setRGB(propertyValue[0]/255,propertyValue[1]/255,propertyValue[2]/255);
                    //}
                    else if ( propertyName == 'enable' ) {
                        if ( parentNode !== undefined ) {
                            var threeParent = ( parentNode.threeObject !== undefined ) ? parentNode.threeObject : parentNode.threeScene;
                            if ( threeParent !== undefined ) {
                                if ( Boolean( propertyValue ) ) {
                                    if ( threeObject.parent === undefined ) {
                                        threeParent.add( threeObject );    
                                    }    
                                } else {
                                    if ( threeObject.parent !== undefined ) {
                                        threeParent.remove( threeObject );    
                                    } 
                                } 
                            }                           
                        }
                    } else if ( propertyName == 'position' ) {
                        if ( threeObject.position !== null && propertyValue.length ) {
                            threeObject.position.set( propertyValue[0], propertyValue[1], propertyValue[2] );  
                        }
                    }
                    else if ( propertyName == 'distance' ) {
                        value = Number( propertyValue );
                        threeObject.distance = value;
                    }
                    else if ( propertyName == 'color' ) {
                        var vwfColor = new utility.color( propertyValue );
                        if ( vwfColor ) {
                            threeObject.color.setRGB( vwfColor.red()/255, vwfColor.green()/255, vwfColor.blue()/255 );
                        }
                        value = vwfColor.toString();
                    }
                    else if ( propertyName == 'groundColor' ) {
                        var vwfColor = new utility.color( propertyValue );
                        if ( vwfColor ) {
                            threeObject.groundColor = new THREE.Color().setRGB( vwfColor.red()/255, vwfColor.green()/255, vwfColor.blue()/255 );
                        }
                        value = vwfColor.toString();
                    }
                    else if ( propertyName == 'intensity' ) {
                        value = parseFloat( propertyValue );
                        threeObject.intensity = value;
                    }                    
                    else if ( propertyName == 'castShadows' ) {
                        value = Boolean( propertyValue );
                        threeObject.castShadow = value;
                    }
                    else if ( propertyName == 'shadowCameraBottom' ) {
                        value = Number( propertyValue );
                        threeObject.shadowCameraBottom = value;
                    }
                    else if ( propertyName == 'shadowCameraLeft' ) {
                        value = Number( propertyValue );
                        threeObject.shadowCameraLeft = value;
                    }
                    else if ( propertyName == 'shadowCameraRight' ) {
                        value = Number( propertyValue );
                        threeObject.shadowCameraRight = value;
                    }
                    else if ( propertyName == 'shadowCameraTop' ) {
                        value = Number( propertyValue );
                        threeObject.shadowCameraTop = value;
                    }
                    else if ( propertyName == 'shadowCameraNear' ) {
                        value = Number( propertyValue );
                        threeObject.shadowCameraNear = value;
                    }
                    else if ( propertyName == 'shadowCameraFar' ) {
                        value = Number( propertyValue );
                        threeObject.shadowCameraFar = value;
                    }
                    else if ( propertyName == 'shadowDarkness' ) {
                        value = Number( propertyValue );
                        threeObject.shadowDarkness = value;
                    }
                    else if ( propertyName == 'shadowMapHeight' ) {
                        value = Number ( propertyValue );
                        threeObject.shadowMapHeight = value;
                        if(threeObject.shadowMapSize) {
                            threeObject.shadowMapSize.y = value;
                        }
                        if(threeObject.shadowMap) {
                            threeObject.shadowMap.height = value;
                        }
                    }
                    else if ( propertyName == 'shadowMapWidth' ) {
                        value = Number ( propertyValue );
                        threeObject.shadowMapWidth = value;
                        if(threeObject.shadowMapSize) {
                            threeObject.shadowMapSize.x = value;
                        }
                        if(threeObject.shadowMap) {
                            threeObject.shadowMap.width = value;
                        }
                    }
                    else if ( propertyName == 'shadowBias' ) {
                        value = Number ( propertyValue );
                        threeObject.shadowBias = value;
                    }
                    else if ( propertyName == "target" ) {
                        if ( propertyValue instanceof Array ) {
                            value = propertyValue;
                            if ( threeObject.target ) {
                                threeObject.target.position.set( value[ 0 ], value[ 1 ], value[ 2 ] );    
                            }
                        } else if ( this.state.nodes[ propertyValue ] ) {
                            value = propertyValue;
                            threeObject.target = this.state.nodes[ value ].threeObject;
                        } else {
                            this.logger.warnx( "settingProperty", "Invalid target: " + propertyValue );
                        }
                    }

                }
            }
            return value;
        },

        // -- gettingProperty ----------------------------------------------------------------------

        gettingProperty: function( nodeID, propertyName ) {

            if ( this.debug.properties || this.debug.getting ) {
                this.logger.infox( "   G === gettingProperty ", nodeID, propertyName );
            }

            var node = this.state.nodes[ nodeID ]; // { name: childName, glgeObject: undefined }
            if(!node) node = this.state.scenes[ nodeID ]; // { name: childName, glgeObject: undefined }
            var value = undefined;

            //this driver has no representation of this node, so there is nothing to do.
            if(!node) return;

            var threeObject = node.threeObject;
            if( !threeObject )
            threeObject = node.threeScene;

            //There is not three object for this node, so there is nothing this driver can do. return
            if(!threeObject) return value;    
          
            if(threeObject instanceof THREE.Object3D)
            {
                if(propertyName == 'transform' && node.transform)
                {
                    if ( node.storedTransformDirty ) {
                        updateStoredTransform( node );
                    }
                    value = matCpy( node.transform.elements );
                    return value;
                }
                if(propertyName =='lookAt')
                {
                    value = node.lookatval;
                    return value;
                }                 
                if(propertyName ==  "boundingbox")
                {
                    value = getBoundingBox.call( this, threeObject );
                    return value;
                }
                if ( propertyName == "centerOffset" )
                {
                    value = getCenterOffset.call( this, threeObject );
                    return value;
                }

                if(propertyName ==  "meshData")
                {
                    value = [];
                    var scale = this.gettingProperty( nodeID, "scale", [] ); 
                    scale = [1,1,1];
                    var meshList = findAllMeshes.call( this, threeObject );
                    for ( var i = 0; i < meshList.length; i++ ) {
                        value.push( {  "vertices": getMeshVertices.call( this, meshList[i],threeObject ),
                                       "vertexIndices": getMeshVertexIndices.call( this, meshList[i] ),
                                       "scale": scale 
                                    } );
                    }
                    return value;
                }

                if(propertyName == "animationDuration") {
                    var animationDuration = 0;
                    if(node.threeObject.animations) {
                        for(var i=0, il = node.threeObject.animations.length; i < il; i++) {
                            if(node.threeObject.animations[i].length > animationDuration) {
                                animationDuration = node.threeObject.animations[i].length;
                            }
                        }
                        value = animationDuration;
                    }
                    else if ( node.threeObject.animatedMesh && node.threeObject.animatedMesh.length ) {
                        var fps = this.state.kernel.getProperty( nodeID, "animationFPS") || 30;
                        for(var i=0, il = node.threeObject.animatedMesh.length; i < il; i++) {
                            if (node.threeObject.animatedMesh[i].bones) {
                                
                                // Skeletal animations take precedence over Morph Targets
                                animationDuration = node.threeObject.animatedMesh[i].bones.length;
                            }
                            else if(  node.threeObject.animatedMesh[i].morphTargetInfluences.length > animationDuration ) {
                                
                                animationDuration = node.threeObject.animatedMesh[i].morphTargetInfluences.length;
                            }
                        }
                        value = animationDuration / fps;
                    }

                    return value;
                }

                if(propertyName == "animationFPS") {
                    if(node.threeObject.animations) {
                        var animationDuration = 0;
                        var animationFrameCount = 0;
                        for(var i=0, il = node.threeObject.animations.length; i < il; i++) {
                            for(var i=0, il = node.threeObject.animations.length; i < il; i++) {
                                if(node.threeObject.animations[i].length > animationDuration) {
                                    animationDuration = node.threeObject.animations[i].length;
                                }
                                if(node.threeObject.animations[i].hierarchy[0].keys.length > animationFrameCount) {
                                    animationFrameCount = node.threeObject.animations[i].hierarchy[0].keys.length;
                                }
                            }
                        }
                        value = Math.floor(animationFrameCount / animationDuration);
                    }
                    return value;
                }

                if( propertyName == "visible" ) {
                    value = node.threeObject.visible;
                    return value;
                }
                
                if ( propertyName == "castShadows" ) {
                    value = node.threeObject.castShadow;
                    return value;
                }

                if ( propertyName == "receiveShadows" ) {
                    value = node.threeObject.receiveShadow;
                    return value;
                }
            }
            if(threeObject instanceof THREE.Material)
            {
                if(propertyName == "texture")
                {
                    if( threeObject.map && threeObject.map.image )
                        return threeObject.map.image.src;
                        
                }
                if(propertyName == "color") {
                    var vwfColor = new utility.color( [ threeObject.color.r*255, threeObject.color.g*255, threeObject.color.b*255 ] );
                    value = vwfColor.toString();
                    return value;    
                }
                if ( propertyName == "specColor" ) {
                    if ( threeObject.specular !== undefined ) {
                        var vwfColor = new utility.color( [ threeObject.specular.r*255, threeObject.specular.g*255, threeObject.specular.b*255 ] );
                        value = vwfColor.toString();
                        return value;
                    }
                }
                if ( propertyName == "reflect" ) {
                    value = threeObject.reflectivity;
                    return value;
                }
                if ( propertyName == "shininess" ) {
                    value = threeObject.shininess;
                    return value;
                }
                if ( propertyName == "emit" ) {
                    if ( threeObject.emissive ) {
                        var vwfColor = new utility.color( [ threeObject.emissive.r*255, threeObject.emissive.g*255, threeObject.emissive.b*255 ] );
                        value = vwfColor.toString();
                        return value;
                    }
                }
                if ( propertyName == "ambient" ) {
                    if ( threeObject.ambient ) {
                        var vwfColor = new utility.color( [ threeObject.ambient.r*255, threeObject.ambient.g*255, threeObject.ambient.b*255 ] );
                        value = vwfColor.toString();
                        return value;
                    }
                }
                if ( ( propertyName == "bumpScale" ) && ( threeObject.bumpMap ) ) {
                    value = threeObject.bumpScale;
                    return value;
                }
                if ( propertyName == "alphaTest" ) {
                    value = threeObject.alphaTest;
                    return value;
                }
                if ( propertyName == "transparent" ) {
                    value = threeObject.transparent;
                    return value;
                }
                if ( propertyName == "opacity" ) {
                    value = threeObject.opacity;
                    return value;
                }
                if ( propertyName === "side" ) {
                    switch ( threeObject.side ) {
                        case THREE.DoubleSide:
                            value = "double";
                            break;
                        case THREE.FrontSide:
                            value = "front";
                            break;
                        case THREE.BackSide:
                            value = "back";
                            break;
                    }
                }

            }
            if ( threeObject instanceof THREE.ShaderMaterial ) {
                if ( propertyName === "uniforms" ) {
                    value = {};
                    for ( var uni in threeObject.uniforms ) {
                        if ( threeObject.uniforms[ uni ].type === 't' ) {
                            if ( threeObject.uniforms[ uni ].value ) {
                                value[ uni ] = { "type": 't', "value": threeObject.uniforms[ uni ].value.sourceFile };
                            } else {
                                value[ uni ] = threeObject.uniforms[ uni ];    
                            }
                        } else {
                            value[ uni ] = threeObject.uniforms[ uni ];     
                        }
                    }
                    return value;
                }
                if ( propertyName === "vertexShader" ) {
                    value = threeObject.vertexShader;
                    return value;
                }
                if ( propertyName === "fragmentShader" ) {
                    value = threeObject.fragmentShader;
                    return value;
                }
                if ( propertyName === "updateFunction" ) {
                    value = threeObject.updateFunction;
                    return value;
                }
            }
            if ( node.isUniformObject ) {
                value = {};
                for ( var prop in threeObject ) {
                    if ( ! utility.isFunction( threeObject[ prop ] ) ) {
                        if ( threeObject[ prop ].type !== 't' ) {
                            value[ prop ] = {
                                "type": threeObject[ prop ].type,
                                "pValue": threeObject[ prop ].value
                            };
                        } else {
                            value[ prop ] = {
                                "type": threeObject[ prop ].type,
                                "pValue": threeObject[ prop ].src
                            }; 
                        }
                    }
                }
                return value;
            }
            if ( threeObject instanceof THREE.Texture ) {
                value = undefined;
                switch ( propertyName ) {
                    
                    case "url":
                        if( threeObject.image ) {
                            value = threeObject.image.src;    
                        } else {
                            value = "";
                        }
                        break;

                    case "wrapT":
                        switch ( threeObject.wrapT ) {
                            case THREE.ClampToEdgeWrapping:
                                value = "clamp";
                                break;
                            case THREE.RepeatWrapping:
                                value = "repeat";
                                break;
                            case THREE.MirroredRepeatWrapping:
                                value = "mirror";
                                break;
                        }
                        break;

                    case "wrapS":
                        switch ( threeObject.wrapS ) {
                            case THREE.ClampToEdgeWrapping:
                                value = "clamp";
                                break;
                            case THREE.RepeatWrapping:
                                value = "repeat";
                                break;
                            case THREE.MirroredRepeatWrapping:
                                value = "mirror";
                                break;
                        }
                        break;

                    case "repeat":
                        if ( threeObject.repeat ) {
                            value = [ threeObject.repeat.x, threeObject.repeat.y ]
                        }
                        break;

                    case "offset":
                        if ( threeObject.offset ) {
                            value = [ threeObject.repeat.x, threeObject.offset.y ]
                        }
                        break;

                    case "magFilter":
                        switch ( threeObject.magFilter ) {

                            case THREE.NearestFilter:
                                value = "nearest";
                                break;

                            case THREE.NearestMipMapNearestFilter:
                                value = "nearestNearest";
                                break;

                            case THREE.NearestMipMapLinearFilter:
                                value = "nearestLinear";
                                break;

                            case THREE.LinearFilter:
                                value = "linear";
                                break;

                            case THREE.LinearMipMapNearestFilter:
                                value = "linearNearest";
                                break;

                            case THREE.LinearMipMapLinearFilter:
                                value = "linearLinear";
                                break;                                

                        }
                        break;

                    case "minFilter":
                        switch ( threeObject.minFilter ) {

                            case THREE.NearestFilter:
                                value = "nearest";
                                break;

                            case THREE.NearestMipMapNearestFilter:
                                value = "nearestNearest";
                                break;

                            case THREE.NearestMipMapLinearFilter:
                                value = "nearestLinear";
                                break;

                            case THREE.LinearFilter:
                                value = "linear";
                                break;

                            case THREE.LinearMipMapNearestFilter:
                                value = "linearNearest";
                                break;

                            case THREE.LinearMipMapLinearFilter:
                                value = "linearLinear";
                                break;                                

                        }
                        break;

                    case "anisotropy":
                        value = threeObject.anisotropy;
                        break; 

                }
                if ( value !== undefined ) {
                    return value;
                }
            }
            if( threeObject instanceof THREE.Camera ) {
                switch ( propertyName ) {
                    case "fovy":
                        value = threeObject.fovy; 
                        break;
                    case "near":
                        value = threeObject.near;
                        break; 
                    case "aspect":
                        value = threeObject.aspect;
                        break; 
                    case "far":
                        value = threeObject.far;
                        break;                    
                    case "cameraType":
                        if ( threeObject instanceof THREE.OrthographicCamera ) {
                            value = 'orthographic';
                        } else {
                            value = 'perspective';
                        }
                        break;
                }
            }
            if( threeObject instanceof THREE.Scene ) {
                var found = false;
                var vwfColor, color;
                switch ( propertyName ) {
                    case "ambientColor":
                        for( var i = 0; i < threeObject.children.length && !found; i++ ) {
                            if( threeObject.children[i] instanceof THREE.AmbientLight ) {
                                color = threeObject.children[i].color;
                                vwfColor = new utility.color( [ color.r*255, color.g*255, color.b*255 ] );
                                value = vwfColor.toString();
                                found = true;
                            }
                        }
                        break;
                    case "backgroundColor":
                        if ( node.renderer ) {
                            color = node.renderer.getClearColor();
                            var alpha = node.renderer.getClearAlpha();
                            if ( alpha !== undefined && alpha != 1 ){
                                vwfColor = new utility.color( [ color.r*255, color.g*255, color.b*255, alpha ] );
                            } else {
                                vwfColor = new utility.color( [ color.r*255, color.g*255, color.b*255 ] );
                            }
                            value = vwfColor.toString();
                        }
                        break;
                    case 'enableShadows':
                        if ( node.renderer ) {
                            value = node.renderer.shadowMapEnabled;
                        }
                        break;
                    case "activeCamera":
                        value = node.camera.ID;
                        break;
                    case "shadowMapCullFace":
                        if ( node.renderer ) {
                            value = node.renderer.shadowMapCullFace;
                        }
                        break;
                    case "shadowMapType":
                        if ( node.renderer ) {
                            value = node.renderer.shadowMapType;
                        }
                        break;
                    case "fogexp_color":
                        if ( threeObject.fog && threeObject.fog instanceof THREE.FogExp2 ) {
                            color = threeObject.fog.color;    
                            vwfColor = new utility.color( [ color.r*255, color.g*255, color.b*255 ] );
                            value = vwfColor.toString();
                        }
                        break;
                    case "fogexp_density":
                        if ( threeObject.fog && threeObject.fog instanceof THREE.FogExp2 ) {
                            value = threeObject.fog.density;    
                        }
                        break;
                    case "fog_color":
                        if ( threeObject.fog && threeObject.fog instanceof THREE.Fog ) {
                            color = threeObject.fog.color;    
                            vwfColor = new utility.color( [ color.r*255, color.g*255, color.b*255 ] );
                            value = vwfColor.toString();  
                        }
                        break;
                    case "fog_near":
                        if ( threeObject.fog && threeObject.fog instanceof THREE.Fog ) {
                            value = threeObject.fog.near;    
                        }
                        break;
                    case "fog_far":
                        if ( threeObject.fog && threeObject.fog instanceof THREE.Fog ) {
                            value = threeObject.fog.far;    
                        }
                        break;
                }
            }
            if( threeObject instanceof THREE.Light ) {
                switch ( propertyName ) {
                    case "lightType":
                        if ( threeObject instanceof THREE.DirectionalLight ){
                            value = 'directional';
                        } else if ( threeObject instanceof THREE.SpotLight ) {
                            value = 'spot';
                        } else if ( threeObject instanceof THREE.HemisphereLight ) {
                            value = 'hemisphere';
                        } else {
                            value = 'point';
                        }
                        break;
                    case "enable":
                        value = ( threeObject.parent !== undefined );
                        break;
                    case "position":
                        if ( threeObject.position !== null ) {
                            value = [ threeObject.position.x, threeObject.position.y, threeObject.position.z ];  
                        }
                        break;

                    case "distance":
                        value = threeObject.distance;
                        break;
                    case "color":
                        var clr = new utility.color( [ threeObject.color.r * 255, threeObject.color.g * 255, 
                                                       threeObject.color.b * 255 ] ) 
                        value = clr.toString();
                        break;
                    case "groundColor":
                        var clr = new utility.color( [ threeObject.groundColor.r * 255, threeObject.groundColor.g * 255, 
                                                       threeObject.groundColor.b * 255 ] ) 
                        value = clr.toString();
                        break;
                    case "intensity":
                        value = threeObject.intensity;
                        break;
                    case "castShadows":
                        value = threeObject.castShadow;
                        break;
                    case "shadowCameraBottom":
                        value = threeObject.shadowCameraBottom;
                        break;
                    case "shadowCameraLeft": 
                        value = threeObject.shadowCameraLeft;
                        break;
                    case "shadowCameraRight": 
                        value = threeObject.shadowCameraRight;
                        break;
                    case "shadowCameraTop": 
                        value = threeObject.shadowCameraTop;
                        break;
                    case "shadowCameraNear":
                        value = threeObject.shadowCameraNear;
                        break;
                    case "shadowCameraFar":
                        value = threeObject.shadowCameraFar;
                        break;
                    case "shadowDarkness":
                        value = threeObject.shadowDarkness;
                        break;
                    case "shadowMapHeight":
                        value = threeObject.shadowMapHeight;
                        break;
                    case "shadowMapWidth":
                        value = threeObject.shadowMapWidth;
                        break;
                    case "shadowBias":
                        value = threeObject.shadowBias;
                        break;
                    case "target":
                        // TODO: Return target node information if target is a node.
                        //   The threeObjects of some nodes do not have a vwfID. This
                        //   will incorrectly return a position in those cases. This
                        //   needs to be fixed.
                        if ( threeObject.target !== undefined ) {

                            if ( threeObject.target.vwfID !== undefined ) {
                                value = threeObject.target.vwfID;
                            } else {
                                var targetPos = [ threeObject.target.position.x,
                                                  threeObject.target.position.y,
                                                  threeObject.target.position.z ];
                                value = targetPos;
                            }                            
                        } 

                        break;
                }
            }
            return value;
        },


        // TODO: deletingMethod

        // -- callingMethod --------------------------------------------------------------------------

        callingMethod: function( nodeID, methodName, parameters /* [, parameter1, parameter2, ... ] */ ) { // TODO: parameters

            if ( methodName === "raycast" ) {

                var origin, direction, near, far, recursive, objectIDs;

                if ( parameters ) {

                    if ( parameters[0] instanceof THREE.Vector3 ) {

                        origin = parameters[0];

                    } else if ( parameters[0] instanceof Array && parameters[0].length === 3 ) {

                        var x, y, z;
                        x = isNaN( parameters[0][0] ) ? 0 : parameters[0][0];
                        y = isNaN( parameters[0][1] ) ? 0 : parameters[0][1];
                        z = isNaN( parameters[0][2] ) ? 0 : parameters[0][2];
                        origin = new THREE.Vector3( x, y, z );

                    } else {

                        origin = new THREE.Vector3();

                    }

                    if ( parameters[1] instanceof THREE.Vector3 ) {

                        direction = parameters[1];

                    } else if ( parameters[1] instanceof Array && parameters[1].length === 3 ) {

                        var x, y, z;
                        x = isNaN( parameters[1][0] ) ? 0 : parameters[1][0];
                        y = isNaN( parameters[1][1] ) ? 0 : parameters[1][1];
                        z = isNaN( parameters[1][2] ) ? 0 : parameters[1][2];
                        direction = new THREE.Vector3( x, y, z );

                    } else {

                        direction = new THREE.Vector3();
                        
                    }

                    near = isNaN( parameters[2] ) ? 0 : parameters[2];
                    far = isNaN( parameters[3] ) ? Infinity : parameters[3];
                    recursive = typeof parameters[4] === "boolean" ? parameters[4] : false;

                    if ( parameters[5] instanceof Array ) {

                        objectIDs = parameters[5];

                    } else if ( utility.isString( parameters[5] ) ) {

                        objectIDs = new Array();
                        objectIDs.push( parameters[5] );

                    } else {

                        objectIDs = null;
                    }

                } else {

                    origin = new THREE.Vector3();
                    direction = new THREE.Vector3();
                    near = 0;
                    far = Infinity;
                    recursive = false;
                    objectIDs = null;

                }

                var objects = new Array();

                if ( objectIDs !== null ) {

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

                        var object = this.state.nodes[ objectIDs[i] ];

                        if ( object !== undefined && object.threeObject !== undefined ) {

                            objects.push( object.threeObject );

                        }

                    }

                } else {

                    for ( nodeID in this.state.nodes ) {

                        var object = this.state.nodes[ nodeID ];

                        if ( object.threeObject !== undefined ) {

                            objects.push( object.threeObject );

                        }

                    }

                }

                var raycaster = new THREE.Raycaster( origin, direction, near, far );
                var intersects = raycaster.intersectObjects( objects, recursive );
                return intersects;

            }

            return undefined;
        },


        // TODO: creatingEvent, deltetingEvent, firingEvent

        // -- executing ------------------------------------------------------------------------------

        executing: function( nodeID, scriptText, scriptType ) {
            return undefined;
        },

        // == ticking =============================================================================

        ticking: function( vwfTime ) {
            
            if ( checkLights && this.state.appInitialized && sceneCreated ) {
                
                var lightsInScene = sceneLights.call( this );

                createDefaultLighting.call( this, lightsInScene );
                checkLights = false;    
            }
        }

    } );

    // == PRIVATE  ========================================================================================

    function checkCompatibility() {
        this.compatibilityStatus = { compatible:true, errors:{} }
        var contextNames = ["webgl","experimental-webgl","moz-webgl","webkit-3d"];
        for(var i = 0; i < contextNames.length; i++){
            try{
                var canvas = document.createElement('canvas');
                var gl = canvas.getContext(contextNames[i]);
                if(gl){
                    return true;
                }
            }
            catch(e){}
        }
        this.compatibilityStatus.compatible = false;
        this.compatibilityStatus.errors["WGL"] = "This browser is not compatible. The vwf/view/threejs driver requires WebGL.";
        return false;
    }

    function getPrototypes( kernel, extendsID ) {
        var prototypes = [];
        var id = extendsID;

        while ( id !== undefined ) {
            prototypes.push( id );
            id = kernel.prototype( id );
        }
                
        return prototypes;
    }

    function getThreeScene( id ) {
        if ( id === undefined ) {
            id = this.kernel.application();
        }
        if ( this.state.scenes[ id ] ) {
            return this.state.scenes[ id ].threeScene;
        }
        return undefined;
    }

    function isSceneDefinition( prototypes ) {
        var foundScene = false;
        if ( prototypes ) {
            for ( var i = 0; i < prototypes.length && !foundScene; i++ ) {
                foundScene = ( prototypes[i] == "http://vwf.example.com/navscene.vwf" || prototypes[i] == "http://vwf.example.com/scene.vwf" );
            }
        }

        return foundScene;
    }
    function isMaterialDefinition( prototypes ) {
        var foundMaterial = false;
        if ( prototypes ) {
            for ( var i = 0; i < prototypes.length && !foundMaterial; i++ ) {
                foundMaterial = ( prototypes[i] == "http://vwf.example.com/material.vwf" );
            }
        }

        return foundMaterial;
    }
    function isShaderMaterialDefinition( prototypes ) {
        var foundMaterial = false;
        if ( prototypes ) {
            for ( var i = 0; i < prototypes.length && !foundMaterial; i++ ) {
                foundMaterial = ( prototypes[i] == "http://vwf.example.com/shaderMaterial.vwf" );
            }
        }

        return foundMaterial;
    }
    function isShaderUniformsDefinition( prototypes ) {
        var found = false;
        if ( prototypes ) {
            for ( var i = 0; i < prototypes.length && !found; i++ ) {
                found = ( prototypes[i] == "http://vwf.example.com/threejs/uniforms.vwf" );
            }
        }

        return found;
    }
    function isTextureDefinition( prototypes ) {
        var found = false;
        if ( prototypes ) {
            for ( var i = 0; i < prototypes.length && !found; i++ ) {
                found = ( prototypes[i] == "http://vwf.example.com/texture.vwf" );
            }
        }

        return found;
    }
    function isCameraDefinition( prototypes ) {
        var foundCamera = false;
        if ( prototypes ) {
            for ( var i = 0; i < prototypes.length && !foundCamera; i++ ) {
                foundCamera = ( prototypes[i] == "http://vwf.example.com/camera.vwf" );
            }
        }

        return foundCamera;
    }
    function isParticleDefinition( prototypes ) {
        var foundSystem = false;
        if ( prototypes ) {
            for ( var i = 0; i < prototypes.length && !foundSystem; i++ ) {
                foundSystem = ( prototypes[i] == "http://vwf.example.com/particlesystem.vwf" );
            }
        }

        return foundSystem;
    }
    
    
    function isNodeDefinition( prototypes ) {
        var foundNode = false;
        if ( prototypes ) {
            for ( var i = 0; i < prototypes.length && !foundNode; i++ ) {
                foundNode = ( prototypes[i] == "http://vwf.example.com/node3.vwf" );
            }
        }

        return foundNode;
    }

    function isStarFieldDefinition( prototypes ) {
        var foundNode = false;
        if ( prototypes ) {
            for ( var i = 0; i < prototypes.length && !foundNode; i++ ) {
                foundNode = ( prototypes[i] == "http://vwf.example.com/threejs/starfield.vwf" );
            }
        }
        return foundNode;
    }
    function isCubeDefinition( prototypes ) {
        var foundNode = false;
        if ( prototypes ) {
            for ( var i = 0; i < prototypes.length && !foundNode; i++ ) {
                foundNode = ( prototypes[i] == "http://vwf.example.com/threejs/cube.vwf" );
            }
        }
        return foundNode;
    }
    function isCircleDefinition( prototypes ) {
        var foundNode = false;
        if ( prototypes ) {
            for ( var i = 0; i < prototypes.length && !foundNode; i++ ) {
                foundNode = ( prototypes[i] == "http://vwf.example.com/threejs/circle.vwf" );
            }
        }
        return foundNode;
    }
    function isPlaneDefinition( prototypes ) {
        var foundNode = false;
        if ( prototypes ) {
            for ( var i = 0; i < prototypes.length && !foundNode; i++ ) {
                foundNode = ( prototypes[i] == "http://vwf.example.com/threejs/plane.vwf" );
            }
        }
        return foundNode;
    }
    function isSphereDefinition( prototypes ) {
        var foundNode = false;
        if ( prototypes ) {
            for ( var i = 0; i < prototypes.length && !foundNode; i++ ) {
                foundNode = ( prototypes[i] == "http://vwf.example.com/threejs/sphere.vwf" );
            }
        }
        return foundNode;
    }
    function isCylinderDefinition( prototypes ) {
        var foundNode = false;
        if ( prototypes ) {
            for ( var i = 0; i < prototypes.length && !foundNode; i++ ) {
                foundNode = ( prototypes[i] == "http://vwf.example.com/threejs/cylinder.vwf" );
            }
        }
        return foundNode;
    }
    function isTextDefinition( prototypes ) {
        var foundNode = false;
        if ( prototypes ) {
            for ( var i = 0; i < prototypes.length && !foundNode; i++ ) {
                foundNode = ( prototypes[i] == "http://vwf.example.com/threejs/text.vwf" );
            }
        }
        return foundNode;
    }
    function supportedFileType( type ) {
        return ( type == "model/vnd.collada+xml" || 
                 type == "model/vnd.osgjs+json+compressed" ||
                 type == "model/x-threejs-morphanim+json" ||
                 type == "model/vnd.gltf+json" ||
                 type == "model/x-threejs-skinned+json" );
    }
    function CreateThreeJSSceneNode( parentID, thisID, extendsID )
    {
        var node = {};
        node.name = "scene";
        node.camera = { 
            "ID": undefined, 
            "defaultCamera": CreateThreeCamera() 
        };
        node.ID = thisID;
        node.parentID = parentID;
        node.type = extendsID;
        node.viewInited = false;
        node.modelInited = false;
        node.threeScene = new THREE.Scene();
        node.threeScene.name = "scene";
        node.pendingLoads = 0;
        node.srcAssetObjects = [];
        node.rendererProperties = {};
        
        return node;
    }
    
    function nameTest( obj, name ) {
        if ( obj.name == "" ) {
            return ( obj.parent.name+"Child" == name );
        } else {
            return ( obj.name == name || obj.id == name || obj.vwfID == name );
        }
    }

    // Changing this function significantly from the GLGE code
    // Will search hierarchy down until encountering a matching child
    // Will look into nodes that don't match.... this might not be desirable
    function FindChildByName( obj, childName, childType, recursive ) {
        
        var child = undefined;
        if ( recursive ) {

            // TODO: If the obj itself has the child name, the object will be returned by this function
            //       I don't think this this desirable.

            if( nameTest.call( this, obj, childName ) ) {
                child = obj;
            } else if ( obj.children && obj.children.length > 0) {
                for( var i = 0; i < obj.children.length && child === undefined; i++ ) {
                    child = FindChildByName( obj.children[i], childName, childType, true );
                }
            }
        } else {
            if ( obj.children ) {
                for( var i = 0; i < obj.children.length && child === undefined; i++ ) {
                    if ( nameTest.call( this, obj.children[i], childName ) ) {
                        child = obj.children[i];
                    }
                }
            }
        }
        return child;

    }
    function findObject( objName, type ) {

        //there is no global registry of threejs objects. return undefined;

        return undefined;

    }   
    function CreateThreeCamera()
    {
        var cam = new THREE.PerspectiveCamera( 35, $(document).width()/$(document).height() , 0.01, 10000 );
        cam.matrixAutoUpdate = false;
        cam.up = new THREE.Vector3( 0, 0, 1 );
        cam.matrix.elements = [ 1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1 ];
        cam.updateMatrixWorld( true );    
        return cam;
    }
    function createAmbientLight( threeScene, clr ){
        var ambient = new THREE.AmbientLight();
        if ( clr !== undefined && clr instanceof Array ) {
            ambient.color.r = clr[0];
            ambient.color.g = clr[1];
            ambient.color.b = clr[2];
        } else {
            ambient.color.r = 0.5;
            ambient.color.g = 0.5;
            ambient.color.b = 0.5;
        }
        threeScene.add( ambient );
    }

    function createCamera( nodeID, childID, childName ) {

        var sceneNode = this.state.scenes[ nodeID ];
        var parent = sceneNode ? sceneNode : this.state.nodes[ nodeID ];
        if ( !sceneNode ) sceneNode = this.state.scenes[ parent.sceneID ];
        if ( sceneNode && parent ) {
            var child = this.state.nodes[ childID ];
            if ( child ) {
                var cam = CreateThreeCamera.call( this );;
                var threeParent = parent.threeObject;
                if( !threeParent ) threeParent = parent.threeScene;
                if ( threeParent && threeParent.add ) {
                    threeParent.add( cam );
                }

                child.name = childName;
                child.threeObject = cam;
                child.uid = child.threeObject.uid;
                cam.name = childName;
            }
        }

    }

    function getThreeObject( ID ) {
        var threeObject = undefined;
        var node = this.state.nodes[ ID ]; // { name: childName, glgeObject: undefined }
        if( node === undefined ) node = this.state.scenes[ ID ]; // { name: childName, glgeObject: undefined }

        if( node ) {
            threeObject = node.threeObject;
            if( !threeObject )
                threeObject = node.threeScene;
        }

        return threeObject;
    }

    function findAllGeometries( threeObject, list ) {
        
        if( threeObject === undefined ) return;
        if( list === undefined ) list = [];
        if( threeObject instanceof THREE.Geometry )
            list.push( threeObject );
        if( threeObject.children ) {
            for( var i = 0; i < threeObject.children.length; i++ ) {
                findAllGeometries( threeObject.children[i], list );
            }
        }
        return list;    
    }    
    
    function findAllMeshes(threeObject,list)
    {
        
        if(!threeObject) return;
        if(!list) list = [];
        if(threeObject instanceof THREE.Mesh)
            list.push(threeObject);
        if(threeObject.children)
        {
            for(var i = 0; i < threeObject.children.length; i++)
            {
                findAllMeshes(threeObject.children[i],list);
            }
        }
        return list;    
    }

    function createLightContainer() {
        return { 
                    "ambientLights": [], 
                    "directionalLights": [],
                    "spotLights": [],
                    "pointLights": []
                }; 
    }

    function findAllLights( threeObject, lights ) {
        
        if( !threeObject ) 
            return;

        if ( threeObject instanceof THREE.DirectionalLight )
            lights.directionalLights.push( threeObject );
        else if ( threeObject instanceof THREE.SpotLight )
            lights.spotLights.push( threeObject );
        else if ( threeObject instanceof THREE.PointLight ) 
            lights.pointLights.push( threeObject );
        else if ( threeObject instanceof THREE.AmbientLight ) 
            lights.ambientLights.push( threeObject );

        if( threeObject.children ) {
            for ( var i = 0; i < threeObject.children.length; i++) {
                findAllLights( threeObject.children[ i ], lights );
            }
        }
        return lights;    
    }    
    
    function getMeshVertexIndices(mesh)
    {
        
        var ret = [];
        for(var i = 0; i < mesh.geometry.faces.length; i++)
        {
            var face = mesh.geometry.faces[i];
            ret.push([face.a,face.b,face.c]);

        }
        return ret;
    }
    //Get all mesh verts. Transform via matrix stack up to threeObject. Thus, get all sub mesh verts relative to this object's transform
    function getMeshVertices(mesh,threeObject )
    {
        
        var matrix = new THREE.Matrix4();
        matrix.copy(mesh.matrix);
        var parent = mesh.parent;
        while(parent && parent != threeObject)
        {
            var mat = new THREE.Matrix4();
            mat.copy(parent.matrix);
            matrix = matrix.multiplyMatrices(mat,matrix);
            parent = parent.parent;
        }
        var mat = new THREE.Matrix4();
            mat.copy(threeObject.matrix);
        matrix = matrix.multiplyMatrices(mat,matrix);
        var ret = [];
        for(var i = 0; i < mesh.geometry.vertices.length; i++)
        {
            var vert = new THREE.Vector3();
            vert.copy(mesh.geometry.vertices[i]);
            vert.applyMatrix4( matrix );
            ret.push([vert.x,-vert.y,vert.z]);

        }
        return ret;
    }
    
    function GetAllMaterials( threeObject ) {
        var result = [];
        var resultUUID = [];
        if ( !threeObject ) {
          return result;
        }
        if ( threeObject && threeObject.material ) {
            if ( ( threeObject.material instanceof THREE.Material ) && ( resultUUID.indexOf( threeObject.material.uuid ) < 0 ) ) {
                result.push( threeObject.material );
                resultUUID.push( threeObject.material.uuid );
            }
            else if ( threeObject.material instanceof THREE.MeshFaceMaterial ) {
                if ( threeObject.material.materials ) {
                    for ( var index = 0; index < threeObject.material.materials.length; index++ ) {
                        if ( ( threeObject.material.materials[ index ] instanceof THREE.Material ) && ( resultUUID.indexOf( threeObject.material.materials[ index ].uuid ) < 0 ) ) {
                            result.push( threeObject.material.materials[ index ] );
                            resultUUID.push( threeObject.material.materials[ index ].uuid );
                        }
                    }
                }
            }
        }
        
        if ( threeObject && threeObject.children ) {
            for ( var index = 0; index < threeObject.children.length; index++ ) {
                var childrenMaterials = GetAllMaterials( threeObject.children[ index ] );
                for ( var subindex = 0; subindex < childrenMaterials.length; subindex++ ) {
                    if ( resultUUID.indexOf( childrenMaterials[ subindex ].uuid ) < 0 ) {
                        result.push( childrenMaterials[ subindex ] );
                        resultUUID.push( childrenMaterials[ subindex ].uuid );
                    }
                }
            }
        }
        return result;
    }
    //do a depth first search of the children, return the first material
    function GetMaterial(threeObject, optionalName)
    {        
        //something must be pretty seriously wrong if no threeobject
        if(!threeObject)
        {
            return null;
        }
        var allMaterialChildren = GetAllMaterials( threeObject );
                
        if ( optionalName ) {
            var regExResult = optionalName.match(/^material_\d+_/);
            if ( regExResult ) {
                var updatedName = optionalName.slice( regExResult[ 0 ].length );
                var firstMatch = undefined;
                var nameIndex = parseInt( optionalName.slice( 9, regExResult[ 0 ].length - 1 ) );
                var foundIndex = 0;
                for ( var index = 0; index < allMaterialChildren.length; index++ ) {
                    if ( allMaterialChildren[ index ].name == updatedName ) {
                        if ( ! firstMatch ) {
                            firstMatch = allMaterialChildren[ index ];
                        }
                        foundIndex++;
                        if ( foundIndex == nameIndex ) {
                            return allMaterialChildren[ index ];
                        }
                    }
                }
                if ( firstMatch ) {
                    return firstMatch;
                }
            }
            for ( var index = 0; index < allMaterialChildren.length; index++ ) {
                if ( allMaterialChildren[ index ].name == optionalName ) {
                    return allMaterialChildren[ index ];
                }
            }
        }
        if ( allMaterialChildren.length > 0 ) {
            return allMaterialChildren[ 0 ];
        }
        return null;
    }
    
    function GetAllLeafMeshes(threeObject,list)
    {
        if(threeObject instanceof THREE.Mesh)
        {
            list.push(threeObject);
        }
        if(threeObject.children)
        {
            for(var i=0; i < threeObject.children.length; i++)
            {
                GetAllLeafMeshes(threeObject.children[i],list);
            }               
        }     
    }
    function fixMissingUVs(mesh)
    {
       
        if ( mesh.geometry instanceof THREE.BufferGeometry ) {
            return;
        }
        var geometry = mesh.geometry;
        if ( !geometry.faceVertexUvs[ 0 ] ) {
            geometry.faceVertexUvs[ 0 ] = [];
        }
        if ( geometry.faceVertexUvs[ 0 ].length === 0 ) {
            for ( var i = 0; i < geometry.faces.length; i++ ) {
                var face = geometry.faces[ i ];
                if ( face instanceof THREE.Face4 ) {
                    geometry.faceVertexUvs[0].push( [ new THREE.Vector2( 0, 1 ),
                                                      new THREE.Vector2( 0, 1 ),
                                                      new THREE.Vector2( 0, 1 ), 
                                                      new THREE.Vector2( 0, 1 ) 
                                                    ] );
                } else if ( face instanceof THREE.Face3 ) {
                    geometry.faceVertexUvs[0].push( [ new THREE.Vector2( 0, 1 ), 
                                                      new THREE.Vector2( 0, 1 ),
                                                      new THREE.Vector2( 0, 1 ) 
                                                    ] );
                }
            }
        }
         
        geometry.computeCentroids && geometry.computeCentroids();
        geometry.computeFaceNormals && geometry.computeFaceNormals();
        geometry.computeVertexNormals && geometry.computeVertexNormals();
        geometry.uvsNeedUpdate = true;

    }
    //set the material on all the sub meshes of an object.
    //This could cause some strangeness in cases where an asset has multiple sub materials
    //best to only specify the material sub-node where an asset is a mesh leaf
    function SetMaterial( threeObject, material, materialname )
    {
        
        //something must be pretty seriously wrong if no threeobject
        if(!threeObject)
            return null;
        
        
        var meshes =[];
        GetAllLeafMeshes(threeObject,meshes);
        //apply to all sub meshes
        if(!materialname || materialname == 'material')
        {
            for(var i=0; i < meshes.length; i++)
            {
                
                meshes[i].material = material;
                meshes[i].needsUpdate = true;
            }
        }else
        {
            var index = parseInt(materialname.substr(8));
            if(meshes[index])
            {
                
                meshes[index].material = material;
                meshes[index].needsUpdate = true;
                window._dMesh =meshes[index];
            }
        }
    }
    function createShader( shaderDef ) {

        var shaderMaterial = undefined;
        if ( shaderDef && shaderDef.shaderType ) {

            if ( THREE.ShaderLib[ shaderDef.shaderType ] !== undefined ) {

                var shader = THREE.ShaderLib[ shaderDef.shaderType ];
                var uniforms = THREE.UniformsUtils.clone( shader.uniforms );
                var mergedShader = { 
                    fragmentShader: shader.fragmentShader, 
                    vertexShader: shader.vertexShader, 
                    uniforms: uniforms
                }; 

                for ( var prop in shaderDef ) {
                    
                    switch ( prop ) {
                        
                        case "shaderType":
                            break;

                        case "uniforms":
                            for ( var uProp in shaderDef.uniforms ) {
                                var uniProp = shaderDef.uniforms[ uProp ];
                                setUniformProperty( mergedShader.uniforms, uProp, uniProp.type, uniProp.pValue );
                            }
                            break;

                        default:
                            mergedShader[ prop ] = shaderDef[ prop ];
                            break;                           
                    }
                }



                shaderMaterial = new THREE.ShaderMaterial( mergedShader );

            } else {
                shaderMaterial = new THREE.ShaderMaterial( shaderDef );
            }

        }

        return shaderMaterial;
    }
    function createGeometry( node, meshDef, doubleSided ) {
        var geo = undefined;
        var i, face, sides;
        if ( ( node && isCubeDefinition.call( this, node.prototypes ) ) || meshDef.type === "box" ) {
            sides = meshDef.sides || { px: true, nx: true, py: true, ny: true, pz: true, nz: true };
            geo = new THREE.BoxGeometry( meshDef.width || 10, meshDef.height || 10, meshDef.depth || 10, 
                meshDef.segmentsWidth || 1, meshDef.segmentsHeight || 1, meshDef.segmentsDepth || 1 );
        } else if ( ( node && isPlaneDefinition.call( this, node.prototypes ) ) || meshDef.type === "plane" ) {
            geo = new THREE.PlaneGeometry( meshDef.width || 1, meshDef.height || 1,
                meshDef.segmentsWidth || 1, meshDef.segmentsHeight || 1 );
        } else if ( ( node && isCircleDefinition.call( this, node.prototypes ) ) || meshDef.type === "circle" ) {
            geo = new THREE.CircleGeometry( meshDef.radius || 10, 
                meshDef.segments || 8, meshDef.thetaStart || 0, 
                meshDef.thetaLength || Math.PI * 2 );
        } else if ( ( node && isSphereDefinition.call( this, node.prototypes ) ) || meshDef.type === "sphere" ) {
            geo = new THREE.SphereGeometry( meshDef.radius || 10, meshDef.segmentsWidth || 8,
                meshDef.segmentsHeight || 6, meshDef.phiStart || 0,
                meshDef.phiLength || Math.PI * 2, meshDef.thetaStart || 0, 
                meshDef.thetaLength || Math.PI );
        } else if ( ( node && isCylinderDefinition.call( this, node.prototypes ) ) || meshDef.type === "cylinder" ) {
            geo = new THREE.CylinderGeometry( meshDef.radiusTop || 10, meshDef.radiusBottom || 10,
                 meshDef.height || 10, meshDef.segmentsRadius || 8, 
                 meshDef.segmentsHeight || 1, meshDef.openEnded );
        } else if ( ( node && isTextDefinition.call( this, node.prototypes ) ) || meshDef.type === "text" ) {
            if ( meshDef.text != "" ) {
                var parms = meshDef.parameters || {};
                geo = new THREE.TextGeometry( meshDef.text, { "size": parms.size || 100,
                    "curveSegments": parms.curveSegments || 4, "font": parms.font || "helvetiker",
                    "weight": parms.weight || "normal", "style": parms.style || "normal",
                    "amount": parms.amount || 50, "height": parms.height || 50, 
                    "bevelThickness": parms.bevelThickness || 10, "bevelSize": parms.bevelSize || 8,
                    "bevelEnabled": Boolean( parms.bevelEnabled ),
                } );
                // geo = new THREE.TextGeometry( meshDef.text, {
                //     size: 80,
                //     height: 20,
                //     curveSegments: 2,
                //     font: "helvetiker"
                // } );
            }
        } else {
            geo = new THREE.Geometry();

            for ( i = 0; geo.vertices && meshDef.positions && ((i*3) < meshDef.positions.length); i++ ) {
                //console.info( "     adding vertices: [" + (meshDef.positions[i*3]) + ", " + (meshDef.positions[i*3+1]) + ", "+ (meshDef.positions[i*3+2]) + " ]" )
                geo.vertices.push( new THREE.Vector3( meshDef.positions[i*3], meshDef.positions[i*3+1],meshDef.positions[i*3+2] ) );   
            }
            for ( i = 0; geo.faces && meshDef.faces && ( (i*3) < meshDef.faces.length ); i++ ) {
                //console.info( "     adding face: [" + (meshDef.faces[i*3]) + ", " + (meshDef.faces[i*3+1]) + ", "+ (meshDef.faces[i*3+2]) + " ]" );
                face = new THREE.Face3( meshDef.faces[i*3], meshDef.faces[i*3+1],meshDef.faces[i*3+2] );
                geo.faces.push( face );
                if ( doubleSided ) {
                    //console.info( "     adding face: [" + (meshDef.faces[i*3+2]) + ", " + (meshDef.faces[i*3+1]) + ", "+ (meshDef.faces[i*3]) + " ]" );
                    face = new THREE.Face3( meshDef.faces[i*3+2], meshDef.faces[i*3+1],meshDef.faces[i*3] );
                    geo.faces.push( face );
                }
            } 
            // TODO: needed doubleSided support for normals
            for ( i = 0 ; geo.faces && meshDef.normals && i < geo.faces.length; i++ ) {
                face = geo.faces[ i ];
                //console.info( "     adding face normal: [" + (meshDef.normals[i*3]) + ", " + (meshDef.normals[i*3+1]) + ", "+ (meshDef.normals[i*3+2]) + " ]" );
                face.vertexNormals.push( new THREE.Vector3( meshDef.normals[i*3], meshDef.normals[i*3+1],meshDef.normals[i*3+2] ) );   
            }
            for ( i = 0; geo.faceVertexUvs && meshDef.uv1 && i < meshDef.uv1.length; i++ ) {
                //console.info( "     adding face vertex uv: [" + (meshDef.uv1[i*2]) + ", " + (meshDef.uv1[i*2+1]) + " ]" );
                geo.faceVertexUvs.push( new THREE.Vector2( meshDef.uv1[i*2], meshDef.uv1[i*2+1] ) );   
            }   
        }
        return geo;
    }

    function createMesh( node, meshDef, doubleSided ) {
        if ( node.threeObject && node.threeObject instanceof THREE.Object3D ) {
            
            var vwfColor, colorValue = 0xFFFFFF;    
            if ( meshDef.color !== undefined ) {
                vwfColor = new utility.color( meshDef.color );
                if ( vwfColor ) {
                    colorValue = vwfColor._decimal;
                }
            }
            var mat = new THREE.MeshLambertMaterial( { "color": colorValue, "ambient": colorValue, side: THREE.DoubleSide } );

            var geo = createGeometry( node, meshDef, doubleSided );
            if ( meshDef.children ) {
                var childGeo = undefined;
                var matrix = new THREE.Matrix4();
                var trans = [];
                for ( var child in meshDef.children ) {
                    childGeo = createGeometry( undefined, meshDef.children[ child ], doubleSided );
                    if ( childGeo ) {
                        trans[ 0 ] = meshDef.children[ child ].properties.translation[ 0 ] || 0;
                        trans[ 1 ] = meshDef.children[ child ].properties.translation[ 1 ] || 0;
                        trans[ 2 ] = meshDef.children[ child ].properties.translation[ 2 ] || 0;
                        geo.merge( childGeo, matrix.makeTranslation( trans[ 0 ], trans[ 1 ], trans[ 2 ] ) );                        
                    }
                }
                geo.computeBoundingBoxSphere && geo.computeBoundingBoxSphere();
            }           

            if ( geo !== undefined ) {
                //geo.computeTangents && geo.computeTangents();

                var mesh = new THREE.Mesh( geo, mat );

                // The child mesh is created after the properties have been initialized, so copy
                // the values of cast and receive shadow so they match
                mesh.castShadow = node.threeObject.castShadow;
                mesh.receiveShadow = node.threeObject.receiveShadow;

                node.threeObject.add( mesh ); 

                node.threeObject.vwfID = node.ID;
                node.threeObject.name = node.name;

                mesh.vwfID = node.ID;
                mesh.name = node.name;
                
                //geo.computeCentroids();
                geo.computeFaceNormals && geo.computeFaceNormals();                
            }

        }         
    }

    //walk the graph of an object, and set all materials to new material clones
    function cloneMaterials( nodein ) {
	
		    //sort the materials in the model, and when cloneing, make the new model share the same material setup as the old.
	    	var materialMap = {};
	
        walkGraph( nodein, function( node ) {
            if(node.material) {
                if ( node.material instanceof THREE.Material ) {
                    if(!materialMap[node.material.uuid]) {
                				materialMap[node.material.uuid] = [];
                    }
			       			  materialMap[node.material.uuid].push( [ node, -1 ] );
                }
                else if ( node.material instanceof THREE.MeshFaceMaterial ) {
                    if ( node.material.materials ) {
                        for ( var index = 0; index < node.material.materials.length; index++ ) {
                            if ( node.material.materials[ index ] instanceof THREE.Material ) {
                                if(!materialMap[node.material.materials[ index ].uuid]) {
                				            materialMap[node.material.materials[ index ].uuid] = [];
                                }
			       	            		  materialMap[node.material.materials[ index ].uuid].push( [ node, index ] );
                            }
                        }
                    }
                }              
            }
        });
		
		    for(var i in materialMap)
    		{
		    	  var newmat;
            if ( materialMap[ i ][ 0 ][ 1 ] < 0 ) {
                newmat = materialMap[ i ][ 0 ][ 0 ].material.clone( );
            }
            else {
                newmat = materialMap[ i ][ 0 ][ 0 ].material.materials[ materialMap[ i ][ 0 ][ 1 ] ].clone( );
            }
			      for ( var j =0; j < materialMap[i].length; j++ ) {
                if ( materialMap[ i ][ j ][ 1 ] < 0 ) {
    			     	    materialMap[ i ][ j ][ 0 ].material = newmat;
                }
                else {
                    materialMap[ i ][ j ][ 0 ].material.materials[ materialMap[ i ][ j ][ 1 ] ] = newmat;
                }
            }
		    }
    }

    function loadAsset( parentNode, node, childType, propertyNotifyCallback ) {

        var nodeCopy = node; 
        var nodeID = node.ID;
        var childName = node.name;
        var threeModel = this;
        var sceneNode = this.state.scenes[ this.kernel.application() ];
        var parentObject3 = parentNode.threeObject ? parentNode.threeObject : parentNode.threeScene;
        //console.info( "---- loadAsset( "+parentNode.name+", "+node.name+", "+childType+" )" );

        node.assetLoaded = function( geometry , materials) { 
            //console.info( "++++ assetLoaded( "+parentNode.name+", "+node.name+", "+childType+" )" );
            sceneNode.pendingLoads--;
            var removed = false;
            
            // THREE JSON model
            if ( childType == "model/x-threejs-morphanim+json" || childType == "model/x-threejs-skinned+json" ) {

                for ( var i = 0; i < materials.length; i++ ) {
                    
                    var m = materials[ i ];
                    
                    // Do we have Morph Target animations?
                    if ( geometry.morphTargets.length > 0 ) {
                        m.morphTargets = true;
                    }

                    // Do we have skeletal animations?
                    if ( geometry.animation ) {
                        m.skinning = true;
                    }
                }
                
                var meshMaterial;
                if ( materials.length > 1 ) {

                    // THREE.MeshFaceMaterial for meshes that have multiple materials
                    meshMaterial = new THREE.MeshFaceMaterial( materials );

                } else {

                    // This mesh has only one material
                    meshMaterial = materials[ 0 ];
                }

                if ( childType == "model/x-threejs-morphanim+json" ) {
                    var asset = new THREE.MorphAnimMesh( geometry, meshMaterial );
                
                } else {  // childType == "model/x-threejs-skinned+json"

                    // THREE.AnimationHandler had a couple of methods
                    // depricated, check THREE.UCSCharacter

                    //  THREE.AnimationHandler.add( geometry.animation );   
                    var asset = new THREE.SkinnedMesh( geometry, meshMaterial );
                    var skinnedAnimation = new THREE.Animation( asset, geometry.animation ); 
                    skinnedAnimation.play();
                }

                asset.updateMatrix();

            } else {  // Collada model
                var asset = geometry;
            }
         
            var keyframeAnimations, animatedMesh;
            if ( asset.animations && asset.animations.length > 0 ) {
                keyframeAnimations = asset.animations;
            }

            if ( asset.scene ) {
                asset = asset.scene;
            }

            var meshes = [];
            GetAllLeafMeshes( asset, meshes );

            for ( var i = 0; i < meshes.length; i++ ) {
                if ( meshes[ i ].material.map != null ) {
                    fixMissingUVs( meshes[ i ] );
                }
            }
            
            asset.updateMatrixWorld();
            
            asset.matrix = new THREE.Matrix4();
            asset.matrixAutoUpdate = false;
            
            // Don't make a copy of the three object if there are keyframe or skeletal animations associated with it
            // until we figure out a way to copy them successfully.
            if ( keyframeAnimations || skinnedAnimation ) {
                nodeCopy.threeObject = asset;
            }
            else {
                nodeCopy.threeObject = asset.clone();
            }
            
            //make sure that the new object has a unique material
            cloneMaterials( nodeCopy.threeObject );

            //find and bind the animations
            //NOTE: this would probably be better handled by walking and finding the animations and skins only on the 
            //property setter when needed.
		
            animatedMesh = [];
            walkGraph(nodeCopy.threeObject,function( node ){
                if( node instanceof THREE.SkinnedMesh  || node instanceof THREE.MorphAnimMesh ) {
                    animatedMesh.push( node );
                }
            });
            nodeCopy.threeObject.animatedMesh = animatedMesh;
            nodeCopy.threeObject.updateMatrixWorld();
            
            removeAmbientLights.call( this, nodeCopy.threeObject );

            parentObject3.add( nodeCopy.threeObject );
            nodeCopy.threeObject.name = childName;
            nodeCopy.threeObject.vwfID = nodeID;
            nodeCopy.threeObject.matrixAutoUpdate = false;

            if( keyframeAnimations ) {
                //var animHandler = THREE.AnimationHandler;
                nodeCopy.threeObject.kfAnimations = [];
                nodeCopy.threeObject.animations = keyframeAnimations;

                // Initialize the key frame animations
                for ( var i = 0; i < keyframeAnimations.length; i++ ) {
                    var animation = keyframeAnimations[ i ];

                    if ( !animation.node ) {
                        continue;
                    }

                    // Save references to the animations on the node that is animated, so that it can play separately
                    if( animation.node.animations == undefined ) {
                        animation.node.animations = [];
                    }
                    if( animation.node.kfAnimations == undefined ) {
                        animation.node.kfAnimations = [];
                    }
                    animation.node.animations.push( animation );
                    
                    // add has been depricated
                    //animHandler.add( animation );
                    //var kfAnimation = new THREE.KeyFrameAnimation( animation.node, animation.name );

                    var kfAnimation = new THREE.KeyFrameAnimation( animation );

                    kfAnimation.timeScale = 1;
                    nodeCopy.threeObject.kfAnimations.push( kfAnimation );
                    animation.node.kfAnimations.push( kfAnimation );
                    for ( var h = 0; h < kfAnimation.hierarchy.length; h++ ) {
                        var keys = kfAnimation.data.hierarchy[ h ].keys;
                        var sids = kfAnimation.data.hierarchy[ h ].sids;
                        var obj = kfAnimation.hierarchy[ h ];

                        if ( keys.length && sids ) {
                            for(var s = 0; s < sids.length; s++) {
                                var sid = sids[s];
                                var next = kfAnimation.getNextKeyWith(sid, h, 0);
                                if(next) next.apply(sid);
                            }
                            obj.matrixAutoUpdate = false;
                            kfAnimation.data.hierarchy[h].node.updateMatrix();
                            obj.matrixWorldNeedsUpdate = true;
                        }
                    }
                    kfAnimation.play(false, 0);
                }
            }
            if(animatedMesh) {
                nodeCopy.threeObject.animatedMesh = animatedMesh;
            }
            
            // remember that this was a loaded collada file
            nodeCopy.threeObject.loadedColladaNode = true;

            for ( var j = 0; j < sceneNode.srcAssetObjects.length; j++ ) {
                if ( sceneNode.srcAssetObjects[j] == nodeCopy ){
                    sceneNode.srcAssetObjects.splice( j, 1 );
                    removed = true;
                }
            } 

            updateStoredTransform( node );

            // Since prototypes are created before the object, it does not get "setProperty" updates for
            // its prototype (and behavior) properties.  Therefore, we cycle through those properties to
            // notify the drivers of the property values so they can react accordingly
            // TODO: Have the kernel send the "setProperty" updates itself so the driver need not
            propertyNotifyCallback();

            // let vwf know the asset is loaded 
            if ( nodeCopy.loadingCallback ) {

                //console.info( "========= LOADED ========== "+node.name+" ========= LOADED ==========" );
                nodeCopy.loadingCallback( true );                    
            }

            //get the entry from the asset registry
            reg = threeModel.assetRegistry[nodeCopy.source];
            
            // If there are animations, set loaded to false and don't store the asset
            // in the registry, since the animations don't work with the copy process
            if(keyframeAnimations) {
                reg.pending = false;
                reg.loaded = false;
            }
            else {
                //it's not pending, and it is loaded
                reg.pending = false;
                reg.loaded = true;
                
                //store this asset in the registry
                reg.node = asset;
            }
            
            //if any callbacks were waiting on the asset, call those callbacks
            for( var i = 0; i < reg.callbacks.length; i++ ) {
                reg.callbacks[i]( asset );
            }
            
            //nothing should be waiting on callbacks now.  
            reg.callbacks = [];  
            
        }
        node.name = childName;
      
        //create an asset registry if one does not exist for this driver
        if( !this.assetRegistry ) {
            this.assetRegistry = {};
        }
        
        // if there is no entry in the registry, create one
        if( !this.assetRegistry[node.source] ) {
            
            //it's new, so not waiting, and not loaded
            this.assetRegistry[node.source] = {};
            this.assetRegistry[node.source].loaded = false;
            this.assetRegistry[node.source].pending = false;
            this.assetRegistry[node.source].callbacks = [];
        }
        
        //grab the registry entry for this asset
        var reg = this.assetRegistry[node.source];
        
        //if the asset entry is not loaded and not pending, you'll have to actually go download and parse it
        if( reg.loaded == false && reg.pending == false ) {
            
            //thus, it becomes pending
            reg.pending = true;
            
            sceneNode.srcAssetObjects.push( node.threeObject );
            
            //node.threeObject.vwfID = nodeID;
            sceneNode.pendingLoads++;
          
            switch ( childType ) {
                
                case "model/vnd.collada+xml":
                    node.parse = true;
                    node.loader = new THREE.ColladaLoader();
                    node.loader.options.convertUpAxis = true;
                    node.loader.options.upAxis = "Z";
                    node.loader.load(node.source,node.assetLoaded.bind( this ));
                    break;

                case "model/vnd.osgjs+json+compressed":
                    node.loader = new UTF8JsonLoader( node,node.assetLoaded.bind( this ) );
                    break;

                case "model/x-threejs-morphanim+json":
                case "model/x-threejs-skinned+json":
                    node.loader = new THREE.JSONLoader()
                    node.loader.load( node.source, node.assetLoaded.bind( this ) );
                    break;

                case "model/vnd.gltf+json":
                    //create a queue to hold requests to the loader, since the loader cannot be re-entered for parallel loads
                    if ( !THREE.glTFLoader.queue )
                    {
                        //task is an object that holds the info about what to load
                        //nextTask is supplied by async to trigger the next in the queue
                        THREE.glTFLoader.queue = new async.queue( function( task, nextTask ) {
                            var node = task.node;
                            var cb = task.cb;
                            //call the actual load function
                            //signature of callback dictated by loader
                            node.loader.load( node.source, function( geometry , materials ) {
                                //ok, this model loaded, we can start the next load
                                nextTask();
                                //do whatever it was (asset loaded) that this load was going to do when complete
                                cb( geometry , materials );
                            } );

                        }, 1 );
                    }
                    node.loader = new THREE.glTFLoader();
                    node.loader.useBufferGeometry = true;
                    //we need to queue up our entry to this module, since it cannot handle re-entry. This means that while it 
                    //is an async function, it cannot be entered again before it completes
                    THREE.glTFLoader.queue.push( { 
                        node: node,
                        cb: node.assetLoaded.bind( this ) 
                    } );
                    break;

                default:
                    self.logger.warnx( "Unable to import " + node.source + ".  Unsupported file type: " + childType );
                    break;

            }
        }

        //if the asset registry entry is not pending and it is loaded, then just grab a copy, 
        //no download or parse necessary
        else if( reg.loaded == true && reg.pending == false ) {
            var asset = (reg.node.clone());
		
            // make sure the materails are unique
            cloneMaterials( asset );
            
            var n = asset;
            var skins = [];
            walkGraph( n, function( node ) {
                if( node instanceof THREE.SkinnedMesh || node instanceof THREE.MorphAnimMesh ) {
                    skins.push( node );
                }
            });

            n.animatedMesh = skins;
            nodeCopy.threeObject = asset;
            
            nodeCopy.threeObject.matrix = new THREE.Matrix4();
            nodeCopy.threeObject.matrixAutoUpdate = false;
            parentObject3.add( nodeCopy.threeObject );
            nodeCopy.threeObject.name = childName;
            nodeCopy.threeObject.vwfID = nodeID;
            nodeCopy.threeObject.matrixAutoUpdate = false;
            nodeCopy.threeObject.updateMatrixWorld( true );
            propertyNotifyCallback();
            window.setTimeout( function() {
                nodeCopy.loadingCallback( true ); 
            }, 10);
          
        }
        
        //if it's pending but not done, register a callback so that when it is done, it can be attached.
        else if( reg.loaded == false && reg.pending == true ) {  
            sceneNode.srcAssetObjects.push( node.threeObject );
          
            //so, not necessary to do all the other VWF node goo stuff, as that will be handled by the node that requested
            //the asset in teh first place
            
            reg.callbacks.push( function( node ) {
            
                //just clone the node and attach it.
                //this should not clone the geometry, so much lower memory.
                //seems to take near nothing to duplicated animated avatar            
                var n = node.clone();
                cloneMaterials( n );
                var skins = [];
                walkGraph( n, function( node ) {
                    if( node instanceof THREE.SkinnedMesh || node instanceof THREE.MorphAnimMesh ) {
                        skins.push( node );
                    }            
                });
                n.animatedMesh = skins;
                nodeCopy.threeObject = n;            
    
                nodeCopy.threeObject.matrix = new THREE.Matrix4();
                nodeCopy.threeObject.matrixAutoUpdate = false;
                
                removeAmbientLights.call(this, nodeCopy.threeObject);
    
                parentObject3.add( nodeCopy.threeObject );
                nodeCopy.threeObject.name = childName;
                nodeCopy.threeObject.vwfID = nodeID;
                nodeCopy.threeObject.matrixAutoUpdate = false;
                nodeCopy.threeObject.updateMatrixWorld( true );
                propertyNotifyCallback();
                nodeCopy.loadingCallback( true ); 
            
            });
        }
            
    }

    //walk the scenegraph from the given root, calling the given function on each node
    function walkGraph( root, func ) {
        if( root ) {
            func( root );
        }
        for( var i =0; i < root.children.length; i++ ) {
            walkGraph( root.children[i], func );
        }
    }


    // Strips the imported scene's ambient lights
    function removeAmbientLights( threeObject ) {
        for( var i = threeObject.children.length -1; i >= 0; i-- )
            {
                if( threeObject.children[i] instanceof THREE.AmbientLight )
                {
                    threeObject.remove( threeObject.children[i] );
                }
            }
    }
    

    function getObjectID( objectToLookFor, bubbleUp, debug ) {

        var objectIDFound = -1;
            
        while (objectIDFound == -1 && objectToLookFor) {
            if ( debug ) {
                this.logger.info("====>>>  vwf.model-glge.mousePick: searching for: " + path(objectToLookFor) );
            }
            $.each( this.state.nodes, function (nodeID, node) {
                if ( node.threeObject == objectToLookFor && !node.glgeMaterial ) {
                    if ( debug ) { this.logger.info("pick object name: " + name(objectToLookFor) + " with id = " + nodeID ); }
                    objectIDFound = nodeID;
                }
            });
            if ( bubbleUp ) {
                objectToLookFor = objectToLookFor.parent;
            } else {
                objectToLookFor = undefined;
            }
        }
        if (objectIDFound != -1)
            return objectIDFound;

        return undefined;
    }
    
    function isLightDefinition( prototypes ) {
        var foundLight = false;
        if ( prototypes ) {
            for ( var i = 0; i < prototypes.length && !foundLight; i++ ) {
                foundLight = ( prototypes[i] == "http://vwf.example.com/light.vwf" );
            }
        }

        return foundLight;
    }
    function createLight( nodeID, childID, type, childName ) {

        var child = this.state.nodes[childID];
        if ( child ) {

            switch( type ) {
                
                case "http://vwf.example.com/directionallight.vwf":
                    child.threeObject = new THREE.DirectionalLight( 'FFFFFF' );
                    break;

                case "http://vwf.example.com/spotlight.vwf":
                    child.threeObject = new THREE.SpotLight( 'FFFFFF' );
                    break;

                case "http://vwf.example.com/hemispherelight.vwf":
                    child.threeObject = new THREE.HemisphereLight('FFFFFF','FFFFFF',1);
                    break;

                case "http://vwf.example.com/pointlight.vwf":
                default:
                    child.threeObject = new THREE.PointLight( 'FFFFFF', 1, 1000 );
                    break;    
            }
      
            child.threeObject.name = childName;
            child.name = childName;
            addThreeChild.call( this, nodeID, childID );
        } 
               
    }
    function vwfColor( color ) {
        var vwfColor = {};
        vwfColor['r'] = color['r']*255;
        vwfColor['g'] = color['g']*255;
        vwfColor['b'] = color['b']*255;                                
        if ( color['a'] !== undefined && color['a'] != 1 ) {
            vwfColor['a'] = color['a'];
            vwfColor = new utility.color( "rgba("+vwfColor['r']+","+vwfColor['g']+","+vwfColor['b']+","+vwfColor['a']+")" );
        } else {
            vwfColor = new utility.color( "rgb("+vwfColor['r']+","+vwfColor['g']+","+vwfColor['b']+")" );
        }
        return vwfColor;        
    }

    function CreateParticleSystem(nodeID, childID, childName )
    {
        
        
        var child = this.state.nodes[childID];
        if ( child ) 
        {
        
            
            
            // create the particle variables
           
            var particles = new THREE.Geometry();
            
                  
                

            //default material expects all computation done cpu side, just renders
			// note that since the color, size, spin and orientation are just linear
		    // interpolations, they can be done in the shader
            var vertShader_default = 
            "attribute float size; \n"+
            "attribute vec4 vertexColor;\n"+
            "varying vec4 vColor;\n"+
			"attribute vec4 random;\n"+
			"varying vec4 vRandom;\n"+
			"uniform float sizeRange;\n"+
			"uniform vec4 colorRange;\n"+
            "void main() {\n"+
            "   vColor = vertexColor + (random -0.5) * colorRange;\n"+
            "   vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n"+
			"   float psize = size + (random.y -0.5) * sizeRange;\n"+
            "   gl_PointSize = psize * ( 1000.0/ length( mvPosition.xyz ) );\n"+
            "   gl_Position = projectionMatrix * mvPosition;\n"+
			 "   vRandom = random;"+
            "}    \n";
            var fragShader_default = 
            "uniform float useTexture;\n"+
            "uniform sampler2D texture;\n"+
            "varying vec4 vColor;\n"+
			"varying vec4 vRandom;\n"+
			"uniform float time;\n"+
            "uniform float maxSpin;\n"+
            "uniform float minSpin;\n"+
			"uniform float maxOrientation;\n"+
            "uniform float minOrientation;\n"+
			"uniform float textureTiles;\n"+
            "void main() {\n"+
			            " vec2 coord = vec2(0.0,0.0);"+
			" vec2 orig_coord = vec2(gl_PointCoord.s,1.0-gl_PointCoord.t);"+
            " float spin = mix(maxSpin,minSpin,vRandom.x);"+
            " float orientation = mix(maxOrientation,minOrientation,vRandom.y);"+
            " coord.s = (orig_coord.s-.5)*cos(time*spin+orientation)-(orig_coord.t-.5)*sin(time*spin+orientation);"+
            " coord.t = (orig_coord.t-.5)*cos(time*spin+orientation)+(orig_coord.s-.5)*sin(time*spin+orientation);"+
			" coord = coord + vec2(.5,.5);\n"+
			" coord = coord/textureTiles;\n"+
			" coord.x = clamp(coord.x,0.0,1.0/textureTiles);\n"+
			" coord.y = clamp(coord.y,0.0,1.0/textureTiles);\n"+
			" coord += vec2(floor(vRandom.x*textureTiles)/textureTiles,floor(vRandom.y*textureTiles)/textureTiles);\n"+
            "   vec4 outColor = (vColor * texture2D( texture, coord  )) *useTexture + vColor * (1.0-useTexture);\n"+
            
            "   gl_FragColor = outColor;\n"+
            "}\n";
			
			//the default shader - the one used by the analytic solver, just has some simple stuff
			//note that this could be changed to do just life and lifespan, and calculate the 
			//size and color from to uniforms. Im not going to bother
            var attributes_default = {
                size: { type: 'f', value: [] },
                vertexColor:   {    type: 'v4', value: [] },
				random:   { type: 'v4', value: [] },
				
            };
            var uniforms_default = {
                amplitude: { type: "f", value: 1.0 },
                texture:   { type: "t", value: new THREE.Texture( new Image() ) },
                useTexture: { type: "f", value: 0.0 },
                maxSpin: { type: "f", value: 0.0 },
                minSpin: { type: "f", value: 0.0 },
				maxOrientation: { type: "f", value: 0.0 },
                minOrientation: { type: "f", value: 0.0 },
				time: { type: "f", value: 0.0 },
				fractime: { type: "f", value: 0.0 },
				sizeRange: { type: "f", value: 0.0 },
				textureTiles: { type: "f", value: 1.0 },
				colorRange:   { type: 'v4', value: new THREE.Vector4(0,0,0,0) },
				startColor:{type: "v4", value:new THREE.Vector4()},
                endColor:{type: "v4", value:new THREE.Vector4()},
                startSize:{type:"f", value:1},
                endSize:{type:"f", value:1},
            };
            uniforms_default.texture.value.wrapS = uniforms_default.texture.value.wrapT = THREE.RepeatWrapping;
            var shaderMaterial_default = new THREE.ShaderMaterial( {
                uniforms:       uniforms_default,
                attributes:     attributes_default,
                vertexShader:   vertShader_default,
                fragmentShader: fragShader_default

            });
            
			//the interpolate shader blends from one simulation step to the next on the shader
			//this	allows for a complex sim to run at a low framerate, but still have smooth motion
            //this is very efficient, as it only requires sending data up to the gpu on each sim tick		
			//reuse the frag shader from the normal material	
            var vertShader_interpolate = 
           
			"attribute float age; \n"+
			"attribute float lifespan; \n"+
			"attribute vec3 previousPosition;\n"+
            "varying vec4 vColor;\n"+
			"attribute vec4 random;\n"+
			"varying vec4 vRandom;\n"+
			"uniform float sizeRange;\n"+
			"uniform vec4 colorRange;\n"+
			"uniform float fractime;\n"+
			"uniform float startSize;\n"+
            "uniform float endSize;\n"+
            "uniform vec4 startColor;\n"+
            "uniform vec4 endColor;\n"+
            "void main() {\n"+
            "   vColor = mix(startColor,endColor,(age+fractime*3.33)/lifespan) + (random -0.5) * colorRange;\n"+
            "   vec4 mvPosition = modelViewMatrix * vec4(mix(previousPosition,position,fractime), 1.0 );\n"+
			"   float psize = mix(startSize,endSize,(age+fractime*3.33)/lifespan) + (random.y -0.5) * sizeRange;\n"+
            "   gl_PointSize = psize * ( 1000.0/ length( mvPosition.xyz ) );\n"+
            "   gl_Position = projectionMatrix * mvPosition;\n"+
			 "   vRandom = random;"+
            "}    \n";
            
			//the interpolation does need to remember the previous position
			var attributes_interpolate = {
				random:   attributes_default.random,
				previousPosition: { type: 'v3', value: [] },
				age: { type: 'f', value: [] },
				lifespan: { type: 'f', value: [] }
            };
            var shaderMaterial_interpolate = new THREE.ShaderMaterial( {
                uniforms:       uniforms_default,
                attributes:     attributes_interpolate,
                vertexShader:   vertShader_interpolate,
                fragmentShader: fragShader_default

            });
            
            
            //analytic shader does entire simulation on GPU
			//it cannot account for drag, gravity. nor can it generate new randomness. Each particle has it's randomness assigned and it 
			//just repeats the same motion over and over. Also, the other solvers can hold a particle until 
			//it can be reused based on the emitRate. This cannot, as the entire life of the particle must be 
			//computed from an equation given just time t. It does offset them in time to avoid all the particles 
			//being generated at once. Also, it does not account for emitter motion. 
			//upside : very very efficient. No CPU intervention required
            var vertShader_analytic = 
            "attribute float size; \n"+
            "attribute vec4 vertexColor;\n"+
            "attribute vec3 acceleration;\n"+
            "attribute vec3 velocity;\n"+
            "attribute float lifespan;\n"+
            "attribute vec4 random;\n"+
            "uniform float time;\n"+
            "uniform float startSize;\n"+
            "uniform float endSize;\n"+
            "uniform vec4 startColor;\n"+
            "uniform vec4 endColor;\n"+
            "varying vec4 vColor;\n"+
            "varying vec4 vRandom;\n"+
			"uniform float sizeRange;\n"+
			"uniform vec4 colorRange;\n"+
            "void main() {\n"+
			//randomly offset in time
            "   float lifetime = mod( random.x * lifespan + time, lifespan );"+
			//solve for position
            "   vec3 pos2 = position.xyz + velocity*lifetime + (acceleration*lifetime*lifetime)/2.0;"+ // ;
            "   vec4 mvPosition = modelViewMatrix * vec4( pos2.xyz, 1.0 );\n"+
			//find random size based on randomness, start and end size, and size range
			"   float psize = mix(startSize,endSize,lifetime/lifespan) + (random.y -0.5) * sizeRange;\n"+
            "   gl_PointSize = psize * ( 1000.0/ length( mvPosition.xyz ) );\n"+
            "   gl_Position = projectionMatrix * mvPosition;\n"+
			" vec4 nR = (random -0.5);\n"+
			//find random color based on start and endcolor, time and colorRange
            "   vColor = mix(startColor,endColor,lifetime/lifespan)  +  nR * colorRange;\n"+
            "   vRandom = random;"+
            "}    \n";
            var fragShader_analytic = 
            "uniform float useTexture;\n"+
            "uniform sampler2D texture;\n"+
            "uniform float time;\n"+
            "uniform float maxSpin;\n"+
            "uniform float minSpin;\n"+
            "varying vec4 vColor;\n"+
            "varying vec4 vRandom;\n"+
			"uniform float maxOrientation;\n"+
            "uniform float minOrientation;\n"+
			"uniform float textureTiles;\n"+
            "void main() {\n"+
           
			//bit of drama for dividing into 4 or 9 'virtual' textures
			//nice to be able to have different images on particles
 		    " vec2 coord = vec2(0.0,0.0);"+
			" vec2 orig_coord = vec2(gl_PointCoord.s,1.0-gl_PointCoord.t);"+
            " float spin = mix(maxSpin,minSpin,vRandom.x);"+
            " float orientation = mix(maxOrientation,minOrientation,vRandom.y);"+
            " coord.s = (orig_coord.s-.5)*cos(time*spin+orientation)-(orig_coord.t-.5)*sin(time*spin+orientation);"+
            " coord.t = (orig_coord.t-.5)*cos(time*spin+orientation)+(orig_coord.s-.5)*sin(time*spin+orientation);"+
			" coord = coord + vec2(.5,.5);\n"+
			" coord = coord/textureTiles;\n"+
			" coord.x = clamp(coord.x,0.0,1.0/textureTiles);\n"+
			" coord.y = clamp(coord.y,0.0,1.0/textureTiles);\n"+
			" coord += vec2(floor(vRandom.x*textureTiles)/textureTiles,floor(vRandom.y*textureTiles)/textureTiles);\n"+
            
			//get the color from the texture and blend with the vertexColor.
			" vec4 outColor = (vColor * texture2D( texture, coord )) *useTexture + vColor * (1.0-useTexture);\n"+
            
            "   gl_FragColor = outColor;\n"+
            "}\n";
            var attributes_analytic = {
                acceleration:   {   type: 'v3', value: [] },
                velocity:   {   type: 'v3', value: [] },
                lifespan:  attributes_interpolate.lifespan,
                random:   attributes_default.random,
                vertexColor : attributes_default.vertexColor,
                size: attributes_default.size
            };
            var shaderMaterial_analytic = new THREE.ShaderMaterial( {
                uniforms:       uniforms_default,
                attributes:     attributes_analytic,
                vertexShader:   vertShader_analytic,
                fragmentShader: fragShader_analytic
            });

            // create the particle system
            var particleSystem = new THREE.PointCloud( particles, shaderMaterial_default );
            
			//keep track of the shaders
            particleSystem.shaderMaterial_analytic = shaderMaterial_analytic;
            particleSystem.shaderMaterial_default = shaderMaterial_default;
			particleSystem.shaderMaterial_interpolate = shaderMaterial_interpolate;
            
			//setup all the default values
            particleSystem.minVelocity = [0,0,0];
            particleSystem.maxVelocity = [0,0,0];
            particleSystem.maxAcceleration = [0,0,0];
            particleSystem.minAcceleration = [0,0,0];
            particleSystem.minLifeTime = 0;
            particleSystem.maxLifeTime = 1;
            particleSystem.emitterType = 'point';
            particleSystem.emitterSize = [0,0,0];
            particleSystem.startColor = [1,1,1,1];
            particleSystem.endColor = [0,0,0,0];
            particleSystem.regenParticles = [];
            particleSystem.maxRate = 1000;
            particleSystem.particleCount = 1000;
            particleSystem.damping = 0;
            particleSystem.startSize = 3;
            particleSystem.endSize = 3;
			particleSystem.gravity = 0;
			particleSystem.gravityCenter = [0,0,0];
            particleSystem.velocityMode = 'cartesian';
			particleSystem.temp = new THREE.Vector3();
           
			//create a new particle. create and store all the values for vertex attributes in each shader
		   particleSystem.createParticle = function(i)
            {
                var particle = new THREE.Vector3(0,0,0);
                this.geometry.vertices.push(particle);
            
                particle.i = i;
                
				//the world space position
                particle.world = new THREE.Vector3();
				//the previous !tick! (not frame) position
                particle.prevworld = new THREE.Vector3();
				this.shaderMaterial_interpolate.attributes.previousPosition.value.push(particle.prevworld);
                //the color
				var color = new THREE.Vector4(1,1,1,1);
                this.shaderMaterial_default.attributes.vertexColor.value.push(color);
				//age
				this.shaderMaterial_interpolate.attributes.age.value.push(1);
                particle.color = color;
				
				//the sise
                this.shaderMaterial_default.attributes.size.value.push(1);
                var self = this;
				//set the size - stored per vertex
                particle.setSize = function(s)
                {
                    self.material.attributes.size.value[this.i] = s;
                }
				//set the age - stored per vertex
				particle.setAge = function(a)
				{
					this.age = a;
					self.shaderMaterial_interpolate.attributes.age.value[this.i] = this.age;
				}
				//the lifespan - stored per vertex
				particle.setLifespan = function(a)
				{
					this.lifespan = a;
					self.shaderMaterial_interpolate.attributes.lifespan.value[this.i] = this.a;
				}
                
				//This looks like it could be computed from the start and end plus random on the shader
				//doing this saves computetime on the shader at expense of gpu mem
                shaderMaterial_analytic.attributes.acceleration.value.push(new THREE.Vector3());
                shaderMaterial_analytic.attributes.velocity.value.push(new THREE.Vector3());
                shaderMaterial_analytic.attributes.lifespan.value.push(1);
                shaderMaterial_analytic.attributes.random.value.push(new THREE.Vector4(Math.random(),Math.random(),Math.random(),Math.random()));
                return particle;
            }
            
			//Generate a new point in space based on the emitter type and size
            particleSystem.generatePoint = function()
            {
				//generate from a point
				//TODO: specify point?
                if(this.emitterType.toLowerCase() == 'point')
                {
                    return new THREE.Vector3(0,0,0);
                }
				//Generate in a box
				//assumes centered at 0,0,0
                if(this.emitterType.toLowerCase() == 'box')
                {
                    var x = this.emitterSize[0] * Math.random() - this.emitterSize[0]/2;
                    var y = this.emitterSize[1] * Math.random() - this.emitterSize[1]/2;
                    var z = this.emitterSize[2] * Math.random() - this.emitterSize[2]/2;
                    
                    return new THREE.Vector3(x,y,z);
                }
				//Generate in a sphere
				//assumes centered at 0,0,0
                if(this.emitterType.toLowerCase() == 'sphere')
                {
                    var u2 = Math.random();
                    u2 = Math.pow(u2,1/3);
                    var o = this.emitterSize[0] * Math.random() * Math.PI*2;
                    var u = this.emitterSize[1] * Math.random() * 2 - 1;
                    var r = this.emitterSize[2]  * u2;
                    var x = Math.cos(o)*Math.sqrt(1-(u*u));
                    var y = Math.sin(o)*Math.sqrt(1-(u*u));
                    var z = u;
                    
                    
                    return new THREE.Vector3(x,y,z).setLength(r);
                }
            
            }
			//setup the particles with new values
            particleSystem.rebuildParticles = function()
            {
                for(var i = 0; i < this.geometry.vertices.length; i++)
                {
                    this.setupParticle(this.geometry.vertices[i],this.matrix);
                }
            }
			//set the particles initial values. Used when creating and resuing particles
            particleSystem.setupParticle = function(particle,mat,inv)
            {
                
                particle.x = 0;
                particle.y = 0;
                particle.z = 0;
                
				//generate a point in objects space, the move to world space
                particle.world = this.generatePoint().applyMatrix4( mat );
                
				//back up initial (needed by the analyticShader)
                particle.initialx = particle.world.x;
                particle.initialy = particle.world.y;
                particle.initialz = particle.world.z;
                
				//start at initial pos
                particle.x = particle.initialx;
                particle.y = particle.initialy;
                particle.z = particle.initialz;
                
              
                //start stoped, age 0
                particle.age = 0;
                particle.velocity = new THREE.Vector3(0,0,0);
                particle.acceleration = new THREE.Vector3( 0,0,0);  
                particle.lifespan = 1;  
                
				//Generate the initial velocity
				//In this mode, you specify a min and max x,y,z
                if(this.velocityMode == 'cartesian')
                {
                    particle.velocity.x = this.minVelocity[0] + (this.maxVelocity[0] - this.minVelocity[0]) * Math.random();
                    particle.velocity.y = this.minVelocity[1] + (this.maxVelocity[1] - this.minVelocity[1]) * Math.random();
                    particle.velocity.z = this.minVelocity[2] + (this.maxVelocity[2] - this.minVelocity[2]) * Math.random();
                }
				//In this mode, you give a pitch and yaw from 0,1, and a min and max length.
				//This is easier to emit into a circle, or a cone section
                if(this.velocityMode == 'spherical')
                {
                
                    //random sphercial points concentrate at poles
                    /* var r = this.minVelocity[2] + (this.maxVelocity[2] - this.minVelocity[2]) * Math.random();
                    var t = this.minVelocity[1] + (this.maxVelocity[1] - this.minVelocity[1]) * Math.random() * Math.PI*2;
                    var w = this.minVelocity[0] + (this.maxVelocity[0] - this.minVelocity[0]) * Math.random() * Math.PI - Math.PI/2;
                    particle.velocity.x = r * Math.sin(t)*Math.cos(w);
                    particle.velocity.y = r * Math.sin(t)*Math.sin(w);
                    particle.velocity.z = r * Math.cos(t); */
                    
					//better distribution
                    var o = this.minVelocity[0] + (this.maxVelocity[0] - this.minVelocity[0]) * Math.random() * Math.PI*2;
                    var u = this.minVelocity[1] + (this.maxVelocity[1] - this.minVelocity[1]) * Math.random() * 2 - 1;
                    var u2 = Math.random();
                    u2 = Math.pow(u2,1/3);
                    var r = this.minVelocity[2] + (this.maxVelocity[2] - this.minVelocity[2]) * u2;
                    particle.velocity.x = Math.cos(o)*Math.sqrt(1-(u*u));
                    particle.velocity.y = Math.sin(o)*Math.sqrt(1-(u*u));
                    particle.velocity.z = u;
                    particle.velocity.setLength(r);
                }
                
				//The velocity should be in world space, but is generated in local space for 
				//ease of use
                mat = mat.clone();
                mat.elements[12] = 0;
                mat.elements[13] = 0;
                mat.elements[14] = 0;
                particle.velocity.applyMatrix4( mat );
                
                //accelerations are always world space, just min and max on each axis
                particle.acceleration.x = this.minAcceleration[0] + (this.maxAcceleration[0] - this.minAcceleration[0]) * Math.random();
                particle.acceleration.y = this.minAcceleration[1] + (this.maxAcceleration[1] - this.minAcceleration[1]) * Math.random();
                particle.acceleration.z = this.minAcceleration[2] + (this.maxAcceleration[2] - this.minAcceleration[2]) * Math.random();
                particle.setLifespan(this.minLifeTime + (this.maxLifeTime - this.minLifeTime) * Math.random());
                
				//color is start color
				particle.color.x = this.startColor[0];
                particle.color.y = this.startColor[1];
                particle.color.z = this.startColor[2];
                particle.color.w = this.startColor[3];
                
				//save the values into the attributes
                shaderMaterial_analytic.attributes.acceleration.value[particle.i] = (particle.acceleration);
                shaderMaterial_analytic.attributes.velocity.value[particle.i] = (particle.velocity);
                shaderMaterial_analytic.attributes.lifespan.value[particle.i] = (particle.lifespan);
                
                
                shaderMaterial_analytic.attributes.acceleration.needsUpdate = true;
                shaderMaterial_analytic.attributes.velocity.needsUpdate = true;
                shaderMaterial_analytic.attributes.lifespan.needsUpdate = true;
                this.geometry.verticesNeedUpdate = true;
                //randomly move the particle up to one step in time
                particle.prevworld.x = particle.x;
                particle.prevworld.y = particle.y;
                particle.prevworld.z = particle.z;

            }
            
			//when updating in AnalyticShader mode, is very simple, just inform the shader of new time.
            particleSystem.updateAnalyticShader = function(time)
            {   
                particleSystem.material.uniforms.time.value += time/1000;
            
            }
            
			//In Analytic mode, run the equation for the position
            particleSystem.updateAnalytic =function(time)
            {
				particleSystem.material.uniforms.time.value += time/3333.0;
				
                var time_in_ticks = time/33.333;

                var inv = this.matrix.clone();
                inv = inv.getInverse(inv);
                    
                var particles = this.geometry;
				
				//update each particle
                var pCount = this.geometry.vertices.length;
                while(pCount--) 
                {
                    var particle =particles.vertices[pCount];                   
                    this.updateParticleAnalytic(particle,this.matrix,inv,time_in_ticks);
                }
                    
                //examples developed with faster tick - maxrate *33 is scale to make work 
                //with new timing
				//Reuse up to maxRate particles, sliced for delta_time
				//Once a particle reaches it's end of life, its available to be regenerated.
				//We hold extras in limbo with alpha 0 until they can be regenerated
				//Note the maxRate never creates or destroys particles, just manages when they will restart
				//after dying
                var len = Math.min(this.regenParticles.length,this.maxRate*15*time_in_ticks);
                for(var i =0; i < len; i++)
                {
                        
					//setup with new random values, and move randomly forward in time one step	
                    var particle = this.regenParticles.shift();
                    this.setupParticle(particle,this.matrix,inv);
                    this.updateParticleAnalytic(particle,this.matrix,inv,Math.random()*3.33);
                    particle.waitForRegen = false;
                }
                    
					
				//only these things change, other properties are in the shader as they are linear WRT time	
                this.geometry.verticesNeedUpdate  = true;
                this.geometry.colorsNeedUpdate  = true;
                this.material.attributes.vertexColor.needsUpdate = true;
                this.material.attributes.size.needsUpdate = true;
            }
            
            particleSystem.counter = 0;
            particleSystem.testtime = 0;
            particleSystem.totaltime = 0;
            //timesliced Euler integrator
            //todo: switch to RK4 
			//This can do more complex sim, maybe even a cloth sim or such. It ticks 10 times a second, and blends tick with previous via a shader
            particleSystem.updateEuler = function(time)
            {
				particleSystem.material.uniforms.time.value += time/3333.0;
                var time_in_ticks = time/100.0;
                
                if(this.lastTime === undefined) this.lastTime = 0;

                this.lastTime += time_in_ticks;//ticks - Math.floor(ticks);

				var inv = this.matrix.clone();
				inv = inv.getInverse(inv);
					
				var particles = this.geometry;
				
				//timesliced tick give up after 5 steps - just cant go fast enough		
				if(Math.floor(this.lastTime) > 5)
					this.lastTime = 1;
				for(var i=0; i < Math.floor(this.lastTime) ; i++)
				{
					this.lastTime--;
					
					var pCount = this.geometry.vertices.length;
					while(pCount--) 
					{
						var particle =particles.vertices[pCount];					
						this.updateParticleEuler(particle,this.matrix,inv,3.333);
					}
					
					//examples developed with faster tick - maxrate *33 is scale to make work 
					//with new timing
					
					//Reuse up to maxRate particles, sliced for delta_time
					//Once a particle reaches it's end of life, its available to be regenerated.
					//We hold extras in limbo with alpha 0 until they can be regenerated
					//Note the maxRate never creates or destroys particles, just manages when they will restart
					//after dying
					var len = Math.min(this.regenParticles.length,this.maxRate*333);
					for(var i =0; i < len; i++)
					{
						
						particle.waitForRegen = false;
						var particle = this.regenParticles.shift();
						this.setupParticle(particle,this.matrix,inv);
						this.updateParticleEuler(particle,this.matrix,inv,Math.random()*3.33);
						this.material.attributes.lifespan.needsUpdate = true;
					}
					
					//only need to send up the age, position, and previous position. other props handled in the shader
					this.geometry.verticesNeedUpdate  = true;	
					this.material.attributes.previousPosition.needsUpdate = true;
					
					this.material.attributes.age.needsUpdate = true;
					
				}
				
				//even if this is not a sim tick, we need to send the fractional time up to the shader for the interpolation
				this.material.uniforms.fractime.value = this.lastTime;	
				
			}
			
			//Update a particle from the Analytic solver
			particleSystem.updateParticleAnalytic = function(particle,mat,inv,delta_time)
			{
				particle.age += delta_time;
				
				//Make the particle dead. Hide it until it can be reused
				if(particle.age >= particle.lifespan && !particle.waitForRegen)
				{
					this.regenParticles.push(particle);
					particle.waitForRegen = true;
					particle.x = 0;
					particle.y = 0;
					particle.z = 0;
					particle.color.w = 0.0;
				}else
				{
					//Run the formula to get position.
					var percent = particle.age/particle.lifespan;
					particle.world.x = particle.initialx + (particle.velocity.x * particle.age) + 0.5*(particle.acceleration.x * particle.age * particle.age)
					particle.world.y = particle.initialy + (particle.velocity.y * particle.age)  + 0.5*(particle.acceleration.y * particle.age * particle.age)
					particle.world.z = particle.initialz + (particle.velocity.z * particle.age)  + 0.5*(particle.acceleration.z * particle.age * particle.age)
					
					this.temp.x = particle.world.x;
					this.temp.y = particle.world.y;
					this.temp.z = particle.world.z;
					
					//need to specify in object space, event though comptued in local
					this.temp.applyMatrix4( inv );
					particle.x = this.temp.x;
					particle.y = this.temp.y;
					particle.z = this.temp.z;
					
					//Should probably move this to the shader. Linear with time, no point in doing on CPU
					particle.color.x = this.startColor[0] + (this.endColor[0] - this.startColor[0]) * percent;
					particle.color.y = this.startColor[1] + (this.endColor[1] - this.startColor[1]) * percent;
					particle.color.z = this.startColor[2] + (this.endColor[2] - this.startColor[2]) * percent;
					particle.color.w = this.startColor[3] + (this.endColor[3] - this.startColor[3]) * percent;
					
					particle.setSize(this.startSize + (this.endSize - this.startSize) * percent);
				}
			}
			
			//updtae a partilce with the Euler solver
			particleSystem.updateParticleEuler = function(particle,mat,inv,step_dist)
			{
					particle.prevage = particle.age;
					particle.age += step_dist;
					particle.setAge(particle.age + step_dist);
					
					//If the particle is dead ,hide it unitl it can be reused
					if(particle.age >= particle.lifespan && !particle.waitForRegen)
					{
						
						this.regenParticles.push(particle);
						particle.waitForRegen = true;
						particle.x = 0;
						particle.y = 0;
						particle.z = 0;
					    particle.world.x = 0;
						particle.world.y = 0;
						particle.world.z = 0;
						particle.prevworld.x = 0;
						particle.prevworld.y = 0;
						particle.prevworld.z = 0;
						particle.color.w = 1.0;
						particle.size = 100;
					}else
					{
					
					
						// and the position
						particle.prevworld.x = particle.world.x;
						particle.prevworld.y = particle.world.y;
						particle.prevworld.z = particle.world.z;
						
						//find direction to center for gravity
						var gravityAccel = new THREE.Vector3(particle.world.x,particle.world.y,particle.world.z);
						gravityAccel.x -= this.gravityCenter[0];
						gravityAccel.y -= this.gravityCenter[1];
						gravityAccel.z -= this.gravityCenter[2];
						var len = gravityAccel.length()+.1;
						gravityAccel.normalize();
						gravityAccel.multiplyScalar(-Math.min(1/(len*len),100));
						gravityAccel.multiplyScalar(this.gravity);
						
						//update position
						particle.world.x += particle.velocity.x * step_dist + (particle.acceleration.x + gravityAccel.x)* step_dist * step_dist;
						particle.world.y += particle.velocity.y * step_dist + (particle.acceleration.y + gravityAccel.y )* step_dist * step_dist;;
						particle.world.z += particle.velocity.z * step_dist + (particle.acceleration.z + gravityAccel.z )* step_dist * step_dist;;

                      //update velocity
                    particle.velocity.x += (particle.acceleration.x + gravityAccel.x) * step_dist * step_dist;
                    particle.velocity.y += (particle.acceleration.y + gravityAccel.y) * step_dist * step_dist;
                    particle.velocity.z += (particle.acceleration.z + gravityAccel.z) * step_dist * step_dist
                    
                    var damping = 1-(this.damping * step_dist);
					
					//drag
                    particle.velocity.x *= damping;
                    particle.velocity.y *= damping;
                    particle.velocity.z *= damping;
                    
					
					//move from world to local space
					this.temp.x = particle.world.x ;
					this.temp.y = particle.world.y ;
					this.temp.z = particle.world.z;
					this.temp.applyMatrix4( inv );
					particle.x = this.temp.x;
					particle.y = this.temp.y;
					particle.z = this.temp.z;
					//careful to have prev and current pos in same space!!!!
					particle.prevworld.applyMatrix4( inv );
                }
            }
           
            //Change the solver type for the system
            particleSystem.setSolverType =function(type)
            {
				this.solver = type;
                if(type == 'Euler')
                {
                    particleSystem.update = particleSystem.updateEuler;
                    particleSystem.material = particleSystem.shaderMaterial_interpolate;
                    particleSystem.rebuildParticles();
                }
                if(type == 'Analytic')
                {
                    particleSystem.update = particleSystem.updateAnalytic;
                    particleSystem.material = particleSystem.shaderMaterial_default;
                    particleSystem.rebuildParticles();
                }
                if(type == 'AnalyticShader')
                {
                    particleSystem.update = particleSystem.updateAnalyticShader ;       
                    particleSystem.material = particleSystem.shaderMaterial_analytic;
                    particleSystem.rebuildParticles();
                }
                
            }
            
			//If you move a system, all the particles need to be recomputed to look like they stick in world space
			//not that we pointedly dont do this for the AnalyticShader. We could, but that solver is ment to  be very high performance, do we dont
            particleSystem.updateTransform = function(newtransform)
            {
				
				//Get he current transform, and invert new one
				var inv = new THREE.Matrix4();
				var newt = new THREE.Matrix4();
				inv.elements = matCpy(newtransform);
				newt = newt.copy(this.matrix);
				inv = inv.getInverse(inv);
				
				
				//don't adjust for the high performance shader
				if(particleSystem.solver == 'AnalyticShader')
				{
					return;
				}
				
				//Move all particles out of old space to world, then back into new space.
				//this will make it seem like they stay at the correct position in the world, though
				//acutally they change position
				//note that it would actually be more efficient to leave the matrix as identity, and change the position of the 
				//emitters for this...... Could probably handle it in the model setter actually... would be much more efficient, but linking 
				//a system to a moving object would break.
				for(var i =0; i < this.geometry.vertices.length; i++)
				{
						this.geometry.vertices[ i ].applyMatrix4( inv );
						this.shaderMaterial_interpolate.attributes.previousPosition.value[ i ].applyMatrix4( inv );
						this.geometry.vertices[ i ].applyMatrix4( newt );
						this.shaderMaterial_interpolate.attributes.previousPosition.value[ i ].applyMatrix4( newt );
				}
				this.geometry.verticesNeedUpdate  = true;	
				this.shaderMaterial_interpolate.attributes.previousPosition.needsUpdate = true;
						
            }
			//Change the system count. Note that this must be set before the first frame renders, cant be changed at runtime.
            particleSystem.setParticleCount = function(newcount)
            {
                var inv = this.matrix.clone();
                inv = inv.getInverse(inv);
                
                var particles = this.geometry;
                while(this.geometry.vertices.length > newcount) 
                {
                    this.geometry.vertices.pop();
                }
                while(this.geometry.vertices.length < newcount) 
                {
                    var particle = particleSystem.createParticle(this.geometry.vertices.length);
                    particleSystem.setupParticle(particle,particleSystem.matrix,inv);
                    particle.age = Infinity;
                    this.regenParticles.push(particle);
                    particle.waitForRegen = true;
                }
                this.geometry.verticesNeedUpdate  = true;
                this.geometry.colorsNeedUpdate  = true;
                this.shaderMaterial_default.attributes.vertexColor.needsUpdate = true;
                this.particleCount = newcount;
            }
            
            //Setup some defaults
            particleSystem.setParticleCount(1000);
            particleSystem.setSolverType('AnalyticShader');
            particleSystem.update(1);

           
            child.threeObject = particleSystem;
            
        
            child.threeObject.name = childName;
            child.name = childName;
            addThreeChild.call( this, nodeID, childID );
        } 
    
    }
    function addThreeChild( parentID, childID ) {
        
        var threeParent;
        var parent = this.state.nodes[ parentID ];
        if ( !parent && this.state.scenes[ parentID ] ) {
            parent = this.state.scenes[ parentID ];
            threeParent = parent.threeScene;
        } else {
            threeParent = parent.threeObject;
        }
            
        if ( threeParent && this.state.nodes[ childID ]) {
            var child = this.state.nodes[ childID ];

            if ( child.threeObject ) {
                threeParent.add( child.threeObject );
            }
        }
    }

    //search the threeObject of the parent sim node for the threeChild with the name of the sim child node
    function findThreeObjectInParent(childID,parentID)
    {
        var parentThreeObject;
        if(this.state.nodes[parentID])
            parentThreeObject = this.state.nodes[parentID].threeObject;
        if(!parentThreeObject && this.state.scenes[parentID])
            parentThreeObject = this.state.scenes[parentID].threeScene;
        
        //If there is no parent object render node, then there does not need to be a child node
        if(!parentThreeObject) return null; 
        
        var threeChild = findChildThreeObject(parentThreeObject,childID);
        return threeChild;
    }
    function findChildThreeObject(threeParent,childID)
    {
        var ret = null;
        if(threeParent.name == childID)
            ret = threeParent;
        else if(threeParent.children)
        {
            for(var i = 0; i< threeParent.children.length; i++)
                var child = findChildThreeObject(threeParent.children[i],childID);
            if(child)
                ret = child;
        }       
        return ret;
    }

    function sceneLights() {
        var scene = getThreeScene.call( this );
        var lightList = createLightContainer.call( this );
        if ( scene ) {
            lightList = findAllLights( scene, lightList );
        } 
        return lightList;        
    }

    function createDefaultLighting( lights ) {

        var sceneID = this.kernel.application();
        var ambientCount = lights.ambientLights.length;
        var lightCount = lights.spotLights.length + lights.directionalLights.length + lights.pointLights.length;
        
        var scene = getThreeScene.call( this );

        if ( lightCount == 0 ) {
            
            var light1 = new THREE.DirectionalLight( '808080', 2 );
            var light2 = new THREE.DirectionalLight( '808080', 2 );

            light1.distance = light2.distance = 2000;

            scene.add( light1 );
            scene.add( light2 );

            light1.position.set( 0.7, -0.7, 0.3 );
            light2.position.set( -0.7, 0.7, 0.3 );
        }

        if ( ambientCount == 0 ) {
            createAmbientLight.call( this, scene, [ 0.20, 0.20, 0.20 ] );
        }            
    }

    function findVwfChildren( threeObj, children ) {
        if ( threeObj !== undefined ) {
            if ( threeObj.vwfID !== undefined ) {
                children.push( threeObj.vwfID );
            }
            if ( threeObj && threeObj.children ) {
                for ( var i = 0; i < threeObj.children.length; i++ ) {
                    findVwfChildren( threeObj.children[ i ], children );
                }
            }             
        }
    }
 
    function SetVisible( node, state ) {
        if ( node ) {
            node.visible = state;
        }
        if ( node && node.children ) {
            for( var i = 0; i < node.children.length; i++ ) {
                var child = node.children[i];
                if( !child.vwfID ) {
                    SetVisible( child, state );
                }
            }
        }
    }

    function setTransformsDirty( threeObject ) {
        var vwfChildren = [];
        var childNode;
        findVwfChildren( threeObject, vwfChildren );
        for ( var i = 0; i < vwfChildren.length; i++ ) {
            childNode = self.state.nodes[ vwfChildren[ i ] ];
            if ( childNode && childNode.transform !== undefined ) {
                childNode.storedTransformDirty = true;    
            }    
        }
    }

    function getWorldTransform( node ) {
        var parent = self.state.nodes[ node.parentID ];
        if ( parent === undefined ) {
            parent = self.state.scenes[ node.parentID ];
        }
        if ( parent ) {
            var worldTransform = new THREE.Matrix4();
            if ( node.transform === undefined ) {
                node.transform = new THREE.Matrix4();    
            }
            return worldTransform.multiplyMatrices( getWorldTransform( parent ), node.transform );
        } else {
            return node.transform || new THREE.Matrix4();
        }
    }

    function setWorldTransform( node, worldTransform ) {
        if ( node.parent ) {
            var parentInverse = goog.vec.Mat4.create();
            if ( goog.vec.Mat4.invert( getWorldTransform( node.parent ), parentInverse ) ) {
                node.transform = goog.vec.Mat4.multMat( parentInverse, worldTransform, 
                                                        goog.vec.Mat4.create() );
            } else {
                self.logger.errorx( "Parent world transform is not invertible - did not set world transform " +
                                    "on node '" + node.id + "'" );
            }
        } else {
            node.transform = worldTransform;
        }
    }

    function updateStoredTransform( node ) {
        
        if ( node && node.threeObject && ( node.threeObject instanceof THREE.Object3D ) ) {
            // Add a local model-side transform that can stay pure even if the view changes the
            // transform on the threeObject - this already happened in creatingNode for those nodes that
            // didn't need to load a model
            node.transform = new THREE.Matrix4();
            node.transform.elements = matCpy( node.threeObject.matrix.elements );

            // If this threeObject is a camera, it has a 90-degree rotation on it to account for the 
            // different coordinate systems of VWF and three.js.  We need to undo that rotation before 
            // setting the VWF property.
            if ( node.threeObject instanceof THREE.Camera ) {
                
                var transformArray = node.transform.elements;

                // Get column y and z out of the matrix
                var columny = goog.vec.Vec4.create();
                goog.vec.Mat4.getColumn( transformArray, 1, columny );
                var columnz = goog.vec.Vec4.create();
                goog.vec.Mat4.getColumn( transformArray, 2, columnz );

                // Swap the two columns, negating columny
                goog.vec.Mat4.setColumn( transformArray, 1, goog.vec.Vec4.negate( columnz, columnz ) );
                goog.vec.Mat4.setColumn( transformArray, 2, columny );
            } 

            node.storedTransformDirty = false;             
        }
      
    }    

   // -- getBoundingBox ------------------------------------------------------------------------------

    function getBoundingBox( object3 ) {

        var bBox = { 
            min: { x: Number.MAX_VALUE, y: Number.MAX_VALUE, z: Number.MAX_VALUE },
            max: { x: -Number.MAX_VALUE, y: -Number.MAX_VALUE, z: -Number.MAX_VALUE }
        };

        if (object3 instanceof THREE.Object3D)
        {
            object3.traverse (function (mesh)
            {
                if (mesh instanceof THREE.Mesh)
                {
                    mesh.geometry.computeBoundingBox ();
                    var meshBoundingBox = mesh.geometry.boundingBox;

                    // compute overall bbox
                    bBox.min.x = Math.min (bBox.min.x, meshBoundingBox.min.x);
                    bBox.min.y = Math.min (bBox.min.y, meshBoundingBox.min.y);
                    bBox.min.z = Math.min (bBox.min.z, meshBoundingBox.min.z);
                    bBox.max.x = Math.max (bBox.max.x, meshBoundingBox.max.x);
                    bBox.max.y = Math.max (bBox.max.y, meshBoundingBox.max.y);
                    bBox.max.z = Math.max (bBox.max.z, meshBoundingBox.max.z);
                }
            });
        }
        else if ( object3 && object3.geometry && object3.geometry.computeBoundingBox ) {
            object3.geometry.computeBoundingBox();
            var bx = object3.geometry.boundingBox;
            bBox = { 
                min: { x: bx.min.x, y: bx.min.y, z: bx.min.z },
                max: { x: bx.max.x, y: bx.max.y, z: bx.max.z }
            };
        }

        return bBox;
    }

    function getCenterOffset( object3 ) {
        var offset = [ 0, 0, 0 ];
        if ( object3 ) {
            var bBox = getBoundingBox.call( this, object3 );
            offset[0] = ( bBox.max.x + bBox.min.x ) * 0.50;
            offset[1] = ( bBox.max.y + bBox.min.y ) * 0.50;
            offset[2] = ( bBox.max.z + bBox.min.z ) * 0.50;
        }
        return offset;
    }

   

    ////////////////////////////////////////////////////////////////////////////////////////////////////////
    //UTF8 loader
    

    function DecodeARRAY_BUFFER(str,range,inmin,stride,bits)
    {
     str = blobarray[str];
      var attribs_out = [];//new Float32Array(str.length);
      //min = min + 0.0;
      var prev = [0,0,0];
      var divisor = Math.pow(2,bits);
      for (var i = 5; i < str.length-5; i+=stride) {

          for(var j = 0; j< stride; j++)
            {     
                  var code = str.charCodeAt(i+j);
                  var dezigzag = (Number(code) >> 1) ^ (-(Number(code) & 1));
                  prev[j] += dezigzag;
                  var prev_attrib = ((prev[j]/divisor)*(range))  + Number(inmin) ;//(code >> 1) ^ (-(code & 1));
                  attribs_out.push(prev_attrib);
            }     
      }
     
      return attribs_out;
    }
    var debugarraytype = "";
    function DecodeELEMENT_ARRAY_BUFFER(str,range)
    {
     
      str = blobarray[str];
      var attribs_out = [];//new Uint16Array(str.length);
      var prev = 0;
      for (var i = 5; i < str.length-5; i++) {
     
          var code = str.charCodeAt(i);
          var dezigzag = (code >> 1) ^ (-(code & 1));;
          prev += dezigzag;
         // alert("char code " +code + " dezigzag " + dezigzag + " new value " + prev);
          attribs_out.push(prev);
          
      }
     
      return attribs_out;
    }

    function DecodeArray(array,key)
    {
        var type = array.type;
        var array2 =[];
        var itemsize = array.itemSize;
        
        if(type == "ELEMENT_ARRAY_BUFFER")
            array2 = DecodeELEMENT_ARRAY_BUFFER(array.elements.values,array.elements.range);
        if(type == "ARRAY_BUFFER")
            array2 = DecodeARRAY_BUFFER(array.elements.values,array.elements.range,array.elements.min,itemsize,array.elements.bits);    
        
        return array2;
    }

    function UTF8JsonLoader(node,callback)
    {
        this.url = node.source;
        this.callback = callback;
        this.children=[];
        
        this.jsonLoaded = function(e)
        {
            var test = 1+1;
            var jsonData = JSON.parse(decompress(e));
            var texture_load_callback = function(texturename)
            {
                
                var src = "";
                if(this.url.toLowerCase().indexOf('3dr_federation') != -1)
                    src = this.url.substr(0,this.url.indexOf("Model/")) + "textures/NoRedirect/" + encodeURIComponent(texturename) +"?ID=00-00-00";
                else
                    src = this.url.substr(0,this.url.indexOf("Model/")) + "textures/" + encodeURIComponent(texturename) +"?ID=00-00-00";
                console.log(src);
                src = src.replace("AnonymousUser:@","");
                
                var tex = loadTexture( undefined, src );
                
                return tex;
            }
            this.scene = ParseSceneGraph(jsonData,texture_load_callback.bind(this));
            if(this.callback)
                this.callback(this);
        }.bind(this);
        
        this.error = function(e)
        {
            alert(e.responseText);
        }.bind(this);
        
        $.ajax({
            url: this.url,
            data: {},
            success: this.jsonLoaded,
            error: this.error,
            dataType:'text'
        });
        ;
    }

    

    function BuildUTF8JsonNode(node,callback)
    {
        return new UTF8JsonLoader(node,callback);
    }
    function toColor(arr)
        {
            var color = new THREE.Color();
            color.setRGB(arr[0],arr[1],arr[2],arr[3]);
            return color;
        }
    function ApplyMaterial(newnode,newmaterial)
    {
        if(newnode instanceof THREE.Mesh)
            newnode.material = newmaterial;
        else if( newnode.children)
        {
            for(var i =0; i < newnode.children.length;i++)
                ApplyMaterial(newnode.children[0],newmaterial);
        }   
    }
    function isIdentityMatrix( elements ) {
        if ( ( elements.length == 16 ) || ( elements.length == 9 ) ) {
          var modNumber = Math.sqrt( elements.length ) + 1;
          for ( var index = 0; index < elements.length; index++ ) {
              if ( ( index % modNumber ) == 0 ) {
                  if ( elements[ index ] != 1 ) {
                      return false;
                  }
              }
              else {
                  if ( elements[ index ] != 0 ) {
                      return false;
                  }
              }
          }
          return true;
        }
        return false;
    }
    function ParseSceneGraph(node, texture_load_callback) {
        
        var newnode;
        //its geometry
        if (node.primitives) {
            
            //newnode = new THREE.Object3D();
            var geo = new THREE.Geometry();
            var mesh = newnode = new THREE.Mesh(geo);
            mesh.geometry.normals = [];
            mesh.geometry.UVS = [];
            
            
            
            //vertex data
            if (node.attributes) {
                $.each(node.attributes, function(key, element) {
                    debugarraytype = key;
                    var attributeArray = node.attributes[key];
                    node.attributes[key] = DecodeArray(attributeArray,key);
                    if(key == "Vertex")
                    {
                        for(var i = 0; i < node.attributes[key].length-2; i+= 3)
                        {
                            var vert = new THREE.Vector3( node.attributes[ key ][ i ],
                                                          node.attributes[ key ][ i + 1 ],
                                                          node.attributes[ key ][ i + 2 ] );
                            mesh.geometry.vertices.push(vert);
                        }
                    }
                    if(key == "Normal")
                    {
                        for(var i = 0; i < node.attributes[key].length-2; i+= 3)
                        {
                            var norm = new THREE.Vector3( node.attributes[ key ][ i ],
                                                          node.attributes[ key ][ i + 1 ],
                                                          node.attributes[ key ][ i + 2 ] );
                            mesh.geometry.normals.push(norm);
                        }
                    }
                    if(key == "TexCoord0")
                    {
                        for(var i = 0; i < node.attributes[key].length-1; i+= 2)
                        {
                            var uv = new THREE.Vector2( node.attributes[ key ][ i ], 
                                                        node.attributes[ key ][ i + 1 ] );
                            mesh.geometry.UVS.push(uv);
                        }
                    }
                    
                    if(key == "VertexColor")
                    {
                        for(var i = 0; i < node.attributes[key].length-3; i+= 4)
                        {
                            var vert = new THREE.Vector3( node.attributes[ key ][ i ],
                                                          node.attributes[ key ][ i + 1 ],
                                                          node.attributes[ key ][ i + 2 ] );
                            mesh.geometry.colors.push(vert);
                            
                        }
                    }               
                });
            }
            
            var i;
            for (i in node.primitives) {
                
                if (node.primitives[i].indices) {
                    var array = node.primitives[i].indices;
                    array = DecodeArray(array);
                    
                    for(var j = 0; j < array.length-2; j+= 3)
                    {
                        var face = new THREE.Face3(array[j],array[j+1],array[j+2],new THREE.Vector3(0,1,0),new THREE.Color('#000000'),0);
                        face.vertexNormals.push(mesh.geometry.normals[face.a]);
                        face.vertexNormals.push(mesh.geometry.normals[face.b]);
                        face.vertexNormals.push(mesh.geometry.normals[face.c]);
                        mesh.geometry.faces.push(face);
                        mesh.geometry.faceVertexUvs[0].push([mesh.geometry.UVS[face.a],mesh.geometry.UVS[face.b],mesh.geometry.UVS[face.c]]);

                    }
                } else {
                    mode = gl[mode];
                    var first = node.primitives[i].first;
                    var count = node.primitives[i].count;
                    if (count > 65535)
                        count = 32740;
                    //node.primitives[i] = new osg.DrawArrays(mode, first, count);
                }
            }
            
            
            mesh.geometry.verticesNeedUpdate  = true;
            mesh.geometry.facesNeedUpdate  = true;
            }
            var newmaterial = null;
            if (node.stateset) {
                newmaterial = new THREE.MeshPhongMaterial();
                if (node.stateset.textures) {
                    var textures = node.stateset.textures;
                    for ( var t = 0, tl = textures.length; t < tl; t++) {
                        if (textures[t] === undefined) {
                            continue;
                        }
                        if (!textures[t].file) {
                            if (console !== undefined) {
                            console.log("no 'file' field for texture "
                                + textures[t]);
                            }
                        }

                        var tex;
                        if ( texture_load_callback ) {
                            tex = texture_load_callback( textures[t].file );
                        } else {
                            tex = loadTexture( newmaterial, textures[t].file );
                        }
                        if (tex) {
                            tex.wrapS = THREE.RepeatWrapping;
                            tex.wrapT = THREE.RepeatWrapping;
                            newmaterial.map = tex;
                            newmaterial.needsUpdate = true;
                        }
                    }
                }
                if (node.stateset.material) {
                    newmaterial.ambient = (toColor(node.stateset.material.ambient));
                    newmaterial.color = (toColor(node.stateset.material.diffuse));
                    
                    newmaterial.shininess = (node.stateset.material.shininess);
                    newmaterial.specular = (toColor(node.stateset.material.specular));
                    newmaterial.needsUpdate = true;
                }
                
            }
            
            
            
        if (node.matrix) {
        
            if(newnode == null)
                newnode = new THREE.Object3D();
            var matrix = [];
            for(var i =0; i < node.matrix.length; i++)
                matrix.push(node.matrix[i]);
            var glmat = new THREE.Matrix4();
            glmat.elements = matrix;
            
            var flipmat = new THREE.Matrix4(1, 0,0,0,
                                            0, 0,1,0,
                                            0,-1,0,0,
                                            0, 0,0,1);
                            
                                            
            glmat = glmat.multiplyMatrices(flipmat,glmat);
            
            //glmat = glmat.transpose();
            newnode.matrix.copy(glmat)  
            newnode.matrixAutoUpdate = false;
        }
        
        if (node.children) {
            if(newnode == null)
                newnode = new THREE.Object3D();
            for ( var child = 0; child < node.children.length; child++) {
                var childnode = ParseSceneGraph(node.children[child],texture_load_callback);
                if(childnode)
                    newnode.add(childnode);
            }
        }
        
        if(newnode && newmaterial)
            ApplyMaterial(newnode,newmaterial);
        
        if(node.name && newnode)
            newnode.name = node.name;
            
		if(newnode && newnode.children && newnode.children.length == 1 && isIdentityMatrix(newnode.matrix.elements))
		return newnode.children[0];
		
        return newnode;
    }
    var blobsfound = 0;
    var blobarray = [];

    function DecompressStrings(data, replace, find)
    {
        var reg = new RegExp(find,'g');
        return data.replace(reg, replace);
    }

    function decompressJsonStrings(data)
    {
    data = DecompressStrings(data,"\"min\":","min:");
    data = DecompressStrings(data,"\"max\":","max:");
    data = DecompressStrings(data,"\"stateset\":","ss:");
    data = DecompressStrings(data,"\"LINE_LOOP\"","\"LL\"");
    data = DecompressStrings(data,"\"LINEAR\"","\"L\"");
    data = DecompressStrings(data,"\"LINEAR_MIPMAP_LINEAR\"","\"LML\"");
    data = DecompressStrings(data,"\"LINEAR_MIPMAP_NEAREST\"","\"LMN\"");
    data = DecompressStrings(data,"\"NEAREST\"","\"NE\"");
    data = DecompressStrings(data,"\"NEAREST_MIPMAP_LINEAR\"","\"NML\"");
    data = DecompressStrings(data,"\"NEAREST_MIPMAP_NEAREST\"","\"NMN\"");
    data = DecompressStrings(data,"\"mag_filter\":","maf:");
    data = DecompressStrings(data,"\"min_filter\":","mif:");
    data = DecompressStrings(data,"\"file\":","f:");
    data = DecompressStrings(data,"\"name\":","n:");
    data = DecompressStrings(data,"\"ambient\":","a:");
    data = DecompressStrings(data,"\"diffuse\":","d:");
    data = DecompressStrings(data,"\"specular\":","s:");
    data = DecompressStrings(data,"\"emission\":","e:");
    data = DecompressStrings(data,"\"shininess\":","sh:");
    data = DecompressStrings(data,"\"textures\":","t:");
    data = DecompressStrings(data,"\"material\":","m:");
    data = DecompressStrings(data,"\"POINTS\"","\"P\"");
    data = DecompressStrings(data,"\"LINES\"","\"LI\"");

    data = DecompressStrings(data,"\"LINE_STRIP\"","\"LS\"");
    data = DecompressStrings(data,"\"TRIANGLES\"","\"T\"");
    data = DecompressStrings(data,"\"TRIANGLE_FAN\"","\"TF\"");
    data = DecompressStrings(data,"\"TRIANGLE_STRIP\"","\"TS\"");
    data = DecompressStrings(data,"\"first\":","fi:");
    data = DecompressStrings(data,"\"count\":","co:");
    data = DecompressStrings(data,"\"mode\":","mo:");
    data = DecompressStrings(data,"\"undefined\":","u:");
    data = DecompressStrings(data,"\"children\":","c:");
    data = DecompressStrings(data,"\"range\":","r:");
    data = DecompressStrings(data,"\"bits\":","b:");
    data = DecompressStrings(data,"\"values\":","v:");
    data = DecompressStrings(data,"\"elements\":","el:");
    data = DecompressStrings(data,"\"itemSize\":","iS:");
    data = DecompressStrings(data,"\"type\":","ty:");
    data = DecompressStrings(data,"\"ARRAY_BUFFER\"","\"AB\"");
    data = DecompressStrings(data,"\"ELEMENT_ARRAY_BUFFER\"","\"EAB\"");
    data = DecompressStrings(data,"\"indices\":","i:");
    data = DecompressStrings(data,"\"Vertex\":","V:");
    data = DecompressStrings(data,"\"Normal\":","N:");
    data = DecompressStrings(data,"\"TexCoord0\":","T0:");
    data = DecompressStrings(data,"\"TexCoord1\":","T1:");
    data = DecompressStrings(data,"\"TexCoord2\":","T2:");
    data = DecompressStrings(data,"\"TexCoord3\":","T3:");
    data = DecompressStrings(data,"\"TexCoord4\":","T4:");
    data = DecompressStrings(data,"\"attributes\":","A:");
    data = DecompressStrings(data,"\"primitives\":","p:");
    data = DecompressStrings(data,"\"projection\":","pr:");
    data = DecompressStrings(data,"\"matrix\":","M:");

    return data;
    }
    
    function isUUIDinArray( value, arrayToCheck ) {
        for ( var index = 0; index < arrayToCheck.length; index++ ) {
            if ( value.uuid == arrayToCheck[ index ].uuid ) {
                return true;
            }
        }
        return false;
    }
    function threeMaterialsFromIDs( nodeIDs ) {
        var result = [];
        for ( var index = 0; index < nodeIDs.length; index++ ) {
            var node = this.state.nodes[ nodeIDs[ index ] ];
            if ( node && ( node.threeObject instanceof THREE.Material ) ) {
                result.push( node.threeObject );
            }
        }
        return result;
    }
    function createInheritedMaterial( parentID, threeObject, name ) {
        var nodeName = "material";
        if ( name ) {
            nodeName = name;
        }
        else if ( threeObject.name.length > 0 ) {
            nodeName = threeObject.name;
        }
        var newNode = { 
            "id": nodeName,
            "uri": nodeName,
            "extends": "http://vwf.example.com/material.vwf",
            "properties": { 
                "private": null,
            },
            "methods": {
            },
            "scripts": []
        };
        vwf.createChild( parentID, nodeName, newNode);
        
    }
    function generateNodeMaterial( nodeID, node ) {
        if ( false ) {
            if ( node.threeObject instanceof THREE.Object3D ) {
                var representedMaterialsVWF = vwf.find( nodeID, "./element(*,'http://vwf.example.com/material.vwf')" );
                var representedMaterialsThreeJS = threeMaterialsFromIDs.call( this, representedMaterialsVWF );
                var allChildrenMaterials = GetAllMaterials( node.threeObject );

                var nameTallys = {};

                for ( var index = 0; index < allChildrenMaterials.length; index ++ ) {
                    if ( nameTallys[ allChildrenMaterials[ index ].name ] ) {
                        nameTallys[ allChildrenMaterials[ index ].name ] = nameTallys[ allChildrenMaterials[ index ].name ] + 1;
                    }
                    else {
                        nameTallys[ allChildrenMaterials[ index ].name ] = 1;
                    }
                    if ( ! isUUIDinArray( allChildrenMaterials[ index ], representedMaterialsThreeJS ) ) {
                        var newName = "material_" + nameTallys[ allChildrenMaterials[ index ].name ] + "_" + allChildrenMaterials[ index ].name;
                        createInheritedMaterial.call( this, nodeID, allChildrenMaterials[ index ], newName );
                    }
                }                
            }
        }

    }
    function setUniformProperty( obj, prop, type, value ) {
        
        //console.info( "setUniformProperty( obj, "+prop+", "+type+", "+value+" )" );

        switch ( type ) {
            case 'i':
                obj[ prop ].value = Number( value );
                break
            case 'f':
                obj[ prop ].value = parseFloat( value );
                break;
            case 'c':
                obj[ prop ].value = new THREE.Color( value );
                break;
            case 'v2':
                obj[ prop ].value = new THREE.Vector2( value[0], value[1] );
                break;
            case 'v3':
                obj[ prop ].value = new THREE.Vector3( value[0], value[1], value[2] );
                break;
            case 'v4':
                obj[ prop ].value = new THREE.Vector4( value[0], value[1], value[2], value[3] );
                break;
            case 't':
                obj[ prop ].src = value;
                obj[ prop ].value = loadTexture( undefined, value );
                break;
        } 
    
    }
    function decompress(dataencoded)
    {
        blobsfound = 0;
        blobarray = [];

        var regex = new RegExp('\u7FFF\u7FFE\u7FFF\u7FFE\u7FFF[\\S\\s]*?\u7FFE\u7FFF\u7FFE\u7FFF\u7FFE','igm');
        blobarray = dataencoded.match(regex);
        var data = dataencoded.replace(regex,function(match) { return "\""+(blobsfound++)+"\"";});
        data = decompressJsonStrings(data);
        return data;
    }

    function loadTexture( mat, def ) {
        var txt = undefined;
        var url = undefined;
        var mapping = undefined;
        var onLoad = function( texture ) {
            if ( mat ) {
                mat.map = texture;
                mat.needsUpdate = true;
            }
        };

        function onError() {
            self.logger.warnx(  )
        }

        //console.log( [ "loadTexture: ", JSON.stringify( def ) ] );

        if ( utility.isString( def ) ) {
            url = def;    
        } else {
            url = def.url;
            mapping = def.mapping;
        }

        if ( mat === undefined ) {
            if ( mapping === undefined ) {
                txt = THREE.ImageUtils.loadTexture( url );    
            } else {
                txt = THREE.ImageUtils.loadTexture( url, mapping );
            }
        } else {
            txt = THREE.ImageUtils.loadTexture( url, mapping, onLoad, onError );            
        }

        return txt;
    }

    function createMaterial( matDef ) {
        var mat, text;

        //console.log( [ "createMaterial: ", JSON.stringify( matDef ) ] );

        if ( matDef.texture !== undefined ) {
            text = loadTexture( undefined, matDef.texture ); 
            if ( !utility.isString( matDef.texture ) ) {
                for ( var prop in matDef.texture ) {
                    if ( prop !== 'url' && prop !== 'mapping' ) {
                        setTextureProperty( text, prop, matDef.texture[ prop ] );
                    }
                } 
            }
        }

        if ( matDef.type !== undefined ) {

            var matParameters = {};

            for ( var prop in matDef ) {
                switch ( prop ) {
                    
                    case "type":
                    case "texture":
                        break;

                    default:
                        matParameters[ prop ] = matDef[ prop ];
                        break;

                }
            }

            if ( text ) {
                matParameters.map = text;                 
            }

            switch ( matDef.type ) {

                case "MeshBasicMaterial":
                    mat = new THREE.MeshBasicMaterial( matParameters );
                    break;

                case "MeshLambertMaterial":
                    mat = new THREE.MeshLambertMaterial( matParameters );
                    break;

                case "MeshPhongMaterial":
                    mat = new THREE.MeshPhongMaterial( matParameters );
                    break;

                case "MeshNormalMaterial":
                    mat = new THREE.MeshNormalMaterial( matParameters );
                    break;

                case "MeshDepthMaterial":
                    mat = new THREE.MeshDepthMaterial( matParameters );
                    break;

                case "ShaderMaterial":
                    mat = createShader( matParameters );
                    break;

                case "SpriteMaterial":
                    mat = new THREE.SpriteMaterial( matParameters );
                    break;

                case "LineBasicMaterial":
                    mat = new THREE.LineBasicMaterial( matParameters );
                    break;

                case "LineDashedMaterial":
                    mat = new THREE.LineDashedMaterial( matParameters );
                    break;

                case "MeshFaceMaterial":
                    mat = new THREE.MeshFaceMaterial( matParameters );
                    break;

                case "PointCloudMaterial":
                    mat = new THREE.PointCloudMaterial( matParameters );
                    break;

                case "RawShaderMaterial":
                    mat = new THREE.RawShaderMaterial( matParameters );
                    break;

            }

            //if ( mat ) {
            //    console.info( "Material created: " + matDef.type );
            //}
        } else {
            mat = new THREE.MeshBasicMaterial( matDef );
        }
        return mat;
    }

    function setMaterialProperty( material, propertyName, propertyValue ) {
        
        var value = propertyValue;

        if ( material === undefined ) {
            return undefined;
        }

        //console.log( [ "setMaterialProperty: ", propertyName, propertyValue ] );

        switch ( propertyName ) { 

            case "texture":
                if ( propertyValue !== "" && utility.validObject( propertyValue ) ) {
                    loadTexture( material, propertyValue );
                } else {
                    material.map = null;
                    material.needsUpdate;
                }
                break;

            case "color":
            case "diffuse":
                var vwfColor = new utility.color( propertyValue );
                if ( vwfColor ) {
                    material.color.setRGB( vwfColor.red()/255, vwfColor.green()/255, vwfColor.blue()/255 );
                }
                if ( material.ambient !== undefined ) {
                    material.ambient.setRGB( material.color.r, material.color.g, material.color.b ); 
                }
                value = vwfColor.toString();
                break; 

            case "specColor":
                var vwfColor = new utility.color( propertyValue );
                if ( vwfColor ) {
                  material.specular.setRGB( vwfColor.red() / 255, vwfColor.green() / 255, vwfColor.blue() / 255 );
                  value = vwfColor.toString();
                }  
                break; 

            case "reflect":                            
                value = Number( propertyValue );
                material.reflectivity = value;
                break;

            case "shininess":
                value = Number( propertyValue );
                material.shininess = value;
                break;

            case "bumpScale":
                value = Number( propertyValue );
                material.bumpScale = value;
                break;

            case "alphaTest":
                value = Number( propertyValue );
                material.alphaTest = value;
                break;

            case "ambient":
                var vwfColor = new utility.color( propertyValue );
                if ( vwfColor ) {
                  material.ambient.setRGB( vwfColor.red( ) / 255, vwfColor.green( ) / 255, vwfColor.blue( ) / 255 );
                  value = vwfColor.toString();
                }
                break;

            case "emit":
                var vwfColor = new utility.color( propertyValue );
                if ( vwfColor ) {
                  material.emissive.setRGB( vwfColor.red( ) / 255, vwfColor.green( ) / 255, vwfColor.blue( ) / 255 );
                  value = vwfColor.toString();
                }
                break;

            case "transparent":
                value = Boolean( propertyValue );
                material.transparent = value;
                break;

            case "opacity":
                value = Number( propertyValue );
                material.opacity = value;
                break;

            case "side":
                switch ( propertyValue ) {

                    case 2:
                    case "2":
                    case "double":
                        material.side = THREE.DoubleSide;
                        break;

                    case 0:
                    case "0":                        
                    case "front":
                        material.side = THREE.FrontSide;
                        break;

                    case 1:
                    case "1":                        
                    case "back":
                        material.side = THREE.BackSide;
                        break;
                    
                    default:
                        value = undefined;
                        break;
                }
                break;

            default:
                value = undefined;
                break;

        }       

        if ( value !== undefined ) {
            material.needsUpdate = true;    
        }

        return value;
    }

    function setTextureProperty( texture, propertyName, propertyValue ) {
        var value = propertyValue;

        if ( texture === undefined ) {
            return undefined;
        }

        //console.log( [ "setTextureProperty: ", propertyName, propertyValue ] );

        switch ( propertyName ) {
            
            case "wrapT":
                switch ( propertyValue ) {

                    case 1001:
                    case "1001":
                    case "clamp":
                        texture.wrapT = THREE.ClampToEdgeWrapping;
                        break;

                    case 1000:
                    case "1000":
                    case "repeat":
                        texture.wrapT = THREE.RepeatWrapping;
                        break;

                    case 1002:
                    case "1002":
                    case "mirror":
                        texture.wrapT = THREE.MirroredRepeatWrapping;
                        break;

                    default:
                        value = undefined;
                        break;
                }
                break;
            
            case "wrapS":
                switch ( propertyValue ) {
                    
                    case 1001:
                    case "1001":
                    case "clamp":
                        texture.wrapS = THREE.ClampToEdgeWrapping;
                        break;

                    case 1000:
                    case "1000":                        
                    case "repeat":
                        texture.wrapS = THREE.RepeatWrapping;
                        break;
                    
                    case 1002:
                    case "1002":
                    case "mirror":
                        texture.wrapS = THREE.MirroredRepeatWrapping;
                        break;
                    
                    default:
                        value = undefined;
                        break;
                }
                break;
            
            case "repeat":
                if ( propertyValue instanceof Array && propertyValue.length > 1 ) {
                    texture.repeat = new THREE.Vector2( propertyValue[0], propertyValue[1] );
                } else {
                    value = undefined;
                }
                break;
            
            case "offset":
                if ( propertyValue instanceof Array && propertyValue.length > 1 ) {
                    texture.offset = new THREE.Vector2( propertyValue[0], propertyValue[1] );
                } else {
                    value = undefined;
                }
                break
            
            case "magFilter":
                switch ( propertyValue ) {

                    case 1003:
                    case "1003":
                    case "nearest":
                        texture.magFilter = THREE.NearestFilter;
                        break;

                    case 1004:
                    case "1004":
                    case "nearestNearest":
                        texture.magFilter = THREE.NearestMipMapNearestFilter;
                        break;

                    case 1005:
                    case "1005":
                    case "nearestLinear":
                        texture.magFilter = THREE.NearestMipMapLinearFilter;
                        break;

                    case 1006:
                    case "1006":
                    case "linear":
                        texture.magFilter = THREE.LinearFilter;
                        break;

                    case 1007:
                    case "1007":
                    case "linearNearest":
                        texture.magFilter = THREE.LinearMipMapNearestFilter;
                        break;

                    case 1008:
                    case "1008":
                    case "linearLinear":
                        texture.magFilter = THREE.LinearMipMapLinearFilter;
                        break;

                    default:
                        value = undefined;
                        break;
                }
                break;

            
            case "minFilter":
                switch ( propertyValue ) {

                    case 1003:
                    case "1003":
                    case "nearest":
                        texture.minFilter = THREE.NearestFilter;
                        break;

                    case 1004:
                    case "1004":
                    case "nearestNearest":
                        texture.minFilter = THREE.NearestMipMapNearestFilter;
                        break;

                    case 1005:
                    case "1005":
                    case "nearestLinear":
                        texture.minFilter = THREE.NearestMipMapLinearFilter;
                        break;

                    case 1006:
                    case "1006":
                    case "linear":
                        texture.minFilter = THREE.LinearFilter;
                        break;

                    case 1007:
                    case "1007":
                    case "linearNearest":
                        texture.minFilter = THREE.LinearMipMapNearestFilter;
                        break;

                    case 1008:
                    case "1008":
                    case "linearLinear":
                        texture.minFilter = THREE.LinearMipMapLinearFilter;
                        break;

                    default:
                        value = undefined;
                        break;
                }
                break;
            
            case "anisotropy":
                texture.anisotropy = parseFloat( prop );
                break;

            default:
                value = undefined;
                break;

        }

        if ( value !== undefined ) {
            texture.needsUpdate = true;
        }

        return value;

    }

});