- ///@INFO: BASE
- //****************************************************************************
-
- /**
- * The SceneNode class represents and object in the scene
- * Is the base class for all objects in the scene as meshes, lights, cameras, and so
- *
- * @class SceneNode
- * @param {String} name the name for this node (otherwise a random one is computed)
- * @constructor
- */
-
- function SceneNode( name )
- {
- if(name && name.constructor !== String)
- {
- name = null;
- console.warn("SceneNode constructor first parameter must be a String with the name");
- }
-
- //Generic identifying info
- this._name = name || ("node_" + (Math.random() * 10000).toFixed(0)); //generate random number
- this._uid = LS.generateUId("NODE-");
- this._classList = {}; //to store classes
- this.layers = 3|0; //32 bits for layers (force to int)
- this.node_type = null; //used to store a string defining the node info
-
- //more generic info
- this._prefab = null;
- this._material = null;
-
- //from Componentcontainer
- this._components = []; //used for logic actions
- this._missing_components = null; //used to store state of component that couldnt be created
-
- //from CompositePattern
- this._parentNode = null;
- this._children = null;
- this._in_tree = null;
- this._instances = []; //render instances
-
- //flags
- this.flags = {
- visible: true,
- is_static: false,
- selectable: true,
- locked: false
- };
-
- this.init(false,true);
-
- /** Fired here (from Transform) when the node transform changes
- * @event transformChanged
- */
- }
-
- SceneNode.prototype.init = function( keep_components, keep_info )
- {
- if(!keep_info)
- {
- this.layers = 3|0; //32 bits for layers (force to int)
- this._name = name || ("node_" + (Math.random() * 10000).toFixed(0)); //generate random number
- this._uid = LS.generateUId("NODE-");
- this._classList = {};
-
- //material
- this._material = null;
- this.extra = {}; //for extra info
- this.node_type = null;
-
- //flags
- this.flags = {
- visible: true,
- is_static: false,
- selectable: true
- };
- }
-
- //Basic components
- if(!keep_components)
- {
- if( this._components && this._components.length )
- console.warn("SceneNode.init() should not be called if it contains components, call clear instead");
- this._components = []; //used for logic actions
- this._missing_components = null;
- this.addComponent( new LS.Transform() );
- }
- }
-
- //get methods from other classes
- LS.extendClass( SceneNode, ComponentContainer ); //container methods
- LS.extendClass( SceneNode, CompositePattern ); //container methods
-
- /**
- * changes the node name
- * @method setName
- * @param {String} new_name the new name
- * @return {Object} returns true if the name changed
- */
-
- Object.defineProperty( SceneNode.prototype, 'name', {
- set: function(name)
- {
- this.setName( name );
- },
- get: function(){
- return this._name;
- },
- enumerable: true
- });
-
- Object.defineProperty( SceneNode.prototype, 'fullname', {
- set: function(name)
- {
- throw("You cannot set fullname, it depends on the parent nodes");
- },
- get: function(){
- return this.getPathName();
- },
- enumerable: false
- });
-
- //Changing the UID has lots of effects (because nodes are indexed by UID in the scene)
- //If you want to catch the event of the uid_change, remember, the previous uid is stored in LS.SceneNode._last_uid_changed (it is not passed in the event)
- Object.defineProperty( SceneNode.prototype, 'uid', {
- set: function(uid)
- {
- if(!uid)
- return;
-
- //valid uid?
- if(uid[0] != LS._uid_prefix)
- {
- console.warn("Invalid UID, renaming it to: " + uid );
- uid = LS._uid_prefix + uid;
- }
-
- //no changes?
- if(uid == this._uid)
- return;
-
- SceneNode._last_uid_changed = this._uid; //hack, in case we want the previous uid of a node
-
- //update scene tree indexing
- if( this._in_tree && this._in_tree._nodes_by_uid[ this.uid ] )
- delete this._in_tree._nodes_by_uid[ this.uid ];
- this._uid = uid;
- if( this._in_tree )
- this._in_tree._nodes_by_uid[ this.uid ] = this;
- //events
- LEvent.trigger( this, "uid_changed", uid );
- if(this._in_tree)
- LEvent.trigger( this._in_tree, "node_uid_changed", this );
- },
- get: function(){
- return this._uid;
- },
- enumerable: true
- });
-
-
- Object.defineProperty( SceneNode.prototype, 'visible', {
- set: function(v)
- {
- this.flags.visible = v;
- },
- get: function(){
- return this.flags.visible;
- },
- enumerable: true
- });
-
- Object.defineProperty( SceneNode.prototype, 'is_static', {
- set: function(v)
- {
- this.flags.is_static = v;
- },
- get: function(){
- return this.flags.is_static;
- },
- enumerable: true
- });
-
- Object.defineProperty( SceneNode.prototype, 'material', {
- set: function(v)
- {
- if( this._material == v )
- return;
-
- this._material = v;
- if(v)
- {
- if(v.constructor === String)
- return;
- if(v._root && v._root != this) //has root and its not me
- console.warn( "Cannot assign a material of one SceneNode to another, you must clone it or register it" )
- else
- v._root = this; //link
- }
- LEvent.trigger( this, "materialChanged" );
- },
- get: function(){
- return this._material;
- },
- enumerable: true
- });
-
- Object.defineProperty( SceneNode.prototype, 'prefab', {
- set: function(name)
- {
- this._prefab = name;
- if(!this._prefab)
- return;
- var prefab = LS.RM.getResource(name);
- var that = this;
- if(prefab)
- this.reloadFromPrefab();
- else
- LS.ResourcesManager.load( name, function(){
- that.reloadFromPrefab();
- });
- },
- get: function(){
- return this._prefab;
- },
- enumerable: true
- });
-
- SceneNode.prototype.clear = function()
- {
- this.removeAllComponents();
- this.removeAllChildren();
- this.init();
- }
-
- SceneNode.prototype.setName = function(new_name)
- {
- if(this._name == new_name)
- return true; //no changes
-
- //check that the name is valid (doesnt have invalid characters)
- if(!LS.validateName(new_name))
- {
- console.warn("invalid name for node: " + new_name );
- //new_name = new_name.replace(/[^a-z0-9\.\-]/gi,"_");
- return false;
- }
-
- var scene = this._in_tree;
- if(!scene)
- {
- this._name = new_name;
- return true;
- }
-
- //remove old link
- if( this._name )
- delete scene._nodes_by_name[ this._name ];
-
- //assign name
- this._name = new_name;
-
- //we already have another node with this name
- if( new_name && !scene._nodes_by_name[ new_name ] )
- scene._nodes_by_name[ this._name ] = this;
-
- /**
- * Node changed name
- *
- * @event name_changed
- * @param {String} new_name
- */
- LEvent.trigger( this, "name_changed", new_name );
- if(scene)
- LEvent.trigger( scene, "node_name_changed", this );
- return true;
- }
-
- Object.defineProperty( SceneNode.prototype, 'classList', {
- get: function() { return this._classList },
- set: function(v) {},
- enumerable: false
- });
-
- /**
- * @property className {String}
- */
- Object.defineProperty( SceneNode.prototype, 'className', {
- get: function() {
- var keys = null;
- if(Object.keys)
- keys = Object.keys(this._classList);
- else
- {
- keys = [];
- for(var k in this._classList)
- keys.push(k);
- }
- return keys.join(" ");
- },
- set: function(v) {
- this._classList = {};
- if(!v)
- return;
- var t = v.split(" ");
- for(var i in t)
- this._classList[ t[i] ] = true;
- },
- enumerable: true
- });
-
- /**
- * Destroys this node
- * @method destroy
- * @param {number} time [optional] time in seconds to wait till destroying the node
- **/
- SceneNode.prototype.destroy = function( time )
- {
- if(time && time.constructor === Number && time > 0)
- {
- setTimeout( this.destroy.bind(this,0), time * 0.001 );
- return;
- }
-
- LEvent.trigger( this, "destroy" );
- this.removeAllComponents();
- if(this.children)
- while(this.children.length)
- this.children[0].destroy();
- if(this._parentNode)
- this._parentNode.removeChild( this );
- }
-
- /**
- * Returns the locator string of this node
- * @method getLocator
- * @param {string} property_name [optional] you can pass the name of a property in this node to get the locator of that one
- * @return {String} the locator string of this node
- **/
- SceneNode.prototype.getLocator = function( property_name )
- {
- if(!property_name)
- return this.uid;
- return this.uid + "/" + property_name;
- }
-
- /**
- * Returns and object with info about a property given a locator
- * @method getPropertyInfo
- * @param {string} locator
- * @return {Object} object with { node, target, name, value and type }
- **/
- SceneNode.prototype.getPropertyInfo = function( locator )
- {
- var path = locator.split("/");
- return this.getPropertyInfoFromPath(path);
- }
-
- /**
- * Returns and object with info about a property given a locator in path format
- * @method getPropertyInfoFromPath
- * @param {Array} path a locator in path format (split by /)
- * @return {Object} object with { node, target, name, value and type }
- **/
- SceneNode.prototype.getPropertyInfoFromPath = function( path )
- {
- var target = this;
- var varname = path[0];
- var no_slice = false;
-
- if(path.length == 0)
- {
- return {
- node: this,
- target: null,
- name: "",
- value: this,
- type: "node" //node because thats the global type for nodes
- };
- }
- else if(path.length == 1) //compo or //var
- {
- if(path[0][0] == "@")
- {
- target = this.getComponentByUId( path[0] );
- return {
- node: this,
- target: target,
- name: target ? LS.getObjectClassName( target ) : "",
- type: "component",
- value: target
- };
- }
- else if (path[0] == "material")
- {
- target = this.getMaterial();
- return {
- node: this,
- target: target,
- name: target ? LS.getObjectClassName( target ) : "",
- type: "material",
- value: target
- };
- }
-
- var target = this.getComponent( path[0] );
- if(target)
- {
- return {
- node: this,
- target: target,
- name: target ? LS.getObjectClassName( target ) : "",
- type: "component",
- value: target
- };
- }
-
- //special cases for a node
- switch(path[0])
- {
- case "matrix":
- case "x":
- case "y":
- case "z":
- case "position":
- case "rotX":
- case "rotY":
- case "rotZ":
- target = this.transform;
- varname = path[0];
- no_slice = true;
- break;
- default:
- target = this;
- varname = path[0];
- break;
- }
- }
- else if(path.length > 1) //compo/var
- {
- if(path[0][0] == "@")
- {
- varname = path[1];
- target = this.getComponentByUId( path[0] );
- }
- else if (path[0] == "material")
- {
- target = this.getMaterial();
- varname = path[1];
- }
- else if (path[0] == "flags")
- {
- target = this.flags;
- varname = path[1];
- }
- else
- {
- target = this.getComponent( path[0] );
- varname = path[1];
- }
-
- if(!target)
- return null;
- }
- else //�?
- {
- }
-
- if(!target) //unknown target
- return null;
-
- //this was moved to Component.prototype.getPropertyInfoFromPath (if any errors check cases)
- if( target != this && target.getPropertyInfoFromPath ) //avoid weird recursion
- return target.getPropertyInfoFromPath( no_slice ? path : path.slice(1) );
-
- return null;
- }
-
- /**
- * Returns the value of a property given a locator in string format
- * @method getPropertyValue
- * @param {String} locaator
- * @return {*} the value of that property
- **/
- SceneNode.prototype.getPropertyValue = function( locator )
- {
- var path = locator.split("/");
- return this.getPropertyValueFromPath(path);
- }
-
- /**
- * Returns the value of a property given a locator in path format
- * @method getPropertyValueFromPath
- * @param {Array} locator in path format (array)
- * @return {*} the value of that property
- **/
- SceneNode.prototype.getPropertyValueFromPath = function( path )
- {
- var target = this;
- var varname = path[0];
- var no_slice = false;
-
- if(path.length == 0)
- return null
- else if(path.length == 1) //compo or //var
- {
- if(path[0][0] == "@")
- return this.getComponentByUId( path[0] );
- else if (path[0] == "material")
- return this.getMaterial();
- var target = this.getComponent( path[0] );
- if(target)
- return target;
-
- switch(path[0])
- {
- case "matrix":
- case "x":
- case "y":
- case "z":
- case "position":
- case "rotX":
- case "rotY":
- case "rotZ":
- target = this.transform;
- varname = path[0];
- no_slice = true;
- break;
- default:
- target = this;
- varname = path[0];
- break;
- }
- }
- else if(path.length > 1) //compo/var
- {
- if(path[0][0] == "@")
- {
- varname = path[1];
- target = this.getComponentByUId( path[0] );
- }
- else if (path[0] == "material")
- {
- target = this.getMaterial();
- varname = path[1];
- }
- else if (path[0] == "flags")
- {
- target = this.flags;
- varname = path[1];
- }
- else
- {
- target = this.getComponent( path[0] );
- varname = path[1];
- }
-
- if(!target)
- return null;
- }
- else //�?
- {
- }
-
- var v = undefined;
-
- if( target.getPropertyValueFromPath && target != this )
- {
- var r = target.getPropertyValueFromPath( no_slice ? path : path.slice(1) );
- if(r)
- return r;
- }
-
- //to know the value of a property of the given target
- if( target.getPropertyValue && target != this )
- v = target.getPropertyValue( varname );
-
- //special case when the component doesnt specify any locator info but the property referenced does
- //used in TextureFX
- if (v === undefined && path.length > 2 && target[ varname ] && target[ varname ].getPropertyValueFromPath )
- {
- var r = target[ varname ].getPropertyValueFromPath( no_slice ? path.slice(1) : path.slice(2) );
- if(r)
- {
- r.node = this;
- return r;
- }
- }
-
- if(v === undefined && target[ varname ] === undefined )
- return null;
- return v !== undefined ? v : target[ varname ];
- }
-
- /**
- * assigns a value to a property given the locator for that property
- * @method setPropertyValue
- * @param {String} locator
- * @param {*} value
- **/
- SceneNode.prototype.setPropertyValue = function( locator, value )
- {
- var path = locator.split("/");
- return this.setPropertyValueFromPath(path, value, 0);
- }
-
- /**
- * given a locator in path mode (array) and a value, it searches for the corresponding value and applies it
- * @method setPropertyValueFromPath
- * @param {Array} path
- * @param {*} value
- * @param {Number} [optional] offset used to skip the firsst positions in the array
- **/
- SceneNode.prototype.setPropertyValueFromPath = function( path, value, offset )
- {
- offset = offset || 0;
-
- if(this.flags && this.flags.locked)
- return; //lock ignores changes from animations or graphs
-
- var target = null;
- var varname = path[offset];
-
- if(path.length > (offset+1))
- {
- if(path[offset][0] == "@")
- {
- varname = path[offset+1];
- target = this.getComponentByUId( path[offset] );
- }
- else if( path[offset] == "material" )
- {
- target = this.getMaterial();
- varname = path[offset+1];
- }
- else if( path[offset] == "flags" )
- {
- target = this.flags;
- varname = path[offset+1];
- }
- else
- {
- target = this.getComponent( path[offset] );
- varname = path[offset+1];
- }
-
- if(!target)
- return null;
- }
- else { //special cases
- switch ( path[offset] )
- {
- case "matrix": target = this.transform; break;
- case "position":
- case "rotation":
- case "x":
- case "y":
- case "z":
- case "xrotation":
- case "yrotation":
- case "zrotation":
- target = this.transform;
- varname = path[offset];
- break;
- case "translate.X": target = this.transform; varname = "x"; break;
- case "translate.Y": target = this.transform; varname = "y"; break;
- case "translate.Z": target = this.transform; varname = "z"; break;
- case "rotateX.ANGLE": target = this.transform; varname = "pitch"; break;
- case "rotateY.ANGLE": target = this.transform; varname = "yaw"; break;
- case "rotateZ.ANGLE": target = this.transform; varname = "roll"; break;
- default: target = this; //null
- }
- }
-
- if(!target)
- return null;
-
- if(target.setPropertyValueFromPath && target != this)
- if( target.setPropertyValueFromPath( path, value, offset+1 ) === true )
- return target;
-
- if(target.setPropertyValue && target != this)
- if( target.setPropertyValue( varname, value ) === true )
- return target;
-
- if( target[ varname ] === undefined )
- return;
-
- //special case when the component doesnt specify any locator info but the property referenced does
- //used in TextureFX
- if ( path.length > 2 && target[ varname ] && target[ varname ].setPropertyValueFromPath )
- return target[ varname ].setPropertyValueFromPath( path, value, offset+2 );
-
- //disabled because if the vars has a setter it wont be called using the array.set
- //if( target[ varname ] !== null && target[ varname ].set )
- // target[ varname ].set( value );
- //else
- target[ varname ] = value;
-
- return target;
- }
-
- /**
- * Returns all the resources used by this node and its components (you can include the resources from the children too)
- * @method getResources
- * @param {Object} res object where to store the resources used (in "res_name":LS.TYPE format)
- * @param {Boolean} include_children if you want to add also the resources used by the children nodes
- * @return {Object} the same object passed is returned
- **/
- SceneNode.prototype.getResources = function( res, include_children )
- {
- //resources in components
- for(var i in this._components)
- if( this._components[i].getResources )
- this._components[i].getResources( res );
-
- //res in material
- if(this.material)
- {
- if( this.material.constructor === String )
- {
- if(this.material[0] != ":") //not a local material, then its a reference
- {
- res[this.material] = LS.Material;
- }
- }
-
- var mat = this.getMaterial();
- if(mat)
- mat.getResources( res );
- }
-
- //prefab
- if(this.prefab)
- res[ this.prefab ] = LS.Prefab;
-
- //propagate
- if(include_children)
- for(var i in this._children)
- this._children[i].getResources(res, true);
-
- return res;
- }
-
- SceneNode.prototype.getTransform = function() {
- return this.transform;
- }
-
- //Helpers
-
- SceneNode.prototype.getMesh = function( use_lod_mesh ) {
- var mesh = this.mesh;
- var mesh_renderer = this.getComponent( LS.Components.MeshRenderer );
- if(!mesh && mesh_renderer)
- {
- if(use_lod_mesh)
- mesh = mesh_renderer.lod_mesh;
- if(!mesh)
- mesh = mesh_renderer.mesh;
- }
- if(!mesh)
- return null;
- if(mesh.constructor === String)
- return LS.ResourcesManager.meshes[mesh];
- return mesh;
- }
-
- //Light component
- SceneNode.prototype.getLight = function() {
- return this.light;
- }
-
- //Camera component
- SceneNode.prototype.getCamera = function() {
- return this.camera;
- }
-
- /**
- * Allows to load some kind of resource and associate it to this node.
- * It can be for prefabs, meshes, scenes from daes, etc
- * @method load
- * @param {string} url
- * @param {Function} on_complete
- **/
- SceneNode.prototype.load = function( url, on_complete )
- {
- var that = this;
- LS.ResourcesManager.load( url, inner );
- function inner( resource )
- {
- if(!resource)
- return;
- that.assign( resource );
- if(on_complete)
- on_complete();
- }
- }
-
- /**
- * Assign a resource/element inteligently to a node: if it is a mesh it creates a MeshRenderer, if it is a Material it assigns it, if it is an animation creates a PlayAnimation, if it is a prefab assigns the prefab. etc
- * @method assign
- * @param {*} resource the resource to assign (it also accepts a resource filename that has been previously loaded).
- * @param {Function} on_complete
- **/
- SceneNode.prototype.assign = function( item, extra )
- {
- if(!item)
- {
- console.error("assignResource cannot have null as resource");
- return;
- }
-
- //assume is the filename of a resource
- if(item.constructor === String)
- item = LS.ResourcesManager.getResource( item );
-
- if(!item)
- return;
-
- switch( item.constructor )
- {
- case LS.SceneNode:
- this.addChild( item );
- break;
- case LS.Scene:
- var node = this;
- item.loadScripts( null, function(){
- item.loadResources( function(){
- node.addChild( item.root.clone() );
- });
- });
- break;
- case LS.Prefab:
- this.prefab = item.fullpath || item.filename;
- break;
- case GL.Mesh:
- var component = this.getComponent( LS.Components.MeshRenderer );
- if(component)
- component.configure({ mesh: item.fullpath || item.filename });
- else
- this.addComponent( new LS.MeshRenderer({ mesh: mesh_name, submesh_id: submesh_id }) );
- break;
- case LS.Animation:
- var comp = this.getComponent( LS.Components.PlayAnimation );
- if(!comp)
- comp = this.addComponent( new LS.Components.PlayAnimation() );
- comp.animation = item.fullpath || item.filename;
- break;
- case LS.Resource: //generic resource
- var ext = LS.ResourcesManager.getExtension( item.filename );
- if(ext == "js") //scripts
- {
- var comp = this.getComponent( LS.Components.ScriptFromFile );
- if(!comp)
- comp = this.addComponent( new LS.Components.ScriptFromFile() );
- comp.src = item.fullpath || item.filename;
- }
- break;
- default:
- console.error("feature not supported loading this type of resource" , item );
- }
- }
-
- /**
- * Simple way to assign a mesh to a node, it created a MeshRenderer component or reuses and existing one and assigns the mesh
- * @method setMesh
- * @param {string} mesh_name the name of the mesh (path to the file)
- * @param {Number} submesh_id if you want to assign a submesh
- **/
- SceneNode.prototype.setMesh = function(mesh_name, submesh_id)
- {
- var component = this.getComponent( LS.Components.MeshRenderer );
- if(component)
- component.configure({ mesh: mesh_name, submesh_id: submesh_id });
- else
- this.addComponent( new LS.MeshRenderer({ mesh: mesh_name, submesh_id: submesh_id }) );
- }
-
- SceneNode.prototype.getMaterial = function()
- {
- if (!this.material)
- return null;
- if(this.material.constructor === String)
- {
- if( !this._in_tree )
- return null;
- if( this.material[0] == "@" )//uid
- return LS.ResourcesManager.materials_by_uid[ this.material ];
- return LS.ResourcesManager.materials[ this.material ];
- }
- return this.material;
- }
-
- /**
- * Apply prefab info (skipping the root components) to node, so all children will be removed and components lost and overwritten
- * It is called from prefab.applyToNodes when a prefab is loaded in memory
- * @method reloadFromPrefab
- **/
- SceneNode.prototype.reloadFromPrefab = function()
- {
- if(!this.prefab)
- return;
-
- var prefab = LS.ResourcesManager.resources[ this.prefab ];
- if(!prefab)
- return;
-
- if( prefab.constructor !== LS.Prefab )
- throw("prefab must be a LS.Prefab class");
-
- //apply info
- this.removeAllChildren();
- this.init( true, true ); //keep components, keep_info
- var prefab_data = prefab.prefab_data;
-
- //remove all but children info (prefabs overwrite only children info)
- prefab_data = { children: prefab.prefab_data.children };
-
- //uid data is already removed from the prefab
- this.configure( prefab_data );
-
- //load secondary resources
- var resources = this.getResources( {}, true );
- LS.ResourcesManager.loadResources( resources );
-
- LEvent.trigger( this, "prefabReady", prefab );
- }
-
-
- /**
- * Assigns this node to one layer
- * @method setLayer
- * @param {number|String} the index of the layer or the name (according to scene.layer_names)
- * @param {boolean} value
- */
- SceneNode.prototype.setLayer = function( num_or_name, value )
- {
- if( num_or_name == null )
- throw("setLayer expects layer");
-
- var num;
-
- if(num_or_name.constructor === String)
- {
- var scene = this.scene || LS.GlobalScene;
- var layer_num = scene.layer_names.indexOf( num_or_name );
- if(layer_num == -1)
- {
- console.error("Layer with name:",num_or_name,"not found in scene");
- return;
- }
- num = layer_num;
- }
- else
- num = num_or_name;
-
- var f = 1<<num;
- this.layers = (this.layers & (~f));
- if(value)
- this.layers |= f;
- }
-
- /**
- * checks if this node is in the given layer
- * @method isInLayer
- * @param {number|String} index of layer or name according to scene.layer_names
- * @return {boolean} true if belongs to this layer
- */
- SceneNode.prototype.isInLayer = function( num_or_name )
- {
- if( num_or_name == null )
- throw("setLayer expects layer");
-
- var num;
-
- if(num_or_name.constructor === String)
- {
- var scene = this.scene || LS.GlobalScene;
- var layer_num = scene.layer_names.indexOf( num_or_name );
- if(layer_num == -1)
- {
- console.error("Layer with name:",num_or_name,"not found in scene");
- return;
- }
- num = layer_num;
- }
- else
- num = num_or_name;
-
- return (this.layers & (1<<num)) !== 0;
- }
-
- SceneNode.prototype.getLayers = function()
- {
- var r = [];
- if(!this.scene)
- return r;
-
- for(var i = 0; i < 32; ++i)
- {
- if( this.layers & (1<<i) )
- r.push( this.scene.layer_names[i] || ("layer"+i) );
- }
- return r;
- }
-
- /**
- * Returns the root node of the prefab incase it is inside a prefab, otherwise null
- * @method insidePrefab
- * @return {Object} returns the node where the prefab starts
- */
- SceneNode.prototype.insidePrefab = function()
- {
- var aux = this;
- while( aux )
- {
- if(aux.prefab)
- return aux;
- aux = aux._parentNode;
- }
- return null;
- }
-
- /**
- * remember clones this node and returns the new copy (you need to add it to the scene to see it)
- * @method clone
- * @return {Object} returns a cloned version of this node
- */
- SceneNode.prototype.clone = function()
- {
- var scene = this._in_tree;
-
- var new_name = scene ? scene.generateUniqueNodeName( this._name ) : this._name ;
- var newnode = new LS.SceneNode( new_name );
- var info = this.serialize();
-
- //remove all uids from nodes and components
- LS.clearUIds( info );
-
- info.uid = LS.generateUId("NODE-");
- newnode.configure( info );
-
- return newnode;
- }
-
- /**
- * Configure this node from an object containing the info
- * @method configure
- * @param {Object} info the object with all the info (comes from the serialize method)
- */
- SceneNode.prototype.configure = function(info)
- {
- //identifiers parsing
- if (info.name)
- this.setName(info.name);
- else if (info.id)
- this.setName(info.id);
- if(info.layers !== undefined)
- this.layers = info.layers;
-
- if (info.uid)
- this.uid = info.uid;
-
- if (info.className && info.className.constructor == String)
- this.className = info.className;
-
- if(info.node_type)
- {
- this.node_type = info.node_type;
- if(info.node_type == "JOINT") //used in editor
- this._is_bone = true;
- }
-
- //some helpers (mostly for when loading from js object that come from importers or code)
- if(info.camera)
- this.addComponent( new LS.Camera( info.camera ) );
-
- if(info.light)
- this.addComponent( new LS.Light( info.light ) );
-
- //in case more than one mesh in on e node
- if(info.meshes)
- {
- for(var i = 0; i < info.meshes.length; ++i)
- this.addMeshComponents( info.meshes[i], info );
- }
- else if(info.mesh)
- this.addMeshComponents( info.mesh, info );
-
- //transform in matrix format could come from importers so we leave it
- if((info.position || info.model || info.transform) && !this.transform)
- this.addComponent( new LS.Transform() );
- if(info.position)
- this.transform.position = info.position;
- if(info.model)
- this.transform.fromMatrix( info.model );
- if(info.matrix)
- this.transform.fromMatrix( info.matrix );
- if(info.transform)
- this.transform.configure( info.transform );
-
- //first the no components
- if(info.material)
- {
- var mat_classname = info.material.material_class;
- if(!mat_classname || mat_classname == "newStandardMaterial") //legacy
- mat_classname = "StandardMaterial";
- var constructor = LS.MaterialClasses[mat_classname];
- if(constructor)
- this.material = typeof(info.material) == "string" ? info.material : new constructor( info.material );
- else
- console.warn("Material not found: " + mat_classname );
- }
-
- if(info.flags) //merge
- for(var i in info.flags)
- this.flags[i] = info.flags[i];
-
- //add animation tracks player
- if(info.animation)
- {
- this.animation = info.animation;
- this.addComponent( new LS.Components.PlayAnimation({ animation: this.animation }) );
- }
-
- //extra user info
- if(info.extra)
- this.extra = info.extra;
-
- if(info.editor)
- this._editor = info.editor;
-
-
- if(info.comments)
- this.comments = info.comments;
-
- //restore components
- if(info.components)
- this.configureComponents( info );
-
- if(info.prefab && !this._is_root) //is_root because in some weird situations the prefab was set to the root node
- this.prefab = info.prefab; //assign and calls this.reloadFromPrefab();
- else //configure children if it is not a prefab
- this.configureChildren(info);
-
- LEvent.trigger(this,"configure",info);
- }
-
- //adds components according to a mesh
- //used mostly to addapt a node to a collada mesh info
- SceneNode.prototype.addMeshComponents = function( mesh_id, extra_info )
- {
- extra_info = extra_info || {};
-
- if(!mesh_id)
- return;
-
- if( mesh_id.constructor !== String )
- {
- extra_info = mesh_id;
- mesh_id = extra_info.mesh;
- if(!mesh_id)
- {
- console.warn("Mesh info without mesh id");
- return null;
- }
- }
-
- var mesh = LS.ResourcesManager.meshes[ mesh_id ];
-
- if(!mesh)
- {
- console.warn( "SceneNode mesh not found: " + mesh_id );
- return;
- }
-
- var mesh_render_config = { mesh: mesh_id };
-
- if(extra_info.submesh_id !== undefined)
- mesh_render_config.submesh_id = extra_info.submesh_id;
- if(extra_info.morph_targets !== undefined)
- mesh_render_config.morph_targets = extra_info.morph_targets;
- if(extra_info.material !== undefined)
- mesh_render_config.material = extra_info.material;
-
- var compo = new LS.Components.MeshRenderer( mesh_render_config );
-
- //parsed meshes have info about primitive
- if( mesh.primitive )
- {
- switch(mesh.primitive)
- {
- case 'points': compo.primitive = GL.POINTS; break;
- case 'lines': compo.primitive = GL.LINES; break;
- case 'line_strip': compo.primitive = GL.LINE_STRIP; break;
- }
- delete mesh.primitive;
- }
-
- //add MeshRenderer
- this.addComponent( compo );
-
- //skinning
- if(mesh && mesh.bones)
- {
- compo = new LS.Components.SkinDeformer({ search_bones_in_parent: false }); //search_bones_in_parent is false because usually DAEs come that way
- this.addComponent( compo );
- }
-
- //morph targets
- if( mesh && mesh.morph_targets )
- {
- var compo = new LS.Components.MorphDeformer( { morph_targets: mesh.morph_targets } );
- this.addComponent( compo );
- }
-
- }
-
- /**
- * Serializes this node by creating an object with all the info
- * it contains info about the components too
- * @method serialize
- * @param {bool} ignore_prefab serializing wont returns children if it is a prefab, if you set this to ignore_prefab it will return all the info
- * @return {Object} returns the object with the info
- */
- SceneNode.prototype.serialize = function( ignore_prefab, simplified )
- {
- var o = {
- object_class: "SceneNode"
- };
-
- if(this._name)
- o.name = this._name;
- if(this.uid)
- o.uid = this.uid;
- if(this.className)
- o.className = this.className;
- o.layers = this.layers;
-
- //work in progress
- if(this.node_type)
- o.node_type = this.node_type;
-
- //modules
- if(this.mesh && typeof(this.mesh) == "string")
- o.mesh = this.mesh; //do not save procedural meshes
- if(this.submesh_id != null)
- o.submesh_id = this.submesh_id;
- if(this.material)
- o.material = typeof(this.material) == "string" ? this.material : this.material.serialize( simplified );
- if(this.prefab && !ignore_prefab && !this._is_root )
- o.prefab = this.prefab;
-
- if(this.flags)
- o.flags = LS.cloneObject(this.flags);
-
- //extra user info
- if(this.extra)
- o.extra = this.extra;
- if(this.comments)
- o.comments = this.comments;
-
- if(this._children && (!this.prefab || ignore_prefab) )
- o.children = this.serializeChildren( simplified );
-
- if(this._editor)
- o.editor = this._editor;
-
- //save components
- this.serializeComponents( o, simplified );
-
- //extra serializing info
- LEvent.trigger(this,"serialize",o);
-
- return o;
- }
-
- //used to recompute matrix so when parenting one node it doesnt lose its global transformation
- SceneNode.prototype._onChildAdded = function( child_node, recompute_transform )
- {
- if(recompute_transform && this.transform)
- {
- var M = child_node.transform.getGlobalMatrix(); //get son transform
- var M_parent = this.transform.getGlobalMatrix(); //parent transform
- mat4.invert(M_parent,M_parent);
- child_node.transform.fromMatrix( mat4.multiply(M_parent,M_parent,M) );
- child_node.transform.getGlobalMatrix(); //refresh
- }
- //link transform
- if(this.transform)
- {
- if(!child_node.transform)
- child_node.transform.addComponent( new LS.Transform() );
- child_node.transform._parent = this.transform;
- }
- }
-
- SceneNode.prototype._onChangeParent = function( future_parent, recompute_transform )
- {
- if(recompute_transform && future_parent.transform)
- {
- var M = this.transform.getGlobalMatrix(); //get son transform
- var M_parent = future_parent.transform.getGlobalMatrix(); //parent transform
- mat4.invert(M_parent,M_parent);
- this.transform.fromMatrix( mat4.multiply(M_parent,M_parent,M) );
- }
- //link transform
- if(future_parent.transform)
- this.transform._parent = future_parent.transform;
- }
-
- SceneNode.prototype._onChildRemoved = function( node, recompute_transform, remove_components )
- {
- if(this.transform)
- {
- //unlink transform
- if(node.transform)
- {
- if(recompute_transform)
- {
- var m = node.transform.getGlobalMatrix();
- node.transform._parent = null;
- node.transform.fromMatrix(m);
- }
- else
- node.transform._parent = null;
- }
- }
-
- if( remove_components )
- node.removeAllComponents();
- }
-
- //Computes the bounding box from the render instance of this node
- //doesnt take into account children
- SceneNode.prototype.getBoundingBox = function( bbox, only_instances )
- {
- bbox = bbox || BBox.create();
- var render_instances = this._instances;
- if(render_instances)
- for(var i = 0; i < render_instances.length; ++i)
- {
- if(i == 0)
- bbox.set( render_instances[i].aabb );
- else
- BBox.merge( bbox, bbox, render_instances[i].aabb );
- }
-
- if(only_instances)
- return bbox;
-
- if( (!render_instances || render_instances.length == 0) && this.transform )
- return BBox.fromPoint( this.transform.getGlobalPosition() );
-
- return bbox;
- }
-
- LS.Scene.Node = SceneNode;
- LS.SceneNode = SceneNode;
- LS.Classes.SceneNode = SceneNode;
-
-