"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.

/// vwf/model/glge.js is an interface to the GLGE WebGL scene manager.
/// 
/// @module vwf/model/glge
/// @requires vwf/model
/// @requires vwf/utility

define( [ "module", "vwf/model", "vwf/utility" ], function( module, model, utility ) {

    // For historical reasons yet to be resolved, the GLGE model code currently resides in
    // vwf-model.glge.js intermixed with the view code. This driver is a gross hack to delegate model
    // calls to the appropriate parts of the GLGE view.

    return model.load( module, {

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

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

        initialize: function() {
            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;

        },


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

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

            var childURI = nodeID === 0 ? childIndex : undefined;

            var self = this;

            //console.log(["creatingNode:",nodeID,childID,childName,childExtendsID,childType]);
            var prototypeID = isPrototype.call( this, nodeID, childID );
            if ( prototypeID !== undefined ) {
                //console.info( "FOUND prototype: " + prototypeID );
                this.state.prototypes[ prototypeID ] = {
                    parentID: nodeID,
                    ID: childID,
                    extendsID: childExtendsID,
                    implementsID: childImplementsIDs,
                    source: childSource, 
                    type: childType,
                    uri: childURI,
                    name: childName
                };
                return;                
            }
            if ( childExtendsID === undefined )
                return;

            //console.log("Create " + childID);

            var node, parentNode, glgeChild, glgeParent;
            var kernel = this.kernel;
            var prototypes = getPrototypes.call( this, kernel, childExtendsID );

//            this.logger.enabled = true;
//            this.logger.infox( "creatingNode", nodeID, childID, childExtendsID, childImplementsIDs,
//                                childSource, childType, childIndex, childName );
//            this.logger.enabled = false;

            // find the parent node
            if ( nodeID ) {
                if ( this.state.nodes[ nodeID ] )
                    parentNode = this.state.nodes[ nodeID ];
                else 
                    parentNode = this.state.scenes[ nodeID ];

                if ( parentNode ) {
                    glgeParent = parentNode.glgeObject ? parentNode.glgeObject : parentNode.glgeScene;
                    var recursive = false;
                    if ( glgeParent && childName ) {
                        recursive = ( glgeParent.constructor == GLGE.Collada );
                        glgeChild = glgeObjectChild.call( this, glgeParent, childName, childExtendsID, prototypes, recursive );
                    }
                }
            }

            if ( prototypes && isGlgeSceneDefinition.call( this, prototypes ) && childID == this.kernel.application() ) {

                this.state.sceneRootID = childID;

                var sceneNode = this.state.scenes[childID] = {
                    glgeDocument: new GLGE.Document(),
                    glgeRenderer: undefined,
                    glgeScene: new GLGE.Scene(),
                    ID: childID,
                    parentID: nodeID,
                    glgeKeys: new GLGE.KeyInput(),
                    type: childExtendsID,
                    camera: {
                        ID: undefined
                    },
                    xmlColladaObjects: [],
                    srcColladaObjects: [],
                    viewInited: false,
                    modelInited: false,
                    pendingLoads: 0,
                };
				
                if ( sceneNode.glgeScene.camera ) {
                    sceneNode.glgeScene.camera.name = "camera";
                    this.state.cameraInUse = sceneNode.glgeScene.camera;
                    initCamera.call( this, sceneNode.glgeScene.camera );
                }   

                var model = this;
                var xmlDocLoadedCallback = callback;
                sceneNode.glgeDocument.onLoad = function () {
                    sceneNode.pendingLoads--;
                    xmlDocLoadedCallback( true );
                };

                if ( childSource ) {
                    switch ( childType ) {
                        case "model/x-glge":
                            callback( false );
                            sceneNode.glgeDocument.load( utility.resolveURI( childSource, childURI ) );
                            sceneNode.pendingLoads++;
                            break;
                    }
                }
           
            } else if ( prototypes && isGlgeCameraDefinition.call( this, prototypes ) ) {
                if ( childName !== undefined ) {
                    var camName = this.kernel.name( childID );
                    var sceneNode = this.state.scenes[ this.state.sceneRootID ];
                    node = this.state.nodes[childID] = {
                        name: childName,
                        glgeObject: glgeChild,
                        ID: childID,
                        parentID: nodeID,
                        sceneID: this.state.sceneRootID,
                        glgeScene: sceneNode ? sceneNode.glgeScene : undefined,
                        type: childExtendsID,
                        sourceType: childType,
                    };

                    if ( nodeID === this.kernel.application() && childName === "camera" ) {

                    } else if ( node.glgeObject === undefined ){
                        createCamera.call( this, nodeID, childID, childName );
                    }
                }              
            } else if ( prototypes && isGlgeLightDefinition.call( this, prototypes ) ) {
                if ( childName !== undefined ) {
                    node = this.state.nodes[childID] = {
                        name: childName,
                        glgeObject: glgeChild,
                        ID: childID,
                        parentID: nodeID,
                        type: childExtendsID,
                        sourceType: childType,
                    };
                    if ( nodeID != 0 && !node.glgeObject ) {
                        createLight.call( this, nodeID, childID, childName );
                    }
                }
            } else if ( prototypes && isGlgeParticleSystemDefinition.call( this, prototypes ) ) {
                if ( childName !== undefined ) {
                    node = this.state.nodes[childID] = {
                        name: childName,
                        glgeObject: glgeChild,
                        ID: childID,
                        parentID: nodeID,
                        type: childExtendsID
                    };
                    if ( nodeID != 0 && !node.glgeObject ) {
                        createParticleSystem.call( this, nodeID, childID, childName );
                    }
                }
            } else if ( prototypes && isGlgeNodeDefinition.call( this, prototypes ) ) {

                if ( childName !== undefined ) {
                    var sceneNode = this.state.scenes[ this.state.sceneRootID ];
                    switch ( childType ) {
                        case "model/vnd.collada+xml":
                            callback( false );
                            node = this.state.nodes[childID] = {
                                name: childName,  
                                glgeObject: undefined,
                                source: utility.resolveURI( childSource, childURI ),
                                ID: childID,
                                parentID: nodeID,
                                sourceType: childType,
                                type: childExtendsID,
                                loadedCallback: callback,
                                glgeScene: sceneNode
                            };
                            loadCollada.call( this, parentNode, node, notifyDriverOfPrototypeAndBehaviorProps ); 
                            break;

                        case "text/xml":
                            node = this.state.nodes[childID] = {
                                name: childName,  
                                glgeObject: undefined,
                                source: childSource,
                                ID: childID,                                
                                parentID: nodeID,
                                type: childExtendsID,
                                sourceType: childType,
                                glgeScene: sceneNode 
                            };
                                
                            if ( sceneNode && sceneNode.glgeDocument ){
                                var meshDef = sceneNode.glgeDocument.getElement( node.source );
                                if ( meshDef ) {
                                    node.glgeObject = new GLGE.Object();
                                    node.glgeObject.setMesh( meshDef );
                                    if ( glgeParent ) {
                                        glgeParent.addObject( node.glgeObject );
                                    } else {
                                        if ( sceneNode.glgeScene ) {
                                            sceneNode.glgeScene.addObject( node.glgeObject );
                                        }
                                    }
                                }
                            }
                            break;
                            
                        case "definition/mesh": {
                                node = this.state.nodes[childID] = {
                                    name: childName,  
                                    glgeObject: undefined,
                                    glgeParent: glgeParent,
                                    source: childSource,
                                    ID: childID,                                
                                    parentID: nodeID,
                                    type: childExtendsID,
                                    sourceType: childType,
                                    glgeScene: sceneNode  
                                };
                                createMesh.call( this, node );
                            }
                            break;

                        default:
                            node = this.state.nodes[childID] = {
                                name: childName,  
                                glgeObject: glgeChild,
                                ID: childID,
                                parentID: nodeID,
                                type: childExtendsID,
                                sourceType: childType,
                                glgeScene: sceneNode  
                            };
                            if ( node.glgeObject ) {
                                if ( ( node.glgeObject.constructor == GLGE.Collada ) ) {
                                    callback( false );
                                    node.glgeObject.vwfID = childID;
                                    sceneNode.xmlColladaObjects.push( node.glgeObject );
                                    setColladaCallback.call( this, node.glgeObject, sceneNode );
                                    node.loadedCallback = callback;                                    
                                }
                            } else {
                                if ( nodeID != 0 ) {
                                    node.glgeObject = new GLGE.Group();
                                    if ( parentNode ) {
                                        if ( parentNode.glgeObject ) {
                                            parentNode.glgeObject.addObject( node.glgeObject );
                                        } else if ( parentNode.glgeScene ) {
                                            parentNode.glgeScene.addObject( node.glgeObject );
                                        }
                                    }
                            
                                    node.gui = node.glgeObject.uid;
                                    node.glgeObject.name = childName;  
                                }
                            }
                            break;
                    }  

                    this.settingProperty( childID, "playing", false );  // TODO: these are matching the defaults in node3; they should be sent through creatingProperty() so that we don't have to ask
                    this.settingProperty( childID, "looping", false );  // TODO: these are matching the defaults in node3; they should be sent through creatingProperty() so that we don't have to ask
                    this.settingProperty( childID, "speed", 1 );  // TODO: these are matching the defaults in node3; they should be sent through creatingProperty() so that we don't have to ask

                }
            } else if ( prototypes && isGlgeMaterialDefinition.call( this, prototypes ) ) {
                if ( childName !== undefined ) {
                    node = this.state.nodes[childID] = {
                        name: childName,
                        glgeObject: undefined,
                        glgeMaterial: true,
                        ID: childID,
                        parentID: nodeID,
                        type: childExtendsID,
                        sourceType: childType,
                    };
                    findMaterial.call( this, nodeID, childName, node );
                }
            } // end of else

            // 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 ( ! ( childType == "model/vnd.collada+xml" || 
                     childType == "model/vnd.osgjs+json+compressed" ) )
                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 three.js 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 ) ) {
                        //console.info( " 1    getting "+propertyName+" of: " + childExtendsID  );
                        ptPropValue = kernel.getProperty( childExtendsID, propertyName );
                        if ( ptPropValue !== undefined && ptPropValue !== null && childID !== undefined && childID !== null) {
                            //console.info( " 1    setting "+propertyName+" of: " + nodeID + " to " + ptPropValue );
                            self.settingProperty( childID, propertyName, ptPropValue );
                        }
                    }
                } );
                childImplementsIDs.forEach( function( behaviorID ) {
                    for ( var propertyName in kernel.getProperties( behaviorID ) ) {
                        //console.info( "     2    getting "+propertyName+" of: " + behaviorID  );
                        ptPropValue = kernel.getProperty( behaviorID, propertyName );
                        if ( ptPropValue !== undefined && ptPropValue !== null && childID !== undefined && childID !== null) {
                            //console.info( "     2    setting "+propertyName+" of: " + nodeID + " to " + ptPropValue );
                            self.settingProperty( childID, propertyName, ptPropValue );
                        }
                    }
                } );
            }; 
        },
         
        // -- deletingNode -------------------------------------------------------------------------

        deletingNode: function( nodeID ) {

            if ( this.state.nodes[ nodeID ] ) {
                var node = this.state.nodes[ nodeID ];
                if ( node.glgeObject ) {
                    var obj = node.glgeObject;
                    var parent = obj.parent;
                    if ( parent ) {
                        if ( parent.removeChild ) parent.removeChild( obj );
                        node.glgeObject = undefined;
                        //delete obj;
                    }
                }
                delete this.state.nodes[ nodeID ];
            }
        },

        // -- addingChild ------------------------------------------------------------------------
        
        addingChild: function( nodeID, childID, childName ) {
            
            var parentGlgeObj = getGlgeObject.call( this, nodeID );
            var childGlgeObj = getGlgeObject.call( this, childID );

            if ( parentGlgeObj && childGlgeObj && parentGlgeObj instanceof GLGE.Group ) {

                var childParent = childGlgeObj.parent;
                // what does vwf do here?  add only if parent is currently undefined
                if ( childParent ) {
                    childParent.remove( childGlgeObj )   
                } 
                parentGlgeObj.add( childGlgeObj );   
            }
        },

        // -- movingChild ------------------------------------------------------------------------
        
        movingChild: function( nodeID, childID, childName ) {
            var parentGlgeObj = getGlgeObject.call( this, nodeID );
            var childGlgeObj = getGlgeObject.call( this, childID );

            if ( parentGlgeObj && childGlgeObj && parentGlgeObj instanceof GLGE.Group ) {

                var childParent = childGlgeObj.parent;
                
                if ( childParent ) {
                    childParent.remove( childGlgeObj ); 
                    parentGlgeObj.add( childGlgeObj );   
                } 
                  
            }
        },

        // -- removingChild ------------------------------------------------------------------------
        
        removingChild: function( nodeID, childID, childName ) {
            var parentGlgeObj = getGlgeObject.call( this, nodeID );
            var childGlgeObj = getGlgeObject.call( this, childID );

            if ( parentGlgeObj && childGlgeObj && parentGlgeObj instanceof GLGE.Group ) {

                var childParent = childGlgeObj.parent;
                if ( childParent === parentGlgeObj ) {
                    parentGlgeObj.remove( childGlgeObj )   
                } 
                  
            }
        },

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

        creatingProperty: function( nodeID, propertyName, propertyValue ) {

            return this.initializingProperty( nodeID, propertyName, propertyValue );
        },

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

        initializingProperty: function( nodeID, propertyName, propertyValue ) {

            var value = undefined;

            if ( propertyValue !== undefined ) {
                var node = this.state.nodes[ nodeID ];
                if ( !node ) node = this.state.scenes[ nodeID ];
                if ( node ) {
                    switch ( propertyName ) {

                        case "meshDefinition":
                            defineMesh.call( this, propertyValue, node );
                            break;

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

            return value;
        },

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

        settingProperty: function( nodeID, propertyName, propertyValue ) {

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

            if ( node && node.glgeObject && propertyValue !== undefined ) {

                var validProperty = false;
                var glgeObject = node.glgeObject;
                var isAnimatable = glgeObject.animate; // implements GLGE.Animatable?
    isAnimatable = isAnimatable && glgeObject.animation || propertyName == "looping" && glgeObject.constructor == GLGE.ParticleSystem; // has an animation?
    isAnimatable = isAnimatable && node.name != "cityblock.dae"; // TODO: this is a hack to prevent disabling the animation that keeps the world upright


                value = propertyValue;

                if ( isAnimatable ) {

                    switch ( propertyName ) {

                        case "playing":

    if ( !Boolean( propertyValue ) && glgeObject.animFinished ) {  // TODO: GLGE finished doesn't flow back into node3's playing yet; assume playing is being toggled and interpret it as true if the animation has played and finished.
        propertyValue = true;
    }

//    if ( !node.initialized ) {  // TODO: this is a hack to set the animation to frame 0 during initialization
//        //if ( glgeObject.animFrames == 100 ) { glgeObject.setFrames( 50 ); } // hack to use only the opening half of the door animation
//        glgeObject.setStartFrame( 0, 0, glgeObject.getLoop() );
//        glgeObject.getInitialValues( glgeObject.animation, glgeObject.animationStart );
//    }

                            if ( Boolean( propertyValue ) ) {
                                if ( glgeObject.animFinished ) {
                                    glgeObject.setStartFrame( 0, 0, glgeObject.getLoop() );
                                } else if ( glgeObject.getPaused() ) {
                                    if ( glgeObject.animFrames == 100 ) {
                                        glgeObject.setFrames( 50 );
                                    }
                                    glgeObject.setPaused( GLGE.FALSE );
                                    value = false;
                                    validProperty = true;
                                }
                            } else {
                                glgeObject.setPaused( GLGE.TRUE );
                                value = true;
                                validProperty = true;
                            }

                            break;

                        case "looping":
                            value = Boolean( propertyValue ) ? GLGE.TRUE : GLGE.FALSE;
                            glgeObject.setLoop( value );
                            value = (value == GLGE.TRUE) ? true : false;
                            validProperty = true;
                            break;

                        case "speed":
                            value = Number( propertyValue ) * 30; // TODO: not safe to assume default speed is 30 fps
                            glgeObject.setFrameRate( value );
                            validProperty = true;
                            break;
                    }
                }

                var pv = propertyValue;
                switch ( propertyName ) {

                    case "transform":

                        //console.info( "setting transform of: " + nodeID + " to " + Array.prototype.slice.call( propertyValue ) );
                        var transform = goog.vec.Mat4.createFromArray( propertyValue || [] );

                        // Rotate 90 degress around X to convert from VWF Z-up to GLGE Y-up.
                        if ( glgeObject instanceof GLGE.Camera ) {
                            var columny = goog.vec.Vec4.create();
                            goog.vec.Mat4.getColumn( transform, 1, columny );
                            var columnz = goog.vec.Vec4.create();
                            goog.vec.Mat4.getColumn( transform, 2, columnz );
                            goog.vec.Mat4.setColumn( transform, 1, columnz );
                            goog.vec.Mat4.setColumn( transform, 2, goog.vec.Vec4.negate( columny, columny ) );
                        }

                        // Assign the transform. GLGE matrices are transposed compared to VWF.
                        // setStaticMatrix() doesn't propagate correctly for cameras, so we have to
                        // decompose camera assignments.

                        if ( glgeObject instanceof GLGE.Camera || glgeObject instanceof GLGE.Light || glgeObject instanceof GLGE.ParticleSystem ) { // setStaticMatrix doesn't work for cameras

                            var translation = goog.vec.Vec3.create();
                            goog.vec.Mat4.getColumn( transform, 3, translation );
                            goog.vec.Mat4.setColumnValues( transform, 3, 0, 0, 0, 1 );
                            goog.vec.Mat4.transpose( transform, transform );
                            glgeObject.setRotMatrix( transform );
                            glgeObject.setLoc( translation[0], translation[1], translation[2] );

                        } else {

                            // Set loc[XYZ] so that GLGE.Placeable.getPosition() will return correct
                            // values for lookAt. setLoc() clears the static matrix, so call it
                            // before setStaticMatrix().

                            var translation = goog.vec.Vec3.create();
                            goog.vec.Mat4.getColumn( transform, 3, translation );
                            glgeObject.setLoc( translation[0], translation[1], translation[2] );

                            // Set the full matrix.

                            glgeObject.setStaticMatrix(
                                goog.vec.Mat4.transpose( transform, goog.vec.Mat4.create() )
                            );
                        }

                        break;

                    case "material": {
                            var sceneNode = this.state.scenes[ this.state.sceneRootID ];
                            if ( sceneNode && node.glgeObject ) {
                                if ( propertyValue && propertyValue.constructor == Array ) propertyValue = propertyValue[(Math.random() * propertyValue.length) | 0];
                                if ( !propertyValue ) propertyValue = "grey";
                                var mat = sceneNode.glgeDocument.getElement( propertyValue );
                                if ( mat ) {
                                    node.glgeObject.setMaterial( mat ); 
                                }
                            } 
                        }                      
                        break;

                    case "lookAt": {
                            //console.info( "settingProperty( " + nodeID + ", " + propertyName + ", " + propertyValue + " )" );
                            if ( propertyValue == "activeCamera" ) {
                                if ( this.state.cameraInUse ) {
                                    glgeObject.setLookat( this.state.cameraInUse );
                                }
                            } else {
                                var lookAtNode = this.state.nodes[ propertyValue ];
                                if ( lookAtNode && lookAtNode.glgeObject ) {
                                    //console.info( "         settingProperty found lookat object" );
                                    glgeObject.setLookat( lookAtNode.glgeObject );
                                } else {
                                    if ( glgeObject.getLookat && glgeObject.getLookat() ) 
                                        glgeObject.setLookat( null );
                                }
                            }
                        }
                        break;

                    case "pickable":
                        if ( glgeObject.setPickable ){
                            glgeObject.setPickable( propertyValue );
                        }                            
                        break;

                    case "visible":
                        if ( glgeObject.setVisible ) {
                            glgeObject.setVisible( propertyValue );
                        }
                        break;

                    default:
                        prototypes = getPrototypes.call( this, this.kernel.kernel.kernel, node["type"] );
                        if ( isGlgeMaterialDefinition.call( this, prototypes ) ){
                            value = setMaterialProperty.call( this, nodeID, propertyName, propertyValue );
                        } else if ( isGlgeCameraDefinition.call( this, prototypes ) ) {
                            value = setCameraProperty.call( this, nodeID, propertyName, propertyValue );
                        } else if ( isGlgeLightDefinition.call( this, prototypes ) ) {
                            value = setLightProperty.call( this, nodeID, propertyName, propertyValue );
                        } else if ( isGlgeParticleSystemDefinition.call( this, prototypes ) ) {
                            value = setParticleSystemProperty.call( this, nodeID, propertyName, propertyValue );
                        } else if ( isGlgeSceneDefinition.call( this, prototypes ) ) {
                            value = setSceneProperty.call( this, nodeID, propertyName, propertyValue );
                        } else {
                            if ( !validProperty ) {
                                value = undefined;
                            }                          
                        }
                        break;
                }
            } else if ( this.state.scenes[nodeID] ) {
                value = setSceneProperty.call( this, nodeID, propertyName, propertyValue );
            } 

             return value;        
        },

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

        gettingProperty: function( nodeID, propertyName, propertyValue ) {

            var node = this.state.nodes[nodeID]; // { name: childName, glgeObject: undefined }
            var value = undefined;
            var glgeModel = this;
            var prototypes = undefined;
            var validProperty = false;

            if ( node && node.glgeObject ) {

                var glgeObject = node.glgeObject;
                var isAnimatable = glgeObject.animate; // implements GLGE.Animatable?
    isAnimatable = isAnimatable && glgeObject.animation || propertyName == "looping" && glgeObject.constructor == GLGE.ParticleSystem; // has an animation?
    isAnimatable = isAnimatable && node.name != "cityblock.dae"; // TODO: this is a hack to prevent disabling the animation that keeps the world upright

                if ( isAnimatable ) {

                    switch ( propertyName ) {

                        case "playing":
                            value = !Boolean( glgeObject.getPaused() );
                            validProperty = true;
                            break;

                        case "looping":
                            value = Boolean( glgeObject.getLoop() );
                            validProperty = true;
                            break;

                        case "speed":
                            value = glgeObject.getFrameRate() / 30; // TODO: not safe to assume default speed is 30 fps
                            validProperty = true;
                            break;
                    }
                }

                switch ( propertyName ) {

                    case "transform":

                        // We would use glgeObject.getLocalMatrix(), but glgeObject.localMatrix
                        // isn't always recalculated. So, we need to replicate the calculations from
                        // glgeObject.getModelMatrix(). VWF matrices are transposed compared to GLGE.

                        value = goog.vec.Mat4.transpose( glgeObject.staticMatrix ||
                            GLGE.mulMat4(
                                glgeObject.getTranslateMatrix(),
                                GLGE.mulMat4(
                                    glgeObject.getRotMatrix(),
                                    glgeObject.getScaleMatrix()
                                )
                            ),
                            goog.vec.Mat4.create()
                        );

                        // Rotate -90 degress around X to convert from GLGE Y-up to VWF Z-up.

                        if ( glgeObject instanceof GLGE.Camera ) {
                            var columny = goog.vec.Vec4.create();
                            goog.vec.Mat4.getColumn( value, 1, columny );
                            var columnz = goog.vec.Vec4.create();
                            goog.vec.Mat4.getColumn( value, 2, columnz );
                            goog.vec.Mat4.setColumn( value, 2, columny );
                            goog.vec.Mat4.setColumn( value, 1, goog.vec.Vec4.negate( columnz, columnz ) );
                        }

                        break;
                
                    case "boundingbox":
                        var bbox;
                        if ( glgeObject.getBoundingVolume ) {
                            bbox = glgeObject.getBoundingVolume( true );
                            value = { min: { x: bbox.limits[0], y: bbox.limits[2], z: bbox.limits[4] }, max: { x: bbox.limits[1], y: bbox.limits[3], z: bbox.limits[5] } };
                        } 
                        break;

                    case "centerOffset":
                        var centerOff = getCenterOffset.call( this, glgeObject );
                        var scale = this.kernel.getProperty( nodeID, "scale", undefined );
                        value = new Array;
                        value.push( centerOff[0] * scale[0], centerOff[1] * scale[1], centerOff[2] * scale[2] ); 
                        break;

                    case "vertices":
                        value = getMeshVertices.call( this, glgeObject );
                        break;

                    case "vertexIndices":
                        value = getMeshVertexIndices.call( this, glgeObject );
                        break;

                    case "meshData":
                        value = [];
                        var scale = this.gettingProperty( nodeID, "scale", [] ); 
                        var meshList = findAllMeshes.call( this, glgeObject );
                        for ( var i = 0; i < meshList.length; i++ ) {
                            value.push( {  "vertices": getMeshVertices.call( this, meshList[i] ),
                                           "vertexIndices": getMeshVertexIndices.call( this, meshList[i] ),
                                           "scale": scale 
                                        } );
                        }
                        break;
 
                    case "lookAt": {
                            value = "";
                            var lookAtObject = glgeObject.getLookat();
                            if ( lookAtObject ) {
                                value = getObjectID.call( glgeModel, lookAtObject, false, false );
                            }
                        }
                        break;

                    case "pickable":
                        if ( glgeObject.getPickable ){
                            value = glgeObject.getPickable();
                        }                            
                        break;

                    case "visible":
                        if ( glgeObject.getVisible ) {
                            value = glgeObject.getVisible();
                        }
                        break;

                    default:
                        // handle all of the other types
                        prototypes = getPrototypes.call( this, this.kernel.kernel.kernel, node["type"] );
                        if ( isGlgeMaterialDefinition.call( this, prototypes ) ){
                            value = getMaterialProperty.call( this, nodeID, propertyName, propertyValue );
                        } else if ( isGlgeCameraDefinition.call( this, prototypes ) ) {
                            value = getCameraProperty.call( this, nodeID, propertyName, propertyValue );
                        } else if ( isGlgeLightDefinition.call( this, prototypes ) ) {
                            value = getLightProperty.call( this, nodeID, propertyName, propertyValue );
                        } else if ( isGlgeParticleSystemDefinition.call( this, prototypes ) ) {
                            value = getParticleSystemProperty.call( this, nodeID, propertyName, propertyValue );
                        } else if ( isGlgeSceneDefinition.call( this, prototypes ) ) {
                            value = getSceneProperty.call( this, nodeID, propertyName, propertyValue );
                        } else {
                            if ( !validProperty ) {
                                value = undefined;
                            }   
                        }
                        break;    

                }
            } else if ( this.state.scenes[nodeID] ) {
                value = getSceneProperty.call( this, nodeID, propertyName, propertyValue );
            } 

//            if ( value && value instanceof Object && !( value instanceof Array ) && !( value instanceof Float32Array ) ){
//                console.info( "WARNING: gettingProperty( "+nodeID+", "+propertyName+" ) returning an OBJECT: " + value );
//            }

            return value;

        },


        // TODO: deletingMethod

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

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


        // TODO: creatingEvent, deltetingEvent, firingEvent

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

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

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

//        ticking: function( vwfTime ) {
//        },

    } );

    // == Private functions ==================================================================

    // -- checkCompatibility -------------------------------------------------------------

    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;
    }

    // -- initScene ------------------------------------------------------------------------

    function initScene( sceneNode ) {

        if ( sceneNode && !sceneNode.modelInited ) {
            sceneNode.modelInited = true;

            //findAllColladaObjs.call( this, sceneNode.glgeScene, sceneNode.ID );
        }
    } 

    // -- loadCollada ------------------------------------------------------------------------  
    
    function loadCollada( parentNode, node, propertyNotifyCallback ) {

        // Create new GLGE collada object
        node.glgeObject = new GLGE.Collada;

        // Set properties on new GLGE collada object
        var sceneNode = this.state.scenes[ this.state.sceneRootID ];
        var colladaLoadedCallback = setColladaCallback.call( this, node.glgeObject, sceneNode, 
                                                             propertyNotifyCallback );
        node.glgeObject.vwfID = node.ID;
        node.glgeObject.setDocument( node.source, window.location.href, colladaLoadedCallback );

        // Add the new GLGE collada object to the list of those that need to be loaded
        sceneNode.srcColladaObjects.push( node.glgeObject );
        
        // If the new node has a parent, attach its GLGE object to that of the parent
        // Else, attach it to the scene directly as a top-level object
        if ( parentNode && parentNode.glgeObject )
            parentNode.glgeObject.addCollada( node.glgeObject );
        else if ( sceneNode && sceneNode.glgeScene )
            sceneNode.glgeScene.addCollada( node.glgeObject );
    }


    // -- findCollada ------------------------------------------------------------------------

    function findCollada( grp, nodeID ) {

        if ( grp && grp.getChildren ) {
            var children = grp.getChildren();
            var sceneNode = this.state.scenes[ this.state.sceneRootID ];    

            for ( var i = 0; i < children.length; i++ ) {
                if ( children[i].constructor == GLGE.Collada ) {
                    var modelID = nodeID;
                    var glgeModel = this;

                    sceneNode.xmlColladaObjects.push( children[i] );
                    setColladaCallback.call( this, children[i], sceneNode );

                    children[i].loadedCallback = colladaLoaded;
                    sceneNode.pendingLoads++;
                }
                findCollada.call( this, children[i] ); 
            }
        }
    }


    // -- setColladaCallback ------------------------------------------------------------------------

    function setColladaCallback( glgeColladaObject, sceneNode, propertyNotifyCallback ) {

        var self = this;

        // Update the number of pending loads on the scene node
        // This information is just for our own knowledge; it is not used anywhere
        // (But double-check to make sure no one has added a reference before you do anything drastic)
        sceneNode.pendingLoads++;

        // Set the callback as defined below
        glgeColladaObject.loadedCallback = colladaLoaded;

        function colladaLoaded( colladaObject ) {

            // Undo default GLGE rotation applied in GLGE.Collada.initVisualScene that adjusts for +Y up
            colladaObject.setRot( 0, 0, 0 );

            // Update the number of pending loads on the scene node
            // This information is just for our own knowledge; it is not used anywhere
            // (But double-check to make sure no one has added a reference before you do anything drastic)
            sceneNode.pendingLoads--;

            // Now that this object has loaded, remove it from the list of objects to be loaded            
            var removedFromLoadList = false;
            for ( var i = 0; !removedFromLoadList && i < sceneNode.srcColladaObjects.length; i++ ) {
                if ( sceneNode.srcColladaObjects[ i ] == colladaObject ){
                    sceneNode.srcColladaObjects.splice( i, 1 );
                    removedFromLoadList = true;
                }
            }
            for ( var i = 0; !removedFromLoadList && i < sceneNode.xmlColladaObjects.length; i++ ) {
                if ( sceneNode.xmlColladaObjects[ i ] == colladaObject ){
                    sceneNode.xmlColladaObjects.splice( i, 1 );
                    removedFromLoadList = true;
                }
            }

            // 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
            if ( propertyNotifyCallback )
                propertyNotifyCallback();

            // If the VWF node for the newly loaded collada has a callback function, call it
            // TODO: Since the callback resumes the queue, maybe we should not call it if there are still
            //       objects to be loaded
            var id = colladaObject.vwfID || getObjectID.call( self, colladaObject, true, false );
            if ( id && ( id != "" ) ) {
                var colladaNode = self.state.nodes[ id ];
                if ( colladaNode && colladaNode.loadedCallback ) {
                    colladaNode.loadedCallback( true );                    
                }
            }
        }
        return colladaLoaded;
    }

    // -- findColladaParent ------------------------------------------------------------------------

    function findAllColladaObjs( glgeScene, nodeID ) {

        findCollada.call( this, glgeScene, nodeID );

    }

    // -- findColladaParent ------------------------------------------------------------------------

    function findColladaParent( glgeObject ) {
        var colladaObj = undefined;
        var currentObj;
        if ( glgeObject ) {
            currentObj = glgeObject;
            while ( !colladaObj && currentObj ) {
                if ( currentObj.constructor == GLGE.Collada )
                    colladaObj = currentObj;
                else
                    currentObj = currentObj.parent;
            } 
        }
        return colladaObj;    
    }


    // -- setSceneProperty ------------------------------------------------------------------------

    function setSceneProperty( nodeID, propertyName, propertyValue ) {

        var value = propertyValue;
        var sceneNode = this.state.scenes[ nodeID ];

        if ( sceneNode && sceneNode.glgeScene ) {
            switch ( propertyName ) {
                case "ambientColor":
                    if ( propertyValue )
                        sceneNode.glgeScene.setAmbientColor( propertyValue );
                    else
                        this.logger.warn( "Invalid ambient color");
                    break;
                case "activeCamera":
                        if ( this.state.nodes[ propertyValue ] ) {
                            setActiveCamera.call( this, sceneNode, propertyValue );
                        }
                    break;
                case "backgroundColor":
                    if ( propertyValue )
                        sceneNode.glgeScene.setBackgroundColor( propertyValue );
                    else
                        this.logger.warn( "Invalid background color");
                    break;

                default:
                    value = undefined;
                    break;
            }
        }
        return value;

    }

    // -- setParticleSystemProperty ------------------------------------------------------------------------

    function setParticleSystemProperty( nodeID, propertyName, propertyValue ) {

        var node = this.state.nodes[nodeID]; // { name: childName, glgeObject: undefined }
        var value = propertyValue;
        switch ( propertyName ) {
            case "numberParticles":
                node.glgeObject.setNumParticles( propertyValue );
                break;
            case "lifeTime":
                node.glgeObject.setLifeTime( propertyValue );
                break;
            case "maxLifeTime":
                node.glgeObject.setMaxLifeTime( propertyValue );
                break;
            case "minLifeTime":
                node.glgeObject.setMinLifeTime( propertyValue );
                break;
            case "startSize":
                node.glgeObject.setStartSize( propertyValue );
                break;
            case "endSize":
                node.glgeObject.setEndSize( propertyValue );
                break;
            case "loop":
                node.glgeObject.setLoop( propertyValue );
                break;
            case "velocity":
                if ( propertyValue )
                    node.glgeObject.setVelocity( propertyValue[0], propertyValue[1], propertyValue[2] );
                break;
            case "maxVelocity":
                if ( propertyValue )
                    node.glgeObject.setMaxVelocity( propertyValue[0], propertyValue[1], propertyValue[2] );
                break;            
            case "minVelocity":
                if ( propertyValue )
                    node.glgeObject.setMinVelocity( propertyValue[0], propertyValue[1], propertyValue[2] );
                break;    
            case "startAcceleration":
                if ( propertyValue )
                    node.glgeObject.setStartAccelertaion( propertyValue[0], propertyValue[1], 
                                                          propertyValue[2] );
                break;
            case "endAcceleration":
                if ( propertyValue )
                    node.glgeObject.setEndAccelertaion( propertyValue[0], propertyValue[1], propertyValue[2] );
                break;
            case "maxStartAcceleration":
                if ( propertyValue )
                    node.glgeObject.setMaxStartAccelertaion( propertyValue[0], propertyValue[1], 
                                                             propertyValue[2] );
                break;
            case "maxEndAcceleration":
                if ( propertyValue )
                    node.glgeObject.setMaxEndAccelertaion( propertyValue[0], propertyValue[1], 
                                                           propertyValue[2] );
                break;
            case "minStartAcceleration":
                if ( propertyValue )
                    node.glgeObject.setMinStartAccelertaion( propertyValue[0], propertyValue[1], 
                                                             propertyValue[2] );
                break;
            case "minEndAcceleration":
                if ( propertyValue )
                    node.glgeObject.setMinEndAccelertaion( propertyValue[0], propertyValue[1], 
                                                           propertyValue[2] );
                break;
            case "startColor":
                if ( propertyValue )
                    node.glgeObject.setStartColor( propertyValue );
                break;
            case "endColor":
                if ( propertyValue )
                    node.glgeObject.setEndColor( propertyValue );
                break;
            case "image":
                node.glgeObject.setImage( propertyValue );
                break;
            default:
                value = undefined;
                break;
        }
        return value;

    }

    // -- setCameraProperty ------------------------------------------------------------------------

    function setCameraProperty( nodeID, propertyName, propertyValue ) {

        var node = this.state.nodes[nodeID]; // { name: childName, glgeObject: undefined }
        var value = propertyValue;
        switch ( propertyName ) {
            case "cameraType":
                switch ( propertyValue ) {
                    case "perspective":
                        node.glgeObject.setType( GLGE.C_PERSPECTIVE );
                        break;
                    case "orthographic":
                        node.glgeObject.setType( GLGE.C_ORTHO );
                        break;
                    default:
                        value = undefined;
                        break;
                }
                break;
            case "far":
                node.glgeObject.setFar( Number( propertyValue ) );
                break;
            case "near":
                node.glgeObject.setNear( Number( propertyValue ) );
                break;
            case "fovy":
                node.glgeObject.setFovY( Number( propertyValue ) );
                break;            
            case "aspect":
                
                // If the propertyValue is real, set it
                // Else, it will be set to be the aspect ratio of the GLGE canvas
                if ( propertyValue )
                    node.glgeObject.setAspect( Number( propertyValue ) );
                break;            
//            case "orthoscale":
//                if ( propertyValue ) { 
//                    node.glgeObject.setOrthoScale( Number( propertyValue ) );
//                }
//                break;
            default:
                value = undefined;
                break;
        }
        return value;

    }

    // -- setMaterialProperty ------------------------------------------------------------------------

    function setMaterialProperty( nodeID, propertyName, propertyValue ) {

        var node = this.state.nodes[ nodeID ]; 
        var value = propertyValue;
        var txtr, mat, txtrPropValue;

        if ( propertyValue ) {
            if ( node.glgeMaterial && node.glgeMaterial.textures ) {
                mat = node.glgeMaterial;
                txtr = node.glgeMaterial.textures[0];
            } else if ( node.glgeObject && node.glgeObject.material ) {
                mat = node.glgeObject.material; 
                txtr = node.glgeObject.material.textures[0];
            }            
            switch ( propertyName ) {
                case "texture": {
                    
            // setting the texture back to the same value is causing a problem, need 
            // to find out why so this get and check can be removed
            txtrPropValue = getMaterialProperty.call( this, nodeID, propertyName );
                    //console.info( "txtrProp = " + txtrPropValue );
                    //console.info( "Setting the texture of: " + nodeID + " to " + propertyValue);    
                    if ( ( propertyValue !== undefined ) && ( propertyValue != "" ) && ( propertyValue != txtrPropValue ) ) {
                        var textureType = "image";
                        if ( propertyValue == "canvas" ) {
                            textureType = "canvas";
                        } else if ( propertyValue.indexOf( "canvas_" ) == 0 ) {
                            textureType = "canvas";   
                        } else if ( this.state.nodes[propertyValue] ) {
                            textureType = "camera";
                        } else if ( !isImageFileRef.call( this, propertyValue ) /* isVideoFileRef.call( this, propertyValue ) */ ) {
                            textureType = "video";
                        }

                        //console.info( "     mat: " + mat );
                        //console.info( "     txtr: " + txtr );                        
                        //console.info( "     textureType: " + textureType );

                        var isCorrectTexture = false;
                        if ( txtr ) {
                            switch ( textureType ){ 
                                case "image":
                                    isCorrectTexture = ( txtr instanceof GLGE.Texture );
                                    break;
                                case "canvas":
                                    isCorrectTexture = ( txtr instanceof GLGE.TextureCanvas );
                                    break;
                                case "camera":
                                    isCorrectTexture = ( txtr instanceof GLGE.TextureCamera );
                                    break;
                                case "video":
                                    isCorrectTexture = ( txtr instanceof GLGE.TextureVideo );
                                    break;                                    
                            }
                        }

                        //console.info( "     isCorrectTexture: " + isCorrectTexture );

                        if ( isCorrectTexture ) {
                            if ( textureType == "video" || textureType == "image" ){
                                txtr.setSrc( propertyValue );   
                            } else if ( "camera" ) {
                                var camNode = this.state.nodes[ propertyValue ];
                                var cam = findCamera.call( this, camNode );
                                if ( cam ) {
                                    txtr.setCamera( cam );
                                } 
                            } 
                        } else if ( mat ) {
                            var txt;
					        var ml = new GLGE.MaterialLayer;
					        ml.setMapto( GLGE.M_COLOR );
					        ml.setMapinput( GLGE.UV1 );
                            //console.info( "     new textureType: " + textureType );

                            switch ( textureType ) {
                                case "image":
                                    txt = new GLGE.Texture();
                                    txt.setSrc( propertyValue );
                                    break;
                                case "canvas":
                                    //console.info( "     GLGE.TextureCanvas with id = " + propertyValue );
                                    txt = new GLGE.TextureCanvas( undefined, "512", "512", propertyValue );
                                    break;
                                case "camera":
                                    txt = new GLGE.TextureCamera( undefined, "512", "512" );
                                    var camNode = this.state.nodes[ propertyValue ];
                                    var cam = findCamera.call( this, camNode );
                                    if ( cam ) {
                                        txt.setCamera( cam );
                                    }                                     
                                    break;
                                case "video":
                                    txt = new GLGE.TextureVideo( undefined, "1024", "512" );
                                    txt.setSrc( propertyValue );
                                    ml.setScaleX( -1 );
                                    ml.setScaleY( -1 );                                    
                                    break;                                  
                            }
                            //console.info( "     txt: " + txt );
                            mat.addTexture( txt );
					        ml.setTexture( txt );
					        mat.addMaterialLayer( ml );
                        }
                    }
                    }
                    break;

                case "color":
                    if ( mat ) { mat.setColor( propertyValue ); } 
                    break;                
                case "ambient":
                    if ( mat ) { mat.setAmbient( propertyValue ); } 
                    break;
                case "specColor":
                    if ( mat ) { mat.setSpecularColor( propertyValue ); } 
                    break;
                case "shininess":
                    if ( mat ) { mat.setShininess( propertyValue ); } 
                    break;  
                case "reflect":
                    if ( mat ) { mat.setReflectivity( propertyValue ); } 
                    break;
                case "specular":
                    if ( mat ) { mat.setSpecular( propertyValue ); } 
                    break;
                case "emit":
                    if ( mat ) { mat.setEmit( propertyValue ); } 
                    break; 
                case "alpha":
                    if ( mat ) { mat.setAlpha( propertyValue ); } 
                    break;                    
                case "binaryAlpha":
                    if ( mat ) { mat.setBinaryAlpha( propertyValue ); } 
                    break; 

                default:
                    value = undefined;
                    break;
            }
        }

        return value;
    }


    // -- setLightProperty ------------------------------------------------------------------------

    function setLightProperty( nodeID, propertyName, propertyValue ) {

        if ( propertyValue === undefined ) return;

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

        switch ( propertyName ) {
            case "lightType":
                switch ( propertyValue ) {
                    case "point":
                        node.glgeObject.setType( GLGE.L_POINT );
                        break;
                    case "directional":
                        node.glgeObject.setType( GLGE.L_DIR );
                        break;
                    case "spot":
                        node.glgeObject.setType( GLGE.L_SPOT );
                        break;
                    default:
                        value = undefined;
                        break;
                }
                break;
            case "enable":
                if ( propertyValue && propertyValue != "false" ) {
                    node.glgeObject.enableLight();
                } else {
                    node.glgeObject.disableLight();
                }
                break;

            case "glge-constantAttenuation":
                node.glgeObject.setAttenuationConstant( propertyValue );
                break;

            case "glge-linearAttenuation":
                node.glgeObject.setAttenuationLinear( propertyValue );
                break;

            case "glge-quadraticAttenuation":
                node.glgeObject.setAttenuationQuadratic( propertyValue );
                break;

            case "glge-spotCosCutOff":
                node.glgeObject.setSpotCosCutOff( propertyValue );
                break;

            case "glge-spotCutOff":
                node.glgeObject.setSpotCutOff( propertyValue );
                break;

            case "glge-spotExponent":
                node.glgeObject.setSpotExponent( propertyValue );
                break;

            case "color":
                node.glgeObject.setColor( propertyValue );
                break;

            case "diffuse":
                node.glgeObject.setColor(propertyValue); // no setDiffuse() in GLGE 0.7
                break;

            case "specular":
            case "glge-specular":
                node.glgeObject.specular = propertyValue; // no setSpecular() in GLGE 0.7
                break;

            case "glge-samples":
                node.glgeObject.setShadowSamples( propertyValue );
                break;

            case "glge-softness":
                node.glgeObject.setShadowSoftness( propertyValue );
                break;

            case "glge-bufferHeight":
                node.glgeObject.setBufferHeight( propertyValue );
                break;

            case "glge-bufferWidth":
                node.glgeObject.setBufferWidth( propertyValue );
                break;

            case "glge-shadowBias":
                node.glgeObject.setShadowBias( propertyValue );
                break;

            case "distance":
                node.glgeObject.setDistance( propertyValue );
                break;

            case "castShadows":
                node.glgeObject.setCastShadows( propertyValue );
                break;

            case "glge-spotSoftness":
                node.glgeObject.setSpotSoftness( propertyValue );
                break;

            case "glge-spotSoftnessDistance":
                node.glgeObject.setSpotSoftDistance( propertyValue );
                break;

            case "glge-cascadeLevels":
                node.glgeObject.setCascadeLevels( propertyValue );
                break;

            default:
                value = undefined;
                break;
        }
        return value;

    }

    // -- getSceneProperty ------------------------------------------------------------------------------

    function getSceneProperty( nodeID, propertyName, propertyValue ) {

        var color = undefined, tempClr;
        var sceneNode = this.state.scenes[nodeID] // { name: childName, glgeObject: undefined }
        var value = undefined;
        switch ( propertyName ) {
              case "ambientColor":
                color = vwfColor.call( this, sceneNode.glgeScene.getAmbientColor() );
                value = color.toString();
                break;
            case "activeCamera":
                if ( sceneNode.glgeScene.camera ) {
                    value = getObjectID.call( this, sceneNode.glgeScene.camera, false, false );
                }
                break;

            case "backgroundColor":
                tempClr = sceneNode.glgeScene.getBackgroundColor();
                if ( tempClr ) {
                    if ( tempClr.r && isNaN( tempClr.r ) ) tempClr.r = 0;
                    if ( tempClr.g && isNaN( tempClr.g ) ) tempClr.g = 0;
                    if ( tempClr.b && isNaN( tempClr.b ) ) tempClr.b = 0;
                    if ( tempClr.a && isNaN( tempClr.a ) ) tempClr.a = 0;
                    color = vwfColor.call( this, tempClr );
                    value = color.toString();
                }
                break;
            
            default:
                value = undefined;
                break;

        }
        return value;

    }

    // -- getParticleSystemProperty ------------------------------------------------------------------------------

    function getParticleSystemProperty( nodeID, propertyName, propertyValue ) {

        var value = undefined;
        var obj, color;
        var node = this.state.nodes[nodeID];
        if ( node && node.glgeObject ) {
            var ps = node.glgeObject;
            switch ( propertyName ) {
                case "numberParticles":
                    if ( ps.getNumParticles )
                        value = ps.getNumParticles();
                    break;
                case "maxLifeTime":
                    if ( ps.getMaxLifeTime )
                        value = ps.getMaxLifeTime();
                    break;
                case "minLifeTime":
                    if ( ps.getMinLifeTime )
                    value = ps.getMinLifeTime();
                    break;
                case "startSize":
                    if ( ps.getStartSize )
                        value = ps.getStartSize();
                    break;
                case "endSize":
                    if ( ps.getEndSize )
                        value = ps.getEndSize();
                    break;
                case "loop":
                    if ( ps.getLoop )
                        value = ps.getLoop();
                    break;
                case "maxVelocity":
                    if ( ps.getMaxVelocity ) {
                        obj = ps.getMaxVelocity();    
                        value = [ obj.x, obj.y, obj.z ]; 
                    }
                    break;            
                case "minVelocity":
                    if ( ps.getMinVelocity ) {
                        obj = ps.getMinVelocity();
                        value = [ obj.x, obj.y, obj.z ];
                    }
                    break;    
                case "maxStartAcceleration":
                    if ( ps.getMaxStartAccelertaion ) {
                        obj = ps.getMaxStartAccelertaion();
                        value = [ obj.x, obj.y, obj.z ];
                    }
                    break;
                case "maxEndAcceleration":
                    if ( ps.getMaxEndAccelertaion ) {
                       obj = ps.getMaxEndAccelertaion();
                       value = [ obj.x, obj.y, obj.z ];
                    }
                    break;
                case "minStartAcceleration":
                    if ( ps.getMinStartAccelertaion ) {
                        obj = ps.getMinStartAccelertaion();
                        value = [ obj.x, obj.y, obj.z ];
                    }
                    break;
                case "minEndAcceleration":
                    if ( ps.getMinEndAccelertaion )
                        obj = ps.getMinEndAccelertaion();
                        value = [ obj.x, obj.y, obj.z ]; 
                    break;
                case "startColor":
                    if ( ps.getStartColor ) {
                        color = vwfColor.call( this, ps.getStartColor() );
                        value = color.toString();
                    } else { value = undefined; }
                    break;
                case "endColor":
                    if ( ps.getEndColor ){
                        color = vwfColor.call( this, ps.getEndColor() );
                        value = color.toString();
                    } else { value = undefined; }
                    break;
                case "image":
                    if ( ps.getImage )
                        value = ps.getImage();
                    break;
                default:
                    value = undefined;
                    break;
            }
        }
        return value;

    }

    // -- getObjectProperty ------------------------------------------------------------------------------

    function getObjectProperty( nodeID, propertyName, propertyValue ) {

        var node = this.state.nodes[ nodeID ]; 
        var value = undefined;
        var txtr, mat;

        switch ( propertyName ) {
            case "mesh": {
                    if ( node.glgeObject && node.glgeObject && node.glgeObject.getMesh ) {
                        value = node.glgeObject.getMesh();
                    }
                }
                break;
        }

        return value;
    }

    // -- getMaterialProperty ------------------------------------------------------------------------------

    function getMaterialProperty( nodeID, propertyName, propertyValue ) {
        var node = this.state.nodes[ nodeID ]; 
        var value = undefined;
        var txtr, mat, obj, color;

        if ( node.glgeMaterial && node.glgeMaterial.textures ) {
            mat = node.glgeMaterial;
            txtr = node.glgeMaterial.textures[0];
        } else if ( node.glgeObject && node.glgeObject.material ) {
            mat = node.glgeObject.material; 
            txtr = node.glgeObject.material.textures[0];
        }   

        switch ( propertyName ) {
            case "texture": 
                if ( txtr ) {
                    if ( txtr instanceof GLGE.TextureCanvas ) {
                        var cv = txtr.getCanvas();
                        value = "canvas";
                        if ( cv ) {
                            value = cv.getAttribute( 'id' );
                        }  
                    } else if ( txtr instanceof GLGE.TextureCamera ) {
                        var cam = txtr.getCamera();
                        if ( cam === this.state.cameraInUse ) {
                            value = "activeCamera"; 
                        } else {
                            value = getObjectID.call( this, cam, false, false );
                        }
                    } else {
                        if ( txtr.getSrc ) {
                            value = txtr.getSrc();
                        }
                    }
                }
                break;

            case "color":
                if ( mat ) { 
                    color = vwfColor.call( this, mat.getColor() );
                    value = color.toString();
                } else { value = undefined; }
                break;
                
            case "ambient":
                if ( mat ) { 
                    color = vwfColor.call( this, mat.getAmbient() );
                    value = color.toString();               
                } else { value = undefined; }
                break;
            case "specColor":
                if ( mat ) { 
                    color = vwfColor.call( this, mat.getSpecularColor() );
                    value = color.toString();  
                } else { value = undefined; }
                break;
            case "shininess":
                if ( mat ) { value = mat.getShininess(); } 
                break;  
            case "reflect":
                if ( mat ) { value = mat.getReflectivity(); } 
                break;
            case "specular":
                if ( mat ) { value = mat.getSpecular(); } 
                break;
            case "emit":
                if ( mat ) { value = mat.getEmit(); } 
                break; 
            case "alpha":
                if ( mat ) { value = mat.getAlpha(); } 
                break;                    
            case "binaryAlpha":
                if ( mat ) { value = mat.getBinaryAlpha(); } 
                break;  
        }

        return value;
    }

    // -- getLightProperty ------------------------------------------------------------------------------

    function getLightProperty( nodeID, propertyName, propertyValue ) {

        var value = undefined;
        var node = this.state.nodes[ nodeID ];
        var temp, color;

        switch( propertyName ) {

            case "lightType":
                switch ( node.glgeObject.getType() ) {
                    case GLGE.L_POINT:
                        value = "point";
                        break;
                    case GLGE.L_DIR:
                        value = "directional";
                        break;
                    case GLGE.L_SPOT:
                        value = "spot";
                        break;
                }
                break;

            case "glge-constantAttenuation":
                temp = node.glgeObject.getAttenuation();
                value = temp.constant;
                break;

            case "glge-linearAttenuation":
                temp = node.glgeObject.getAttenuation();
                value = temp.linear;
                break;

            case "glge-quadraticAttenuation":
                temp = node.glgeObject.getAttenuation();
                value = temp.quadratic;
                break;

            case "glge-spotCosCutOff":
                value = node.glgeObject.getSpotCosCutOff();
                break;

            case "glge-spotCutOff":
                node.glgeObject.getSpotCutOff( propertyValue );
                break;

            case "spotExponent":
                value = node.glgeObject.getSpotExponent();
                break;

            case "color":
                color = vwfColor.call( this, node.glgeObject.getColor() );
                value = color.toString();  
                break;

            case "diffuse":
                value = node.glgeObject.diffuse; // no getDiffuse() in GLGE 0.7
                break;

            case "glge-specular":
                value = node.glgeObject.specular; // no getSpecular() in GLGE 0.7
                break;

            case "glge-samples":
                value = node.glgeObject.getShadowSamples();
                break;

            case "glge-softness":
                value = node.glgeObject.getShadowSoftness();
                break;

            case "glge-bufferHeight":
                value = node.glgeObject.getBufferHeight();
                break;

            case "glge-bufferWidth":
                value = node.glgeObject.getBufferWidth();
                break;

            case "glge-shadowBias":
                value = node.glgeObject.getShadowBias();
                break;

            case "distance":
                value = node.glgeObject.getDistance();
                break;

            case "castShadows":
                value = node.glgeObject.getCastShadows();
                break;

            case "glge-spotSoftness":
                value = node.glgeObject.getSpotSoftness();
                break;
                
            case "glge-spotSoftnessDistance":
                value = node.glgeObject.getSpotSoftDistance();
                break;                

            case "glge-cascadeLevels":
                value = node.glgeObject.getCascadeLevels();
                break;

            default:
                value = undefined;
                break;
        }
        return value;    
    
    }

    // -- getCameraProperty ------------------------------------------------------------------------------

    function getCameraProperty(nodeID, propertyName, propertyValue) {

        var node = this.state.nodes[nodeID];
        var value = undefined;
        switch( propertyName ) {
            case "cameraType":
                switch ( node.glgeObject.getType() ) {
                    case GLGE.C_PERSPECTIVE:
                        value = "perspective";
                        break;
                    case GLGE.C_ORTHO:
                        value = "orthographic";
                        break;
                }
                break;
            case "far":
                value = node.glgeObject.getFar();
                break;
            case "near":
                value = node.glgeObject.getNear();
                break;
            case "fovy":
                value = node.glgeObject.getFovY();
                break;            
            case "aspect":
                value = node.glgeObject.getAspect();
                break;            
            case "orthoscale":
                value = node.glgeObject.getOrthoScale();
                break;
            default:
                value = undefined;
                break;
        }
        return value;

    }

    // -- findGlgeObject ------------------------------------------------------------------------------

    function findGlgeObject( objName, type, prototypes ) {

        var obj = undefined;
        var assetObj = undefined;
        var glgeObjName = "";

        for ( var key in GLGE.Assets.assets ) {
            assetObj = GLGE.Assets.assets[key];
            if ( assetObj ) {
                glgeObjName = name( assetObj );
                if ( glgeObjName == objName ) {
                    switch ( type ) {
                        case "http://vwf.example.com/mesh.vwf":
                            if ( assetObj.constructor == GLGE.Object )
                                obj = assetObj;
                            break;
                        case "http://vwf.example.com/node3.vwf":
                            if ( ( assetObj.constructor == GLGE.Group ) || ( assetObj.constructor == GLGE.Object ) )
                                obj = assetObj;
                            break;
                        case "http://vwf.example.com/light.vwf":
                            if ( assetObj.constructor == GLGE.Light )
                                obj = assetObj;
                            break;
                        case "http://vwf.example.com/camera.vwf":
                            if ( assetObj.constructor == GLGE.Camera )
                                obj = assetObj;
                            break;
                        case "http://vwf.example.com/scene.vwf":
                            if ( assetObj.constructor == GLGE.Scene )
                                obj = assetObj;
                            break;
                        case "http://vwf.example.com/particleSystem.vwf":
                            if ( assetObj.constructor == GLGE.ParticleSystem )
                                obj = assetObj;
                            break;
                    }
                    if ( obj ) break;
                }
            }
        }

        return obj;

    }

    // -- setActiveCamera ------------------------------------------------------------------------------

    function setActiveCamera( sceneNode, nodeID ) {

        if ( this.state.nodes[ nodeID ] ) {
            var glgeCamera = this.state.nodes[ nodeID ].glgeObject;
            if ( glgeCamera ) {
                setAspect.call( this, nodeID );
                this.state.cameraInUse = glgeCamera;
                sceneNode.glgeScene.setCamera( glgeCamera );
            }
        }

    }

    // -- setAspect ------------------------------------------------------------------------------

    function setAspect( nodeID ) {

        if ( this.state.nodes[ nodeID ] ) {
            var glgeCamera = this.state.nodes[ nodeID ].glgeObject;
            if ( glgeCamera ) {
                var canvas = document.getElementById( this.state.sceneRootID );
                glgeCamera.setAspect( canvas.width / canvas.height );
            }
        }
    }


    // -- createLight ------------------------------------------------------------------------------

    function createLight( nodeID, childID, childName ) {

        var child = this.state.nodes[childID];
        if ( child ) {
            child.glgeObject = new GLGE.Light();
            child.glgeObject.name = childName;
            child.name = childName;
            child.uid = child.glgeObject.uid;
            addGlgeChild.call( this, nodeID, childID );
        } 
               
    }

   // -- createCamera ------------------------------------------------------------------------------

    function createCamera( nodeID, childID, childName ) {

        var sceneNode = this.state.scenes[nodeID]
        var parent = sceneNode ? sceneNode : this.state.nodes[nodeID]
        if ( !sceneNode ) sceneNode = parent.glgeScene;
        if ( sceneNode && parent ) {
            var child = this.state.nodes[childID];
            if ( child ) {
                var cam = new GLGE.Camera();;
                initCamera.call( this, cam );
                var glgeParent = parent.glgeObject;
                if ( glgeParent && ( glgeParent instanceof GLGE.Scene || glgeParent instanceof GLGE.Group )) {
                    glgeParent.addObject( cam );
                }

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

    }

   // -- createParticleSystem ------------------------------------------------------------------------------

    function createParticleSystem( nodeID, childID, childName ) {
        var glgeParent = undefined;
        var parentNode = this.state.nodes[ nodeID ];
        if ( parentNode && parentNode.glgeObject && parentNode.glgeObject.getChildren ) {
            glgeParent = parentNode.glgeObject;   
        }
        if ( !glgeParent ) {
            parentNode = this.state.scenes[ this.state.sceneRootID ];
            glgeParent = parentNode.glgeScene; 
        }
        if ( glgeParent ) {
            var ps = new GLGE.ParticleSystem();
            this.state.nodes[ childID ].glgeObject = ps; 
            glgeParent.addObject( ps );
        }
    }

   // -- initCamera ------------------------------------------------------------------------------

    function initCamera( glgeCamera ) {

        if ( glgeCamera ) {
            glgeCamera.setLoc( 0, 0, 0 );
            glgeCamera.setRot( Math.PI/2, 0, 0 ); // rotate to look at +Y, VWF's default orientation
            glgeCamera.setType( GLGE.C_PERSPECTIVE );
            glgeCamera.setRotOrder( GLGE.ROT_XZY );
        }
                
    }

   // -- getLocalBoundingBox ------------------------------------------------------------------------------

    function getLocalBoundingBox( glgeObject ) {

        var bBox = { xMin: Number.MAX_VALUE, xMax: -Number.MAX_VALUE,
                     yMin: Number.MAX_VALUE, yMax: -Number.MAX_VALUE,
                     zMin: Number.MAX_VALUE, zMax: -Number.MAX_VALUE };

        var glgeObjectList = [];
        findAllGlgeObjects.call( this, glgeObject, glgeObjectList );

        for ( var j = 0; j < glgeObjectList.length; j++ ) {
            var vertices = getMeshVertices.call( this, glgeObjectList[j] );
            for ( var i = 0; i < vertices.length; i++ ) {
                if ( vertices[i][0] < bBox.xMin )
                    bBox.xMin = vertices[i][0];
                if ( vertices[i][0] > bBox.xMax )
                    bBox.xMax = vertices[i][0];
                if ( vertices[i][1] < bBox.yMin )
                    bBox.yMin = vertices[i][1];
                if ( vertices[i][1] > bBox.yMax )
                    bBox.yMax = vertices[i][1];
                if ( vertices[i][2] < bBox.zMin )
                    bBox.zMin = vertices[i][2];
                if ( vertices[i][2] > bBox.zMax )
                    bBox.zMax = vertices[i][2];
            }
        }

        return bBox; 
           
    }

   // -- getCenterOffset ------------------------------------------------------------------------------

    function getCenterOffset( glgeObject ) {

        var offset = [ 0, 0, 0 ];
        if ( glgeObject ) {
            var bBox = getLocalBoundingBox.call( this, glgeObject )
            offset[0] = ( bBox.xMax + bBox.xMin ) * 0.50;
            offset[1] = ( bBox.yMax + bBox.yMin ) * 0.50;
            offset[2] = ( bBox.zMax + bBox.zMin ) * 0.50;
        }
        return offset;

    }

   // -- getNodeVertices ------------------------------------------------------------------------------

    function getNodeVertices( nodeID ) {

        if ( this.state.nodes[nodeID] ) {
            return getMeshVertices.call( this, this.state.nodes[nodeID] );
        }
        return undefined;

    }

   // -- getMeshVertices ------------------------------------------------------------------------------

    function getMeshVertices( glgeObject ) {

        var vertices = [];
        var glgeMesh;
        if ( glgeObject ) {
            if ( glgeObject.getChildren && !glgeObject.getMesh ) {
                var objects = []; 
                findAllGlgeObjects.call( this, glgeObject, objects );
                for ( var j = 0; j < objects.length; j++ ) {
                    if ( objects[j].getMesh ) {
                        var pos = objects[j].getMesh().positions;
                        if ( pos ) {
                            for ( var i = 0; i < pos.length; i = i + 3 ) {
                                vertices.push([pos[i], pos[i + 1], pos[i + 2]]);
                            }
                        }
                    }                    
                }
            } else if ( glgeObject.getMesh && glgeObject.getMesh() ) {
                glgeMesh = glgeObject.getMesh();
            } else if ( glgeObject.constructor == GLGE.Mesh ) {
                glgeMesh = glgeObject;
            }

            if ( glgeMesh ) {
                var pos = glgeMesh.positions;
                if ( pos ) {
                    for ( var i = 0; i < pos.length; i = i + 3 ) {
                        vertices.push( [pos[i], pos[i + 1], pos[i + 2]] );
                    }
                }            
            }
        }    
        return vertices;

    }

   // -- getNodeVertexIndices ------------------------------------------------------------------------------

    function getNodeVertexIndices( nodeID ) {

        if ( this.state.nodes[nodeID] ) {
            return getMeshVertexIndices.call( this, this.state.nodes[nodeID] );
        }
        return undefined;

    }

   // -- findAllGlgeObjects ------------------------------------------------------------------------------

    function findAllGlgeObjects( glgeNode, objList ) {

        if ( glgeNode ) {
            if ( glgeNode.constructor == GLGE.Object )
                objList.push( glgeNode );

            if ( glgeNode.getChildren ) {
                var nodeChildren = glgeNode.getChildren();
                for (var i = 0; i < nodeChildren.length; i++) {
                    findAllGlgeObjects.call( this, nodeChildren[i], objList );
                }
            }
        }

    }

   // -- findAllMeshes ------------------------------------------------------------------------------

    function findAllMeshes( glgeNode ) {

        var meshes = [];
        var objs = [];
        findAllGlgeObjects.call( this, glgeNode, objs );
        for ( var i = 0; i < objs.length; i++ ){
            if ( objs[i].getMesh && objs[i].getMesh() ) {
                meshes.push( objs[i].getMesh() );
            }       
        }
        return meshes;

    }

   // -- getGlgeObject ------------------------------------------------------------------------------

    function getGlgeObject( id ) {
        var glgeObj = undefined;
        var node = this.state.nodes[ id ];
        if ( !node && this.state.scenes[ id ] ) {
            node = this.state.scenes[ id ];
        }
        if ( node ) {
            glgeObj = node.glgeObject;
            if ( !glgeObj && node.glgeScene ) {
                glgeObj = node.glgeScene;
            }
        }
        return glgeObj;
    }

   // -- getObjectID ------------------------------------------------------------------------------

    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) );
            }
            jQuery.each( this.state.nodes, function (nodeID, node) {
                if ( node.glgeObject == 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 nameGlge(obj) {
        return obj.colladaName || obj.colladaId || obj.name || obj.id || obj.uid || "";
    }

    function name(obj) {
        return obj.colladaName || obj.colladaId || obj.name || obj.id || "";
    }

    function path(obj) {
        var sOut = "";
        var sName = "";

        while (obj && obj.parent) {
            if (sOut == "")
                sOut = name(obj);
            else
                sOut = name(obj) + "." + sOut;
            obj = obj.parent;
        }
        return sOut;
    }

    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 getMeshVertexIndices( glgeObject ) {
        var vertexIndices = [];
        var mesh;
        if ( glgeObject ) {
            if ( glgeObject.getMesh && glgeObject.getMesh() ) {
                mesh = glgeObject.getMesh();
            } else if ( glgeObject.constructor == GLGE.Mesh ) {
                mesh = glgeObject;
            }

            if ( mesh && mesh.faces ) {
                var faces = mesh.faces.data;
                if ( faces ) {
                    for (var i = 0; i < faces.length; i = i + 3) {
                        vertexIndices.push( [faces[i], faces[i + 1], faces[i + 2]] );
                    }
                }
            }
        }

        return vertexIndices;
    }

    // get the list of types this ID extends

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

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

    function isPrototype( nodeID, childID ) {
        var ptID;
        if ( ( nodeID == 0 && childID != this.kernel.application() ) || this.state.prototypes[ nodeID ] !== undefined ) {
            if ( nodeID != 0 || childID != this.kernel.application() ) {
                ptID = nodeID ? nodeID : childID;
                if ( this.state.prototypes[ ptID ] !== undefined ) {
                    ptID = childID;
                }
                return ptID;
            } 
        }
        return undefined;
    }


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

        return foundGlge;
    }

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

        return foundGlge;
    }

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

        return foundGlge;
    }

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

        return foundGlge;
    }

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

        return foundGlge;
    }

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

        return foundGlge;
    }
    // Search a GLGE.Scene for a child with the given name.

    function glgeSceneChild(glgeScene, childName) {

        var childToReturn = jQuery.grep(glgeScene.children || [], function (glgeChild) {
            return (glgeChild.name || glgeChild.id || glgeChild.sourceURL || "") == childName;
        }).shift();

        //this.logger.info("      glgeSceneChild( " + childName + " ) returns " + childToReturn);
        return childToReturn;

    }

    // Search a GLGE.Object, GLGE.Collada, GLGE.Light for a child with the given name.  TODO: really, it's anything with children[]; could be the same as glgeSceneChild().

    function glgeObjectChild( glgeObject, childName, childType, prototypes, recursive ) {
        
        var childToReturn = jQuery.grep( glgeObject.children || [], function ( glgeChild ) {
            return (glgeChild.colladaName || glgeChild.colladaId || glgeChild.name || glgeChild.id || "") == childName;
        }).shift();

        // to slow, and may bind to the incorrect object
        if ( recursive && childToReturn === undefined ) {
            if ( glgeObject.children ) {
            for ( var i = 0; i < glgeObject.children.length && childToReturn === undefined; i++ )
                childToReturn = glgeObjectChild.call( this, glgeObject.children[i], childName, childType, prototypes, recursive );
            }
        }
        return childToReturn;

    }

    function findMaterial( parentID, materialName, materialNode ) {

        var materialStringIndex, materialIndex;
        var parentNode = this.state.nodes[ parentID ];

        // the material name is a combination of the following information
        // groupParentName + 'Material' + indexOfTheMultiMaterial

        if ( parentNode ) {
            if ( parentNode.glgeObject ) {
                if ( parentNode.glgeObject.constructor == GLGE.Object ) {
                    if ( materialName == "material" ) {
                        materialNode.glgeObject = parentNode.glgeObject;
                        materialNode.glgeMaterial = materialNode.glgeObject.getMaterial();
                    }
                } else {
                    var index = materialName.substr( 8 );
                    var glgeObjs = [];
                    if ( index == "" ) index = 0;

                    findAllGlgeObjects.call( this, parentNode.glgeObject, glgeObjs );

                    for ( var i = 1; i < glgeObjs.length; i++ ) {
                        console.info( "WARNING:  passing other materials .......harding to index to 0 " );
                    }

                    if ( glgeObjs && glgeObjs.length && index < glgeObjs.length ) {
                        materialNode.glgeObject = glgeObjs[index];
                        materialNode.glgeMaterial = glgeObjs[index].getMaterial();
                    }
                }
            }
        }

    }

    function findMesh( parentID, meshName, meshNode ) {

        var materialStringIndex, materialIndex, childName;
        var parentNode = this.state.nodes[ parentID ];

        // the material name is a combination of the following information
        // groupParentName + 'Material' + indexOfTheMultiMaterial

        if ( parentNode ) {
            materialStringIndex = materialName.lastIndexOf( "Material" );
            materialIndex = Number( materialName.substr( materialStringIndex + 8 ) ) - 1;
            childName = materialName.substr( 0, materialStringIndex );

            if ( parentNode.glgeObject ) {
                var glgeObjs = [];
                var found = false;

                findAllGlgeObjects.call( this, parentNode.glgeObject, glgeObjs );

                if ( glgeObjs && glgeObjs.length ) {
                    for ( var i = 0; i < glgeObjs.length && !found; i++ ) {
                        if ( name( glgeObjs[i].parent ) == childName ) {
                            materialNode.glgeObject = glgeObjs[i];
                            materialNode.glgeMaterial = glgeObjs[i].getMaterial( materialIndex );
                            found = true;                        
                        }                   
                    }
                } else if ( parentNode.glgeObject.children.length == 1 && parentNode.glgeObject.children[0].constructor == GLGE.Object ) {

                    var glgeChild = parentNode.glgeObject.children[0];
                    materialNode.glgeObject = glgeChild;
                    materialNode.glgeMaterial = glgeChild.getMaterial( materialIndex );
                
                    if ( !( materialNode.glgeMaterial ) && ( childNode.glgeObject ) ) {
                        materialNode.glgeMaterial = childNode.glgeObject.getMaterial();
                    }
                }
            }
        }

    }

    function createMesh( node ) {
        if ( !node.glgeParent ) {
            node.glgeParent = this.state.nodes[ node.parentID ];    
        }
        
        if ( node.glgeParent ) {
            node.glgeObject = new GLGE.Group();
            node.glgeParent.addObject( node.glgeObject );
            var obj = new GLGE.Object();
            obj.setMaterial( new GLGE.Material() );
            obj.setMesh( new GLGE.Mesh() );
            node.glgeObject.addObject( obj );
        }        
    }

    function defineMesh( def, node ) {
        if ( node.glgeObject ) {
            var obj = new GLGE.Object();
            var mat = new GLGE.Material();
            var mesh = new GLGE.Mesh();

            if ( def.color ) {
                mat.setColor( def.color );
            }
            obj.setMaterial( mat );

            if ( def.positions )
                mesh.setPositions( def.positions );
            if ( def.normals )
                mesh.setNormals( def.normals );
            if ( def.uv1 )
                mesh.setUV( def.uv1 );
            if ( def.faces )
                mesh.setFaces( def.faces );

            obj.setMesh( mesh );
            node.glgeObject.addObject( obj );
        }        
    }
			
	function BuildBox(size,offset,color)
	{
		
		var hx = size[0]/2;
		var hy = size[1]/2;
		var hz = size[2]/2;
		
		var ox = offset[0];
		var oy = offset[1];
		var oz = offset[2];
		
		var planemesh = new GLGE.Mesh();
		var planeobj = new GLGE.Object();
		planeobj.setMesh(planemesh);
		
		var positions = [
		hx + ox,hy + oy,hz + oz, 
		hx + ox,hy + oy,-hz + oz, 
		hx + ox,-hy + oy,hz + oz, 
		hx + ox,-hy + oy,-hz + oz,
		-hx + ox,hy + oy,hz + oz,
		-hx + ox,hy + oy,-hz + oz, 
		-hx + ox,-hy + oy,hz + oz, 
		-hx + ox,-hy + oy,-hz + oz
		];
		
		var colors = [];
		for(var i = 0; i < (positions.length/3); i++)
		{	colors.push(color[0]);
			colors.push(color[1]);
			colors.push(color[2]);
			colors.push(color[3]);
		}
		
		var indexes = [0,2,6,6,4,0,1,3,7,7,5,1,0,1,3,3,2,0,4,5,7,7,6,4,0,1,5,5,4,0,2,3,7,7,6,2];
		
		planemesh.setPositions(positions);
		planemesh.setVertexColors(colors);
		planemesh.setFaces(indexes);
		
		var mat = new GLGE.Material();
		
		planeobj.setPickable(true);
		planeobj.setMaterial(mat);
		mat.setVertexColorMode(GLGE.VC_MUL);
		//mat.setColor(color);
		//mat.setEmit(color);
		//mat.setShadeless(true);
		//mat.setAmbient([.5,.5,.5,1]);
		return planeobj;
	}
	function BuildAxis()
	{	
		var red = [1,.55,.55,1];
		var green = [.55,1,.55,1];
		var blue = [.55,.55,1,1];
		
		var MoveGizmo = new GLGE.Group();
		MoveGizmo.addChild(BuildBox([1,.030,.030],[.5,0,0],red));               //move x
		MoveGizmo.addChild(BuildBox([.030,1,.030],[0,.5,0],green));//move y
		MoveGizmo.addChild(BuildBox([.030,.030,1],[0,0,.5],blue));//move z
		return MoveGizmo;
	}
    function addGlgeChild( parentID, childID ) {
        
        var glgeParent;
        var parent = this.state.nodes[ parentID ];
        if ( !parent && this.state.scenes[ parentID ] ) {
            parent = this.state.scenes[ parentID ];
            glgeParent = parent.glgeScene;
        } else {
            glgeParent = parent.glgeObject;
        }
            
        if ( glgeParent && this.state.nodes[ childID ]) {
            var child = this.state.nodes[ childID ];

            if ( child.glgeObject ) {
                glgeParent.addChild( child.glgeObject );
            }
        }
    }

    function isMovieFileRef( fileName ) {
        var isMovieFile = false;
        if ( fileName && fileName != "" ){
            var fileSplit = fileName.split( "." );
            var fileSLen = fileSplit ? fileSplit.length : 0;
            if ( fileSLen > 0 ) {
                switch ( fileSplit[ fileSLen-1 ] ) {
                    case "ogv":
                        isMovieFile = true;
                        break;
                }
            }             
        }
        return isMovieFile;
    }

    function isImageFileRef( fileName ) {
        var isImageFile = false;
        if ( fileName && fileName != "" ){
            var fileSplit = fileName.split( "." );
            var fileSLen = fileSplit ? fileSplit.length : 0;
            if ( fileSLen > 0 ) {
                switch ( fileSplit[ fileSLen-1 ] ) {
                    case "png":
                    case "jpeg":
                    case "bmp":
                    case "gif":
                    case "tif":
                    case "tiff":
                    case "jpg":
                    case "tga":
                        isImageFile = true;
                        break;
                }
            }             
        }
        return isImageFile;
    }
    function isVideoFileRef( fileName ) {
        var isVideoFile = false;
        if ( fileName && fileName != "" ){
            var fileSplit = fileName.split( "." );
            var fileSLen = fileSplit ? fileSplit.length : 0;
            if ( fileSLen > 0 ) {
                switch ( fileSplit[ fileSLen-1 ] ) {
                    case "ogg":
                    case "mjeg":
                    case "mpeg1":
                    case "mpeg":
                    case "avi":
                    case "mpg":
                    case "mp2":
                    case "m1v":
                        isVideoFile = true;
                        break;
                }
            }             
        }
        return isVideoFile;
    }  

    function findCamera( camNode ) {
        var cam = undefined;
        if ( camNode ) {
            if ( camNode.glgeObject && camNode.glgeObject instanceof GLGE.Camera ){
                cam = camNode.glgeObject;
            } else if ( camNode.glgeObject instanceof GLGE.Group ) {
                cam = new GLGE.Camera();
                camNode.glgeObject.addObject( cam );
            }
        }
        return cam;  
    }

} );