API Docs for:
Show:

File: ../src/sceneNode.js

  1. ///@INFO: BASE
  2. //****************************************************************************
  3.  
  4. /**
  5. * The SceneNode class represents and object in the scene
  6. * Is the base class for all objects in the scene as meshes, lights, cameras, and so
  7. *
  8. * @class SceneNode
  9. * @param {String} name the name for this node (otherwise a random one is computed)
  10. * @constructor
  11. */
  12.  
  13. function SceneNode( name )
  14. {
  15. if(name && name.constructor !== String)
  16. {
  17. name = null;
  18. console.warn("SceneNode constructor first parameter must be a String with the name");
  19. }
  20.  
  21. //Generic identifying info
  22. this._name = name || ("node_" + (Math.random() * 10000).toFixed(0)); //generate random number
  23. this._uid = LS.generateUId("NODE-");
  24. this._classList = {}; //to store classes
  25. this.layers = 3|0; //32 bits for layers (force to int)
  26. this.node_type = null; //used to store a string defining the node info
  27.  
  28. //more generic info
  29. this._prefab = null;
  30. this._material = null;
  31.  
  32. //from Componentcontainer
  33. this._components = []; //used for logic actions
  34. this._missing_components = null; //used to store state of component that couldnt be created
  35.  
  36. //from CompositePattern
  37. this._parentNode = null;
  38. this._children = null;
  39. this._in_tree = null;
  40. this._instances = []; //render instances
  41.  
  42. //flags
  43. this.flags = {
  44. visible: true,
  45. is_static: false,
  46. selectable: true,
  47. locked: false
  48. };
  49.  
  50. this.init(false,true);
  51.  
  52. /** Fired here (from Transform) when the node transform changes
  53. * @event transformChanged
  54. */
  55. }
  56.  
  57. SceneNode.prototype.init = function( keep_components, keep_info )
  58. {
  59. if(!keep_info)
  60. {
  61. this.layers = 3|0; //32 bits for layers (force to int)
  62. this._name = name || ("node_" + (Math.random() * 10000).toFixed(0)); //generate random number
  63. this._uid = LS.generateUId("NODE-");
  64. this._classList = {};
  65.  
  66. //material
  67. this._material = null;
  68. this.extra = {}; //for extra info
  69. this.node_type = null;
  70.  
  71. //flags
  72. this.flags = {
  73. visible: true,
  74. is_static: false,
  75. selectable: true
  76. };
  77. }
  78.  
  79. //Basic components
  80. if(!keep_components)
  81. {
  82. if( this._components && this._components.length )
  83. console.warn("SceneNode.init() should not be called if it contains components, call clear instead");
  84. this._components = []; //used for logic actions
  85. this._missing_components = null;
  86. this.addComponent( new LS.Transform() );
  87. }
  88. }
  89.  
  90. //get methods from other classes
  91. LS.extendClass( SceneNode, ComponentContainer ); //container methods
  92. LS.extendClass( SceneNode, CompositePattern ); //container methods
  93.  
  94. /**
  95. * changes the node name
  96. * @method setName
  97. * @param {String} new_name the new name
  98. * @return {Object} returns true if the name changed
  99. */
  100.  
  101. Object.defineProperty( SceneNode.prototype, 'name', {
  102. set: function(name)
  103. {
  104. this.setName( name );
  105. },
  106. get: function(){
  107. return this._name;
  108. },
  109. enumerable: true
  110. });
  111.  
  112. Object.defineProperty( SceneNode.prototype, 'fullname', {
  113. set: function(name)
  114. {
  115. throw("You cannot set fullname, it depends on the parent nodes");
  116. },
  117. get: function(){
  118. return this.getPathName();
  119. },
  120. enumerable: false
  121. });
  122.  
  123. //Changing the UID has lots of effects (because nodes are indexed by UID in the scene)
  124. //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)
  125. Object.defineProperty( SceneNode.prototype, 'uid', {
  126. set: function(uid)
  127. {
  128. if(!uid)
  129. return;
  130.  
  131. //valid uid?
  132. if(uid[0] != LS._uid_prefix)
  133. {
  134. console.warn("Invalid UID, renaming it to: " + uid );
  135. uid = LS._uid_prefix + uid;
  136. }
  137.  
  138. //no changes?
  139. if(uid == this._uid)
  140. return;
  141.  
  142. SceneNode._last_uid_changed = this._uid; //hack, in case we want the previous uid of a node
  143.  
  144. //update scene tree indexing
  145. if( this._in_tree && this._in_tree._nodes_by_uid[ this.uid ] )
  146. delete this._in_tree._nodes_by_uid[ this.uid ];
  147. this._uid = uid;
  148. if( this._in_tree )
  149. this._in_tree._nodes_by_uid[ this.uid ] = this;
  150. //events
  151. LEvent.trigger( this, "uid_changed", uid );
  152. if(this._in_tree)
  153. LEvent.trigger( this._in_tree, "node_uid_changed", this );
  154. },
  155. get: function(){
  156. return this._uid;
  157. },
  158. enumerable: true
  159. });
  160.  
  161.  
  162. Object.defineProperty( SceneNode.prototype, 'visible', {
  163. set: function(v)
  164. {
  165. this.flags.visible = v;
  166. },
  167. get: function(){
  168. return this.flags.visible;
  169. },
  170. enumerable: true
  171. });
  172.  
  173. Object.defineProperty( SceneNode.prototype, 'is_static', {
  174. set: function(v)
  175. {
  176. this.flags.is_static = v;
  177. },
  178. get: function(){
  179. return this.flags.is_static;
  180. },
  181. enumerable: true
  182. });
  183.  
  184. Object.defineProperty( SceneNode.prototype, 'material', {
  185. set: function(v)
  186. {
  187. if( this._material == v )
  188. return;
  189.  
  190. this._material = v;
  191. if(v)
  192. {
  193. if(v.constructor === String)
  194. return;
  195. if(v._root && v._root != this) //has root and its not me
  196. console.warn( "Cannot assign a material of one SceneNode to another, you must clone it or register it" )
  197. else
  198. v._root = this; //link
  199. }
  200. LEvent.trigger( this, "materialChanged" );
  201. },
  202. get: function(){
  203. return this._material;
  204. },
  205. enumerable: true
  206. });
  207.  
  208. Object.defineProperty( SceneNode.prototype, 'prefab', {
  209. set: function(name)
  210. {
  211. this._prefab = name;
  212. if(!this._prefab)
  213. return;
  214. var prefab = LS.RM.getResource(name);
  215. var that = this;
  216. if(prefab)
  217. this.reloadFromPrefab();
  218. else
  219. LS.ResourcesManager.load( name, function(){
  220. that.reloadFromPrefab();
  221. });
  222. },
  223. get: function(){
  224. return this._prefab;
  225. },
  226. enumerable: true
  227. });
  228.  
  229. SceneNode.prototype.clear = function()
  230. {
  231. this.removeAllComponents();
  232. this.removeAllChildren();
  233. this.init();
  234. }
  235.  
  236. SceneNode.prototype.setName = function(new_name)
  237. {
  238. if(this._name == new_name)
  239. return true; //no changes
  240.  
  241. //check that the name is valid (doesnt have invalid characters)
  242. if(!LS.validateName(new_name))
  243. {
  244. console.warn("invalid name for node: " + new_name );
  245. //new_name = new_name.replace(/[^a-z0-9\.\-]/gi,"_");
  246. return false;
  247. }
  248.  
  249. var scene = this._in_tree;
  250. if(!scene)
  251. {
  252. this._name = new_name;
  253. return true;
  254. }
  255.  
  256. //remove old link
  257. if( this._name )
  258. delete scene._nodes_by_name[ this._name ];
  259.  
  260. //assign name
  261. this._name = new_name;
  262.  
  263. //we already have another node with this name
  264. if( new_name && !scene._nodes_by_name[ new_name ] )
  265. scene._nodes_by_name[ this._name ] = this;
  266.  
  267. /**
  268. * Node changed name
  269. *
  270. * @event name_changed
  271. * @param {String} new_name
  272. */
  273. LEvent.trigger( this, "name_changed", new_name );
  274. if(scene)
  275. LEvent.trigger( scene, "node_name_changed", this );
  276. return true;
  277. }
  278.  
  279. Object.defineProperty( SceneNode.prototype, 'classList', {
  280. get: function() { return this._classList },
  281. set: function(v) {},
  282. enumerable: false
  283. });
  284.  
  285. /**
  286. * @property className {String}
  287. */
  288. Object.defineProperty( SceneNode.prototype, 'className', {
  289. get: function() {
  290. var keys = null;
  291. if(Object.keys)
  292. keys = Object.keys(this._classList);
  293. else
  294. {
  295. keys = [];
  296. for(var k in this._classList)
  297. keys.push(k);
  298. }
  299. return keys.join(" ");
  300. },
  301. set: function(v) {
  302. this._classList = {};
  303. if(!v)
  304. return;
  305. var t = v.split(" ");
  306. for(var i in t)
  307. this._classList[ t[i] ] = true;
  308. },
  309. enumerable: true
  310. });
  311.  
  312. /**
  313. * Destroys this node
  314. * @method destroy
  315. * @param {number} time [optional] time in seconds to wait till destroying the node
  316. **/
  317. SceneNode.prototype.destroy = function( time )
  318. {
  319. if(time && time.constructor === Number && time > 0)
  320. {
  321. setTimeout( this.destroy.bind(this,0), time * 0.001 );
  322. return;
  323. }
  324.  
  325. LEvent.trigger( this, "destroy" );
  326. this.removeAllComponents();
  327. if(this.children)
  328. while(this.children.length)
  329. this.children[0].destroy();
  330. if(this._parentNode)
  331. this._parentNode.removeChild( this );
  332. }
  333.  
  334. /**
  335. * Returns the locator string of this node
  336. * @method getLocator
  337. * @param {string} property_name [optional] you can pass the name of a property in this node to get the locator of that one
  338. * @return {String} the locator string of this node
  339. **/
  340. SceneNode.prototype.getLocator = function( property_name )
  341. {
  342. if(!property_name)
  343. return this.uid;
  344. return this.uid + "/" + property_name;
  345. }
  346.  
  347. /**
  348. * Returns and object with info about a property given a locator
  349. * @method getPropertyInfo
  350. * @param {string} locator
  351. * @return {Object} object with { node, target, name, value and type }
  352. **/
  353. SceneNode.prototype.getPropertyInfo = function( locator )
  354. {
  355. var path = locator.split("/");
  356. return this.getPropertyInfoFromPath(path);
  357. }
  358.  
  359. /**
  360. * Returns and object with info about a property given a locator in path format
  361. * @method getPropertyInfoFromPath
  362. * @param {Array} path a locator in path format (split by /)
  363. * @return {Object} object with { node, target, name, value and type }
  364. **/
  365. SceneNode.prototype.getPropertyInfoFromPath = function( path )
  366. {
  367. var target = this;
  368. var varname = path[0];
  369. var no_slice = false;
  370.  
  371. if(path.length == 0)
  372. {
  373. return {
  374. node: this,
  375. target: null,
  376. name: "",
  377. value: this,
  378. type: "node" //node because thats the global type for nodes
  379. };
  380. }
  381. else if(path.length == 1) //compo or //var
  382. {
  383. if(path[0][0] == "@")
  384. {
  385. target = this.getComponentByUId( path[0] );
  386. return {
  387. node: this,
  388. target: target,
  389. name: target ? LS.getObjectClassName( target ) : "",
  390. type: "component",
  391. value: target
  392. };
  393. }
  394. else if (path[0] == "material")
  395. {
  396. target = this.getMaterial();
  397. return {
  398. node: this,
  399. target: target,
  400. name: target ? LS.getObjectClassName( target ) : "",
  401. type: "material",
  402. value: target
  403. };
  404. }
  405.  
  406. var target = this.getComponent( path[0] );
  407. if(target)
  408. {
  409. return {
  410. node: this,
  411. target: target,
  412. name: target ? LS.getObjectClassName( target ) : "",
  413. type: "component",
  414. value: target
  415. };
  416. }
  417.  
  418. //special cases for a node
  419. switch(path[0])
  420. {
  421. case "matrix":
  422. case "x":
  423. case "y":
  424. case "z":
  425. case "position":
  426. case "rotX":
  427. case "rotY":
  428. case "rotZ":
  429. target = this.transform;
  430. varname = path[0];
  431. no_slice = true;
  432. break;
  433. default:
  434. target = this;
  435. varname = path[0];
  436. break;
  437. }
  438. }
  439. else if(path.length > 1) //compo/var
  440. {
  441. if(path[0][0] == "@")
  442. {
  443. varname = path[1];
  444. target = this.getComponentByUId( path[0] );
  445. }
  446. else if (path[0] == "material")
  447. {
  448. target = this.getMaterial();
  449. varname = path[1];
  450. }
  451. else if (path[0] == "flags")
  452. {
  453. target = this.flags;
  454. varname = path[1];
  455. }
  456. else
  457. {
  458. target = this.getComponent( path[0] );
  459. varname = path[1];
  460. }
  461.  
  462. if(!target)
  463. return null;
  464. }
  465. else //�?
  466. {
  467. }
  468.  
  469. if(!target) //unknown target
  470. return null;
  471.  
  472. //this was moved to Component.prototype.getPropertyInfoFromPath (if any errors check cases)
  473. if( target != this && target.getPropertyInfoFromPath ) //avoid weird recursion
  474. return target.getPropertyInfoFromPath( no_slice ? path : path.slice(1) );
  475.  
  476. return null;
  477. }
  478.  
  479. /**
  480. * Returns the value of a property given a locator in string format
  481. * @method getPropertyValue
  482. * @param {String} locaator
  483. * @return {*} the value of that property
  484. **/
  485. SceneNode.prototype.getPropertyValue = function( locator )
  486. {
  487. var path = locator.split("/");
  488. return this.getPropertyValueFromPath(path);
  489. }
  490.  
  491. /**
  492. * Returns the value of a property given a locator in path format
  493. * @method getPropertyValueFromPath
  494. * @param {Array} locator in path format (array)
  495. * @return {*} the value of that property
  496. **/
  497. SceneNode.prototype.getPropertyValueFromPath = function( path )
  498. {
  499. var target = this;
  500. var varname = path[0];
  501. var no_slice = false;
  502.  
  503. if(path.length == 0)
  504. return null
  505. else if(path.length == 1) //compo or //var
  506. {
  507. if(path[0][0] == "@")
  508. return this.getComponentByUId( path[0] );
  509. else if (path[0] == "material")
  510. return this.getMaterial();
  511. var target = this.getComponent( path[0] );
  512. if(target)
  513. return target;
  514.  
  515. switch(path[0])
  516. {
  517. case "matrix":
  518. case "x":
  519. case "y":
  520. case "z":
  521. case "position":
  522. case "rotX":
  523. case "rotY":
  524. case "rotZ":
  525. target = this.transform;
  526. varname = path[0];
  527. no_slice = true;
  528. break;
  529. default:
  530. target = this;
  531. varname = path[0];
  532. break;
  533. }
  534. }
  535. else if(path.length > 1) //compo/var
  536. {
  537. if(path[0][0] == "@")
  538. {
  539. varname = path[1];
  540. target = this.getComponentByUId( path[0] );
  541. }
  542. else if (path[0] == "material")
  543. {
  544. target = this.getMaterial();
  545. varname = path[1];
  546. }
  547. else if (path[0] == "flags")
  548. {
  549. target = this.flags;
  550. varname = path[1];
  551. }
  552. else
  553. {
  554. target = this.getComponent( path[0] );
  555. varname = path[1];
  556. }
  557.  
  558. if(!target)
  559. return null;
  560. }
  561. else //�?
  562. {
  563. }
  564.  
  565. var v = undefined;
  566.  
  567. if( target.getPropertyValueFromPath && target != this )
  568. {
  569. var r = target.getPropertyValueFromPath( no_slice ? path : path.slice(1) );
  570. if(r)
  571. return r;
  572. }
  573.  
  574. //to know the value of a property of the given target
  575. if( target.getPropertyValue && target != this )
  576. v = target.getPropertyValue( varname );
  577.  
  578. //special case when the component doesnt specify any locator info but the property referenced does
  579. //used in TextureFX
  580. if (v === undefined && path.length > 2 && target[ varname ] && target[ varname ].getPropertyValueFromPath )
  581. {
  582. var r = target[ varname ].getPropertyValueFromPath( no_slice ? path.slice(1) : path.slice(2) );
  583. if(r)
  584. {
  585. r.node = this;
  586. return r;
  587. }
  588. }
  589.  
  590. if(v === undefined && target[ varname ] === undefined )
  591. return null;
  592. return v !== undefined ? v : target[ varname ];
  593. }
  594.  
  595. /**
  596. * assigns a value to a property given the locator for that property
  597. * @method setPropertyValue
  598. * @param {String} locator
  599. * @param {*} value
  600. **/
  601. SceneNode.prototype.setPropertyValue = function( locator, value )
  602. {
  603. var path = locator.split("/");
  604. return this.setPropertyValueFromPath(path, value, 0);
  605. }
  606.  
  607. /**
  608. * given a locator in path mode (array) and a value, it searches for the corresponding value and applies it
  609. * @method setPropertyValueFromPath
  610. * @param {Array} path
  611. * @param {*} value
  612. * @param {Number} [optional] offset used to skip the firsst positions in the array
  613. **/
  614. SceneNode.prototype.setPropertyValueFromPath = function( path, value, offset )
  615. {
  616. offset = offset || 0;
  617.  
  618. if(this.flags && this.flags.locked)
  619. return; //lock ignores changes from animations or graphs
  620.  
  621. var target = null;
  622. var varname = path[offset];
  623.  
  624. if(path.length > (offset+1))
  625. {
  626. if(path[offset][0] == "@")
  627. {
  628. varname = path[offset+1];
  629. target = this.getComponentByUId( path[offset] );
  630. }
  631. else if( path[offset] == "material" )
  632. {
  633. target = this.getMaterial();
  634. varname = path[offset+1];
  635. }
  636. else if( path[offset] == "flags" )
  637. {
  638. target = this.flags;
  639. varname = path[offset+1];
  640. }
  641. else
  642. {
  643. target = this.getComponent( path[offset] );
  644. varname = path[offset+1];
  645. }
  646.  
  647. if(!target)
  648. return null;
  649. }
  650. else { //special cases
  651. switch ( path[offset] )
  652. {
  653. case "matrix": target = this.transform; break;
  654. case "position":
  655. case "rotation":
  656. case "x":
  657. case "y":
  658. case "z":
  659. case "xrotation":
  660. case "yrotation":
  661. case "zrotation":
  662. target = this.transform;
  663. varname = path[offset];
  664. break;
  665. case "translate.X": target = this.transform; varname = "x"; break;
  666. case "translate.Y": target = this.transform; varname = "y"; break;
  667. case "translate.Z": target = this.transform; varname = "z"; break;
  668. case "rotateX.ANGLE": target = this.transform; varname = "pitch"; break;
  669. case "rotateY.ANGLE": target = this.transform; varname = "yaw"; break;
  670. case "rotateZ.ANGLE": target = this.transform; varname = "roll"; break;
  671. default: target = this; //null
  672. }
  673. }
  674.  
  675. if(!target)
  676. return null;
  677.  
  678. if(target.setPropertyValueFromPath && target != this)
  679. if( target.setPropertyValueFromPath( path, value, offset+1 ) === true )
  680. return target;
  681. if(target.setPropertyValue && target != this)
  682. if( target.setPropertyValue( varname, value ) === true )
  683. return target;
  684.  
  685. if( target[ varname ] === undefined )
  686. return;
  687.  
  688. //special case when the component doesnt specify any locator info but the property referenced does
  689. //used in TextureFX
  690. if ( path.length > 2 && target[ varname ] && target[ varname ].setPropertyValueFromPath )
  691. return target[ varname ].setPropertyValueFromPath( path, value, offset+2 );
  692.  
  693. //disabled because if the vars has a setter it wont be called using the array.set
  694. //if( target[ varname ] !== null && target[ varname ].set )
  695. // target[ varname ].set( value );
  696. //else
  697. target[ varname ] = value;
  698.  
  699. return target;
  700. }
  701.  
  702. /**
  703. * Returns all the resources used by this node and its components (you can include the resources from the children too)
  704. * @method getResources
  705. * @param {Object} res object where to store the resources used (in "res_name":LS.TYPE format)
  706. * @param {Boolean} include_children if you want to add also the resources used by the children nodes
  707. * @return {Object} the same object passed is returned
  708. **/
  709. SceneNode.prototype.getResources = function( res, include_children )
  710. {
  711. //resources in components
  712. for(var i in this._components)
  713. if( this._components[i].getResources )
  714. this._components[i].getResources( res );
  715.  
  716. //res in material
  717. if(this.material)
  718. {
  719. if( this.material.constructor === String )
  720. {
  721. if(this.material[0] != ":") //not a local material, then its a reference
  722. {
  723. res[this.material] = LS.Material;
  724. }
  725. }
  726.  
  727. var mat = this.getMaterial();
  728. if(mat)
  729. mat.getResources( res );
  730. }
  731.  
  732. //prefab
  733. if(this.prefab)
  734. res[ this.prefab ] = LS.Prefab;
  735.  
  736. //propagate
  737. if(include_children)
  738. for(var i in this._children)
  739. this._children[i].getResources(res, true);
  740.  
  741. return res;
  742. }
  743.  
  744. SceneNode.prototype.getTransform = function() {
  745. return this.transform;
  746. }
  747.  
  748. //Helpers
  749.  
  750. SceneNode.prototype.getMesh = function( use_lod_mesh ) {
  751. var mesh = this.mesh;
  752. var mesh_renderer = this.getComponent( LS.Components.MeshRenderer );
  753. if(!mesh && mesh_renderer)
  754. {
  755. if(use_lod_mesh)
  756. mesh = mesh_renderer.lod_mesh;
  757. if(!mesh)
  758. mesh = mesh_renderer.mesh;
  759. }
  760. if(!mesh)
  761. return null;
  762. if(mesh.constructor === String)
  763. return LS.ResourcesManager.meshes[mesh];
  764. return mesh;
  765. }
  766.  
  767. //Light component
  768. SceneNode.prototype.getLight = function() {
  769. return this.light;
  770. }
  771.  
  772. //Camera component
  773. SceneNode.prototype.getCamera = function() {
  774. return this.camera;
  775. }
  776.  
  777. /**
  778. * Allows to load some kind of resource and associate it to this node.
  779. * It can be for prefabs, meshes, scenes from daes, etc
  780. * @method load
  781. * @param {string} url
  782. * @param {Function} on_complete
  783. **/
  784. SceneNode.prototype.load = function( url, on_complete )
  785. {
  786. var that = this;
  787. LS.ResourcesManager.load( url, inner );
  788. function inner( resource )
  789. {
  790. if(!resource)
  791. return;
  792. that.assign( resource );
  793. if(on_complete)
  794. on_complete();
  795. }
  796. }
  797.  
  798. /**
  799. * 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
  800. * @method assign
  801. * @param {*} resource the resource to assign (it also accepts a resource filename that has been previously loaded).
  802. * @param {Function} on_complete
  803. **/
  804. SceneNode.prototype.assign = function( item, extra )
  805. {
  806. if(!item)
  807. {
  808. console.error("assignResource cannot have null as resource");
  809. return;
  810. }
  811.  
  812. //assume is the filename of a resource
  813. if(item.constructor === String)
  814. item = LS.ResourcesManager.getResource( item );
  815.  
  816. if(!item)
  817. return;
  818.  
  819. switch( item.constructor )
  820. {
  821. case LS.SceneNode:
  822. this.addChild( item );
  823. break;
  824. case LS.Scene:
  825. var node = this;
  826. item.loadScripts( null, function(){
  827. item.loadResources( function(){
  828. node.addChild( item.root.clone() );
  829. });
  830. });
  831. break;
  832. case LS.Prefab:
  833. this.prefab = item.fullpath || item.filename;
  834. break;
  835. case GL.Mesh:
  836. var component = this.getComponent( LS.Components.MeshRenderer );
  837. if(component)
  838. component.configure({ mesh: item.fullpath || item.filename });
  839. else
  840. this.addComponent( new LS.MeshRenderer({ mesh: mesh_name, submesh_id: submesh_id }) );
  841. break;
  842. case LS.Animation:
  843. var comp = this.getComponent( LS.Components.PlayAnimation );
  844. if(!comp)
  845. comp = this.addComponent( new LS.Components.PlayAnimation() );
  846. comp.animation = item.fullpath || item.filename;
  847. break;
  848. case LS.Resource: //generic resource
  849. var ext = LS.ResourcesManager.getExtension( item.filename );
  850. if(ext == "js") //scripts
  851. {
  852. var comp = this.getComponent( LS.Components.ScriptFromFile );
  853. if(!comp)
  854. comp = this.addComponent( new LS.Components.ScriptFromFile() );
  855. comp.src = item.fullpath || item.filename;
  856. }
  857. break;
  858. default:
  859. console.error("feature not supported loading this type of resource" , item );
  860. }
  861. }
  862.  
  863. /**
  864. * Simple way to assign a mesh to a node, it created a MeshRenderer component or reuses and existing one and assigns the mesh
  865. * @method setMesh
  866. * @param {string} mesh_name the name of the mesh (path to the file)
  867. * @param {Number} submesh_id if you want to assign a submesh
  868. **/
  869. SceneNode.prototype.setMesh = function(mesh_name, submesh_id)
  870. {
  871. var component = this.getComponent( LS.Components.MeshRenderer );
  872. if(component)
  873. component.configure({ mesh: mesh_name, submesh_id: submesh_id });
  874. else
  875. this.addComponent( new LS.MeshRenderer({ mesh: mesh_name, submesh_id: submesh_id }) );
  876. }
  877.  
  878. SceneNode.prototype.getMaterial = function()
  879. {
  880. if (!this.material)
  881. return null;
  882. if(this.material.constructor === String)
  883. {
  884. if( !this._in_tree )
  885. return null;
  886. if( this.material[0] == "@" )//uid
  887. return LS.ResourcesManager.materials_by_uid[ this.material ];
  888. return LS.ResourcesManager.materials[ this.material ];
  889. }
  890. return this.material;
  891. }
  892.  
  893. /**
  894. * Apply prefab info (skipping the root components) to node, so all children will be removed and components lost and overwritten
  895. * It is called from prefab.applyToNodes when a prefab is loaded in memory
  896. * @method reloadFromPrefab
  897. **/
  898. SceneNode.prototype.reloadFromPrefab = function()
  899. {
  900. if(!this.prefab)
  901. return;
  902.  
  903. var prefab = LS.ResourcesManager.resources[ this.prefab ];
  904. if(!prefab)
  905. return;
  906.  
  907. if( prefab.constructor !== LS.Prefab )
  908. throw("prefab must be a LS.Prefab class");
  909.  
  910. //apply info
  911. this.removeAllChildren();
  912. this.init( true, true ); //keep components, keep_info
  913. var prefab_data = prefab.prefab_data;
  914. //remove all but children info (prefabs overwrite only children info)
  915. prefab_data = { children: prefab.prefab_data.children };
  916.  
  917. //uid data is already removed from the prefab
  918. this.configure( prefab_data );
  919.  
  920. //load secondary resources
  921. var resources = this.getResources( {}, true );
  922. LS.ResourcesManager.loadResources( resources );
  923.  
  924. LEvent.trigger( this, "prefabReady", prefab );
  925. }
  926.  
  927.  
  928. /**
  929. * Assigns this node to one layer
  930. * @method setLayer
  931. * @param {number|String} the index of the layer or the name (according to scene.layer_names)
  932. * @param {boolean} value
  933. */
  934. SceneNode.prototype.setLayer = function( num_or_name, value )
  935. {
  936. if( num_or_name == null )
  937. throw("setLayer expects layer");
  938.  
  939. var num;
  940.  
  941. if(num_or_name.constructor === String)
  942. {
  943. var scene = this.scene || LS.GlobalScene;
  944. var layer_num = scene.layer_names.indexOf( num_or_name );
  945. if(layer_num == -1)
  946. {
  947. console.error("Layer with name:",num_or_name,"not found in scene");
  948. return;
  949. }
  950. num = layer_num;
  951. }
  952. else
  953. num = num_or_name;
  954.  
  955. var f = 1<<num;
  956. this.layers = (this.layers & (~f));
  957. if(value)
  958. this.layers |= f;
  959. }
  960.  
  961. /**
  962. * checks if this node is in the given layer
  963. * @method isInLayer
  964. * @param {number|String} index of layer or name according to scene.layer_names
  965. * @return {boolean} true if belongs to this layer
  966. */
  967. SceneNode.prototype.isInLayer = function( num_or_name )
  968. {
  969. if( num_or_name == null )
  970. throw("setLayer expects layer");
  971.  
  972. var num;
  973.  
  974. if(num_or_name.constructor === String)
  975. {
  976. var scene = this.scene || LS.GlobalScene;
  977. var layer_num = scene.layer_names.indexOf( num_or_name );
  978. if(layer_num == -1)
  979. {
  980. console.error("Layer with name:",num_or_name,"not found in scene");
  981. return;
  982. }
  983. num = layer_num;
  984. }
  985. else
  986. num = num_or_name;
  987.  
  988. return (this.layers & (1<<num)) !== 0;
  989. }
  990.  
  991. SceneNode.prototype.getLayers = function()
  992. {
  993. var r = [];
  994. if(!this.scene)
  995. return r;
  996.  
  997. for(var i = 0; i < 32; ++i)
  998. {
  999. if( this.layers & (1<<i) )
  1000. r.push( this.scene.layer_names[i] || ("layer"+i) );
  1001. }
  1002. return r;
  1003. }
  1004.  
  1005. /**
  1006. * Returns the root node of the prefab incase it is inside a prefab, otherwise null
  1007. * @method insidePrefab
  1008. * @return {Object} returns the node where the prefab starts
  1009. */
  1010. SceneNode.prototype.insidePrefab = function()
  1011. {
  1012. var aux = this;
  1013. while( aux )
  1014. {
  1015. if(aux.prefab)
  1016. return aux;
  1017. aux = aux._parentNode;
  1018. }
  1019. return null;
  1020. }
  1021.  
  1022. /**
  1023. * remember clones this node and returns the new copy (you need to add it to the scene to see it)
  1024. * @method clone
  1025. * @return {Object} returns a cloned version of this node
  1026. */
  1027. SceneNode.prototype.clone = function()
  1028. {
  1029. var scene = this._in_tree;
  1030.  
  1031. var new_name = scene ? scene.generateUniqueNodeName( this._name ) : this._name ;
  1032. var newnode = new LS.SceneNode( new_name );
  1033. var info = this.serialize();
  1034.  
  1035. //remove all uids from nodes and components
  1036. LS.clearUIds( info );
  1037.  
  1038. info.uid = LS.generateUId("NODE-");
  1039. newnode.configure( info );
  1040.  
  1041. return newnode;
  1042. }
  1043.  
  1044. /**
  1045. * Configure this node from an object containing the info
  1046. * @method configure
  1047. * @param {Object} info the object with all the info (comes from the serialize method)
  1048. */
  1049. SceneNode.prototype.configure = function(info)
  1050. {
  1051. //identifiers parsing
  1052. if (info.name)
  1053. this.setName(info.name);
  1054. else if (info.id)
  1055. this.setName(info.id);
  1056. if(info.layers !== undefined)
  1057. this.layers = info.layers;
  1058.  
  1059. if (info.uid)
  1060. this.uid = info.uid;
  1061.  
  1062. if (info.className && info.className.constructor == String)
  1063. this.className = info.className;
  1064.  
  1065. if(info.node_type)
  1066. {
  1067. this.node_type = info.node_type;
  1068. if(info.node_type == "JOINT") //used in editor
  1069. this._is_bone = true;
  1070. }
  1071.  
  1072. //some helpers (mostly for when loading from js object that come from importers or code)
  1073. if(info.camera)
  1074. this.addComponent( new LS.Camera( info.camera ) );
  1075.  
  1076. if(info.light)
  1077. this.addComponent( new LS.Light( info.light ) );
  1078.  
  1079. //in case more than one mesh in on e node
  1080. if(info.meshes)
  1081. {
  1082. for(var i = 0; i < info.meshes.length; ++i)
  1083. this.addMeshComponents( info.meshes[i], info );
  1084. }
  1085. else if(info.mesh)
  1086. this.addMeshComponents( info.mesh, info );
  1087.  
  1088. //transform in matrix format could come from importers so we leave it
  1089. if((info.position || info.model || info.transform) && !this.transform)
  1090. this.addComponent( new LS.Transform() );
  1091. if(info.position)
  1092. this.transform.position = info.position;
  1093. if(info.model)
  1094. this.transform.fromMatrix( info.model );
  1095. if(info.matrix)
  1096. this.transform.fromMatrix( info.matrix );
  1097. if(info.transform)
  1098. this.transform.configure( info.transform );
  1099.  
  1100. //first the no components
  1101. if(info.material)
  1102. {
  1103. var mat_classname = info.material.material_class;
  1104. if(!mat_classname || mat_classname == "newStandardMaterial") //legacy
  1105. mat_classname = "StandardMaterial";
  1106. var constructor = LS.MaterialClasses[mat_classname];
  1107. if(constructor)
  1108. this.material = typeof(info.material) == "string" ? info.material : new constructor( info.material );
  1109. else
  1110. console.warn("Material not found: " + mat_classname );
  1111. }
  1112.  
  1113. if(info.flags) //merge
  1114. for(var i in info.flags)
  1115. this.flags[i] = info.flags[i];
  1116. //add animation tracks player
  1117. if(info.animation)
  1118. {
  1119. this.animation = info.animation;
  1120. this.addComponent( new LS.Components.PlayAnimation({ animation: this.animation }) );
  1121. }
  1122.  
  1123. //extra user info
  1124. if(info.extra)
  1125. this.extra = info.extra;
  1126.  
  1127. if(info.editor)
  1128. this._editor = info.editor;
  1129.  
  1130.  
  1131. if(info.comments)
  1132. this.comments = info.comments;
  1133.  
  1134. //restore components
  1135. if(info.components)
  1136. this.configureComponents( info );
  1137.  
  1138. if(info.prefab && !this._is_root) //is_root because in some weird situations the prefab was set to the root node
  1139. this.prefab = info.prefab; //assign and calls this.reloadFromPrefab();
  1140. else //configure children if it is not a prefab
  1141. this.configureChildren(info);
  1142.  
  1143. LEvent.trigger(this,"configure",info);
  1144. }
  1145.  
  1146. //adds components according to a mesh
  1147. //used mostly to addapt a node to a collada mesh info
  1148. SceneNode.prototype.addMeshComponents = function( mesh_id, extra_info )
  1149. {
  1150. extra_info = extra_info || {};
  1151.  
  1152. if(!mesh_id)
  1153. return;
  1154.  
  1155. if( mesh_id.constructor !== String )
  1156. {
  1157. extra_info = mesh_id;
  1158. mesh_id = extra_info.mesh;
  1159. if(!mesh_id)
  1160. {
  1161. console.warn("Mesh info without mesh id");
  1162. return null;
  1163. }
  1164. }
  1165.  
  1166. var mesh = LS.ResourcesManager.meshes[ mesh_id ];
  1167.  
  1168. if(!mesh)
  1169. {
  1170. console.warn( "SceneNode mesh not found: " + mesh_id );
  1171. return;
  1172. }
  1173.  
  1174. var mesh_render_config = { mesh: mesh_id };
  1175.  
  1176. if(extra_info.submesh_id !== undefined)
  1177. mesh_render_config.submesh_id = extra_info.submesh_id;
  1178. if(extra_info.morph_targets !== undefined)
  1179. mesh_render_config.morph_targets = extra_info.morph_targets;
  1180. if(extra_info.material !== undefined)
  1181. mesh_render_config.material = extra_info.material;
  1182.  
  1183. var compo = new LS.Components.MeshRenderer( mesh_render_config );
  1184.  
  1185. //parsed meshes have info about primitive
  1186. if( mesh.primitive )
  1187. {
  1188. switch(mesh.primitive)
  1189. {
  1190. case 'points': compo.primitive = GL.POINTS; break;
  1191. case 'lines': compo.primitive = GL.LINES; break;
  1192. case 'line_strip': compo.primitive = GL.LINE_STRIP; break;
  1193. }
  1194. delete mesh.primitive;
  1195. }
  1196.  
  1197. //add MeshRenderer
  1198. this.addComponent( compo );
  1199.  
  1200. //skinning
  1201. if(mesh && mesh.bones)
  1202. {
  1203. compo = new LS.Components.SkinDeformer({ search_bones_in_parent: false }); //search_bones_in_parent is false because usually DAEs come that way
  1204. this.addComponent( compo );
  1205. }
  1206.  
  1207. //morph targets
  1208. if( mesh && mesh.morph_targets )
  1209. {
  1210. var compo = new LS.Components.MorphDeformer( { morph_targets: mesh.morph_targets } );
  1211. this.addComponent( compo );
  1212. }
  1213.  
  1214. }
  1215.  
  1216. /**
  1217. * Serializes this node by creating an object with all the info
  1218. * it contains info about the components too
  1219. * @method serialize
  1220. * @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
  1221. * @return {Object} returns the object with the info
  1222. */
  1223. SceneNode.prototype.serialize = function( ignore_prefab, simplified )
  1224. {
  1225. var o = {
  1226. object_class: "SceneNode"
  1227. };
  1228.  
  1229. if(this._name)
  1230. o.name = this._name;
  1231. if(this.uid)
  1232. o.uid = this.uid;
  1233. if(this.className)
  1234. o.className = this.className;
  1235. o.layers = this.layers;
  1236.  
  1237. //work in progress
  1238. if(this.node_type)
  1239. o.node_type = this.node_type;
  1240.  
  1241. //modules
  1242. if(this.mesh && typeof(this.mesh) == "string")
  1243. o.mesh = this.mesh; //do not save procedural meshes
  1244. if(this.submesh_id != null)
  1245. o.submesh_id = this.submesh_id;
  1246. if(this.material)
  1247. o.material = typeof(this.material) == "string" ? this.material : this.material.serialize( simplified );
  1248. if(this.prefab && !ignore_prefab && !this._is_root )
  1249. o.prefab = this.prefab;
  1250.  
  1251. if(this.flags)
  1252. o.flags = LS.cloneObject(this.flags);
  1253.  
  1254. //extra user info
  1255. if(this.extra)
  1256. o.extra = this.extra;
  1257. if(this.comments)
  1258. o.comments = this.comments;
  1259.  
  1260. if(this._children && (!this.prefab || ignore_prefab) )
  1261. o.children = this.serializeChildren( simplified );
  1262.  
  1263. if(this._editor)
  1264. o.editor = this._editor;
  1265.  
  1266. //save components
  1267. this.serializeComponents( o, simplified );
  1268.  
  1269. //extra serializing info
  1270. LEvent.trigger(this,"serialize",o);
  1271.  
  1272. return o;
  1273. }
  1274.  
  1275. //used to recompute matrix so when parenting one node it doesnt lose its global transformation
  1276. SceneNode.prototype._onChildAdded = function( child_node, recompute_transform )
  1277. {
  1278. if(recompute_transform && this.transform)
  1279. {
  1280. var M = child_node.transform.getGlobalMatrix(); //get son transform
  1281. var M_parent = this.transform.getGlobalMatrix(); //parent transform
  1282. mat4.invert(M_parent,M_parent);
  1283. child_node.transform.fromMatrix( mat4.multiply(M_parent,M_parent,M) );
  1284. child_node.transform.getGlobalMatrix(); //refresh
  1285. }
  1286. //link transform
  1287. if(this.transform)
  1288. {
  1289. if(!child_node.transform)
  1290. child_node.transform.addComponent( new LS.Transform() );
  1291. child_node.transform._parent = this.transform;
  1292. }
  1293. }
  1294.  
  1295. SceneNode.prototype._onChangeParent = function( future_parent, recompute_transform )
  1296. {
  1297. if(recompute_transform && future_parent.transform)
  1298. {
  1299. var M = this.transform.getGlobalMatrix(); //get son transform
  1300. var M_parent = future_parent.transform.getGlobalMatrix(); //parent transform
  1301. mat4.invert(M_parent,M_parent);
  1302. this.transform.fromMatrix( mat4.multiply(M_parent,M_parent,M) );
  1303. }
  1304. //link transform
  1305. if(future_parent.transform)
  1306. this.transform._parent = future_parent.transform;
  1307. }
  1308.  
  1309. SceneNode.prototype._onChildRemoved = function( node, recompute_transform, remove_components )
  1310. {
  1311. if(this.transform)
  1312. {
  1313. //unlink transform
  1314. if(node.transform)
  1315. {
  1316. if(recompute_transform)
  1317. {
  1318. var m = node.transform.getGlobalMatrix();
  1319. node.transform._parent = null;
  1320. node.transform.fromMatrix(m);
  1321. }
  1322. else
  1323. node.transform._parent = null;
  1324. }
  1325. }
  1326.  
  1327. if( remove_components )
  1328. node.removeAllComponents();
  1329. }
  1330.  
  1331. //Computes the bounding box from the render instance of this node
  1332. //doesnt take into account children
  1333. SceneNode.prototype.getBoundingBox = function( bbox, only_instances )
  1334. {
  1335. bbox = bbox || BBox.create();
  1336. var render_instances = this._instances;
  1337. if(render_instances)
  1338. for(var i = 0; i < render_instances.length; ++i)
  1339. {
  1340. if(i == 0)
  1341. bbox.set( render_instances[i].aabb );
  1342. else
  1343. BBox.merge( bbox, bbox, render_instances[i].aabb );
  1344. }
  1345.  
  1346. if(only_instances)
  1347. return bbox;
  1348.  
  1349. if( (!render_instances || render_instances.length == 0) && this.transform )
  1350. return BBox.fromPoint( this.transform.getGlobalPosition() );
  1351.  
  1352. return bbox;
  1353. }
  1354.  
  1355. LS.Scene.Node = SceneNode;
  1356. LS.SceneNode = SceneNode;
  1357. LS.Classes.SceneNode = SceneNode;
  1358.