/home/lindsay/xeolabs/xeogl-next/xeogl/src/objects/object.js
API Docs for:

File: /home/lindsay/xeolabs/xeogl-next/xeogl/src/objects/object.js

  1. /**
  2. An **Object** is a 3D element within a xeogl {{#crossLink "Scene"}}Scene{{/crossLink}}.
  3.  
  4. ## Overview
  5.  
  6. Object is an abstract base class that's subclassed by:
  7.  
  8. * {{#crossLink "Mesh"}}{{/crossLink}}, which represents a drawable 3D primitive.
  9. * {{#crossLink "Group"}}{{/crossLink}}, which is a composite Object that represents a group of child Objects.
  10. * {{#crossLink "Model"}}{{/crossLink}}, which is a Group and is subclassed by {{#crossLink "GLTFModel"}}{{/crossLink}},
  11. {{#crossLink "STLModel"}}{{/crossLink}}, {{#crossLink "OBJModel"}}{{/crossLink}} etc. A Model can contain child Groups
  12. and {{#crossLink "Mesh"}}Meshes{{/crossLink}} that represent its component parts.
  13.  
  14. As shown in the examples below, these component types can be connected into flexible scene hierarchies that contain
  15. content loaded from multiple sources and file formats. Since a {{#crossLink "Group"}}{{/crossLink}} implements the *[Composite](https://en.wikipedia.org/wiki/Composite_pattern)* pattern,
  16. property updates on a {{#crossLink "Group"}}Group{{/crossLink}} will apply recursively to all the Objects within it.
  17.  
  18. This page mostly covers the base functionality provided by Object, while the pages for the subclasses document the
  19. functionality specific to those subclasses.
  20.  
  21. ## Usage
  22.  
  23. * [Creating an Object hierarchy](#creating-an-object-hierarchy)
  24. * [Accessing Objects](#accessing-objects)
  25. * [Updating Objects](#updating-objects)
  26. * [Adding and removing Objects](#updating-objects)
  27. * [Models within Groups](#models-within-groups)
  28. * [Objects within Models](#objects-within-models)
  29. * [Applying a semantic data model](#applying-a-semantic-data-model)
  30. * [Destroying Objects](#destroying-objects)
  31.  
  32. ### Creating an Object hierarchy
  33.  
  34. Let's create a {{#crossLink "Group"}}Group{{/crossLink}} that represents a table, with five child {{#crossLink "Mesh"}}{{/crossLink}}es for its top and legs:
  35.  
  36. <a href="../../examples/#objects_hierarchy"><img src="../../assets/images/screenshots/objectHierarchy.png"></img></a>
  37.  
  38. ````javascript
  39. var boxGeometry = new xeogl.BoxGeometry(); // We'll reuse the same geometry for all our Meshes
  40.  
  41. var table = new xeogl.Group({
  42.  
  43. id: "table",
  44. rotation: [0, 50, 0],
  45. position: [0, 0, 0],
  46. scale: [1, 1, 1],
  47.  
  48. children: [
  49.  
  50. new xeogl.Mesh({ // Red table leg
  51. id: "redLeg", // <<-------- Optional ID within Scene
  52. guid: "5782d454-9f06-4d71-aff1-78c597eacbfb", // <<-------- Optional GUID
  53. position: [-4, -6, -4],
  54. scale: [1, 3, 1],
  55. rotation: [0, 0, 0],
  56. geometry: boxGeometry,
  57. material: new xeogl.PhongMaterial({
  58. diffuse: [1, 0.3, 0.3]
  59. })
  60. }),
  61.  
  62. new xeogl.Mesh({ // Green table leg
  63. id: "greenLeg", // <<-------- Optional ID within Scene
  64. guid: "c37e421f-5440-4ce1-9b4c-9bd06d8ab5ed", // <<-------- Optional GUID
  65. position: [4, -6, -4],
  66. scale: [1, 3, 1],
  67. rotation: [0, 0, 0],
  68. geometry: boxGeometry,
  69. material: new xeogl.PhongMaterial({
  70. diffuse: [0.3, 1.0, 0.3]
  71. })
  72. }),
  73.  
  74. new xeogl.Mesh({// Blue table leg
  75. id: "blueLeg",
  76. position: [4, -6, 4],
  77. scale: [1, 3, 1],
  78. rotation: [0, 0, 0],
  79. geometry: boxGeometry,
  80. material: new xeogl.PhongMaterial({
  81. diffuse: [0.3, 0.3, 1.0]
  82. })
  83. }),
  84.  
  85. new xeogl.Mesh({ // Yellow table leg
  86. id: "yellowLeg",
  87. position: [-4, -6, 4],
  88. scale: [1, 3, 1],
  89. rotation: [0, 0, 0],
  90. geometry: boxGeometry,
  91. material: new xeogl.PhongMaterial({
  92. diffuse: [1.0, 1.0, 0.0]
  93. })
  94. })
  95.  
  96. new xeogl.Mesh({ // Purple table top
  97. id: "tableTop",
  98. position: [0, -3, 0],
  99. scale: [6, 0.5, 6],
  100. rotation: [0, 0, 0],
  101. geometry: boxGeometry,
  102. material: new xeogl.PhongMaterial({
  103. diffuse: [1.0, 0.3, 1.0]
  104. })
  105. })
  106. ]
  107. });
  108. ````
  109.  
  110. ### Accessing Objects
  111.  
  112. We can then get those {{#crossLink "Mesh"}}Mesh{{/crossLink}} Objects by index from the {{#crossLink "Group"}}Group{{/crossLink}}'s children property:
  113.  
  114. ````javascript
  115. var blueLeg = table.children[2];
  116. blueLeg.highlighted = true;
  117. ````
  118.  
  119. We can also get them by ID from the {{#crossLink "Group"}}Group{{/crossLink}}'s childMap property:
  120.  
  121. ````javascript
  122. var blueLeg = table.childMap["blueLeg"];
  123. blueLeg.highlighted = true;
  124. ````
  125.  
  126. or by ID from the {{#crossLink "Scene"}}{{/crossLink}}'s components map:
  127.  
  128. ````javascript
  129. var blueLeg = table.scene.components["blueLeg"];
  130. blueLeg.highlighted = true;
  131. ````
  132.  
  133. or from the {{#crossLink "Scene"}}{{/crossLink}}'s objects map (only Objects are in this map, and {{#crossLink "Mesh"}}Meshes{{/crossLink}} are Objects):
  134.  
  135. ````javascript
  136. var blueLeg = table.scene.objects["blueLeg"];
  137. blueLeg.highlighted = true;
  138. ````
  139.  
  140. or from the {{#crossLink "Scene"}}{{/crossLink}}'s meshes map (only {{#crossLink "Mesh"}}Meshes{{/crossLink}} are in that map):
  141.  
  142. ````javascript
  143. var blueLeg = table.scene.meshes["blueLeg"];
  144. blueLeg.highlighted = true;
  145. ````
  146. For convenience, the {{#crossLink "Scene"}}{{/crossLink}}'s objects map explicitly registers what Objects exist within the {{#crossLink "Scene"}}{{/crossLink}}, while its meshes map
  147. explicitly registers what {{#crossLink "Mesh"}}Meshes{{/crossLink}} exist.
  148.  
  149. #### GUIDs
  150.  
  151. Note the optional globally unique identifiers (GUIDs) on the first two Objects. While regular IDs are unique within the Scene,
  152. GUIDs are unique throughout the entire universe, and are often used to identify elements in things like architectural models. We can
  153. find those Objects within their Scene using their GUIDs, like this:
  154.  
  155. ````javascript
  156. var redLeg = scene.guidObjects["5782d454-9f06-4d71-aff1-78c597eacbfb"];
  157. var greenLeg = scene.guidObjects["c37e421f-5440-4ce1-9b4c-9bd06d8ab5ed"];
  158. ````
  159.  
  160. ### Updating Objects
  161.  
  162. As mentioned earlier, property updates on a {{#crossLink "Group"}}Group{{/crossLink}} {{#crossLink "Object"}}{{/crossLink}} will apply recursively to all
  163. sub-Objects within it, eventually updating the {{#crossLink "Mesh"}}{{/crossLink}} {{#crossLink "Object"}}Objects{{/crossLink}} at the leaves.
  164.  
  165. These properties, defined in Object, are:
  166.  
  167. * {{#crossLink "Object/visible:property"}}visible{{/crossLink}}
  168. * {{#crossLink "Object/highlighted:property"}}highlighted{{/crossLink}}
  169. * {{#crossLink "Object/ghosted:property"}}ghosted{{/crossLink}}
  170. * {{#crossLink "Object/selected:property"}}selected{{/crossLink}}
  171. * {{#crossLink "Object/edges:property"}}edges{{/crossLink}}
  172. * {{#crossLink "Object/colorize:property"}}colorize{{/crossLink}}
  173. * {{#crossLink "Object/opacity:property"}}opacity{{/crossLink}}
  174. * {{#crossLink "Object/clippable:property"}}clippable{{/crossLink}}
  175. * {{#crossLink "Object/collidable:property"}}collidable{{/crossLink}}
  176. * {{#crossLink "Object/pickable:property"}}pickable{{/crossLink}}
  177. * {{#crossLink "Object/castShadow:property"}}castShadow{{/crossLink}}
  178. * {{#crossLink "Object/receiveShadow:property"}}receiveShadow{{/crossLink}}
  179. * {{#crossLink "Object/receiveShadow:property"}}receiveShadow{{/crossLink}}
  180.  
  181. Let's highlight the whole table in one shot:
  182.  
  183. ````javascript
  184. table.highlighted = true;
  185. ````
  186.  
  187. That property value will then recursively propagate down our five Meshes.
  188.  
  189. Each Object has a local transformation that's applied within the coordinate space set up the
  190. transform of its parent, if it has one.
  191.  
  192. Let's rotate the table:
  193.  
  194. ````javascript
  195. table.rotation = [0, 45, 0]; // (X,Y,Z)
  196. table.childMap["tableTop"].position = [0, -10, 0]; // (X,Y,Z)
  197. ````
  198.  
  199. That will rotate the coordinate space containing the five child Meshes.
  200.  
  201. Now let's translate the table top Mesh:
  202.  
  203. ````javascript
  204. table.childMap["tableTop"].position = [0, -10, 0]; // (X,Y,Z)
  205. ````
  206.  
  207. As we translated table top Mesh, we updated the extents its World-space boundary. That update, in addition to rotating
  208. the table Group, has updated the collective boundary of the whole table.
  209.  
  210. We can get the boundary of the table top like this:
  211.  
  212. ````javascript
  213. var tableTopMesh = table.childMap["tableTop"].aabb;
  214. ````
  215.  
  216. We can get the collective boundary of the whole table, like this:
  217.  
  218. ````javascript
  219. var tableTopMesh = table.aabb;
  220. ````
  221.  
  222. Just for fun, let's fit the view to the table top:
  223.  
  224. ````javascript
  225. var cameraFlight = new xeogl.CameraFlightAnimation(); // Fit the boundary in view
  226. cameraFlight.flyTo(tableTopMesh.aabb);
  227. ````
  228.  
  229. Those boundaries will automatically update whenever we add or remove child {{#crossLink "Object"}}Objects{{/crossLink}} or {{#crossLink "Mesh"}}Meshes{{/crossLink}}, or update child {{#crossLink "Mesh"}}Meshes{{/crossLink}}' {{#crossLink "Geometry"}}Geometries{{/crossLink}}
  230. or modeling transforms.
  231.  
  232. Let's follow the table top wherever it goes:
  233.  
  234. ````javascript
  235. tableTopMesh.on("boundary", function() {
  236. cameraFlight.flyTo(this.aabb); // "this" is the table top Mesh
  237. });
  238. ````
  239.  
  240. Or perhaps keep the whole table fitted to view whenever we transform any Objects or Meshes within the hierarchy, or add
  241. or remove Objects within the hierarchy:
  242.  
  243. ````javascript
  244. table.on("boundary", function() {
  245. var aabb = this.aabb; // "this" is the table Group
  246. cameraFlight.flyTo(aabb);
  247. });
  248. ````
  249.  
  250. ### Adding and removing Objects
  251.  
  252. Let's add another {{#crossLink "Mesh"}}Mesh{{/crossLink}} to our table {{#crossLink "Group"}}Group{{/crossLink}}, a sort of spherical ornament sitting on the table top:
  253.  
  254. ````javascript
  255. table.addChild(new xeogl.Mesh({
  256. id: "myExtraObject",
  257. geometry: new xeogl.SphereGeometry({ radius: 1.0 }),
  258. position: [2, -3, 0],
  259. geometry: boxGeometry,
  260. material: new xeogl.PhongMaterial({
  261. diffuse: [0.3, 0.3, 1.0]
  262. })
  263. });
  264. ````
  265.  
  266. That's going to update the {{#crossLink "Group"}}Group{{/crossLink}}'s boundary, as mentioned earlier.
  267.  
  268. To remove it, just destroy it:
  269.  
  270. ````javascript
  271. table.childMap["myExtraObject"].destroy();
  272. ````
  273.  
  274. ### Models within Groups
  275.  
  276. Now let's create a {{#crossLink "Group"}}Group{{/crossLink}} that contains three Models. Recall that Models are {{#crossLink "Group"}}Group{{/crossLink}}s, which are Objects.
  277.  
  278. <a href="../../examples/#objects_hierarchy_models"><img src="../../assets/images/screenshots/modelHierarchy.png"></img></a>
  279.  
  280. ````javascript
  281. var myModels = new xeogl.Group({
  282.  
  283. rotation: [0, 0, 0],
  284. position: [0, 0, 0],
  285. scale: [1, 1, 1],
  286.  
  287. children: [
  288.  
  289. new xeogl.GLTFModel({
  290. id: "engine",
  291. src: "models/gltf/2CylinderEngine/glTF/2CylinderEngine.gltf",
  292. scale: [.2, .2, .2],
  293. position: [-110, 0, 0],
  294. rotation: [0, 90, 0],
  295. objectTree: true // <<----------------- Loads Object tree from glTF scene node graph
  296. }),
  297.  
  298. new xeogl.GLTFModel({
  299. id: "hoverBike",
  300. src: "models/gltf/hover_bike/scene.gltf",
  301. scale: [.5, .5, .5],
  302. position: [0, -40, 0]
  303. }),
  304.  
  305. new xeogl.STLModel({
  306. id: "f1Car",
  307. src: "models/stl/binary/F1Concept.stl",
  308. smoothNormals: true,
  309. scale: [3, 3, 3],
  310. position: [110, -20, 60],
  311. rotation: [0, 90, 0]
  312. })
  313. ]
  314. });
  315. ````
  316.  
  317. Like with the {{#crossLink "Mesh"}}{{/crossLink}} Objects in the previous example, we can then get those Models by index from the {{#crossLink "Group"}}Group{{/crossLink}}'s children property:
  318.  
  319. ````javascript
  320. var hoverBike = myModels.children[1];
  321. hoverBike.scale = [0.5, 0.5, 0.5];
  322. ````
  323.  
  324. or by ID from the {{#crossLink "Group"}}Group{{/crossLink}}'s childMap property:
  325.  
  326. ````javascript
  327. var hoverBike = myModels.childMap["hoverBike"];
  328. hoverBike.scale = [0.5, 0.5, 0.5];
  329. ````
  330.  
  331. or by ID from the {{#crossLink "Scene"}}{{/crossLink}}'s components map:
  332.  
  333. ````javascript
  334. var hoverBike = myModels.scene.components["hoverBike"];
  335. hoverBike.scale = [0.75, 0.75, 0.75];
  336. ````
  337.  
  338. or from the {{#crossLink "Scene"}}{{/crossLink}}'s objects map (only Objects are in this map, and Models are Objects):
  339.  
  340. ````javascript
  341. var hoverBike = myModels.scene.objects["hoverBike"];
  342. hoverBike.scale = [0.75, 0.75, 0.75];
  343. ````
  344.  
  345. or from the {{#crossLink "Scene"}}{{/crossLink}}'s models map (which only contains Models):
  346.  
  347. ````javascript
  348. var hoverBike = myModels.scene.models["hoverBike"];
  349. hoverBike.scale = [0.5, 0.5, 0.5];
  350. ````
  351.  
  352. For convenience, the {{#crossLink "Scene"}}{{/crossLink}}'s objects map explicitly registers what Objects exist within the {{#crossLink "Scene"}}{{/crossLink}}, while its models map
  353. explicitly registers what Models exist.
  354.  
  355. As mentioned earlier, property updates on a {{#crossLink "Group"}}Group{{/crossLink}} will apply recursively to all the Objects within it. Let's highlight
  356. all the Models in the {{#crossLink "Group"}}Group{{/crossLink}}, in one shot:
  357.  
  358. ````javascript
  359. myModels.highlighted = true;
  360. ````
  361.  
  362. and just for fun, let's scale the {{#crossLink "Group"}}Group{{/crossLink}} down, then rotate one of the Models, relative to the {{#crossLink "Group"}}Group{{/crossLink}}:
  363.  
  364. ````javascript
  365. myModels.scale = [0.5, 0.5, 0.5]; // (X,Y,Z)
  366. myModels.childMap["engine"].rotation = [0, 45, 0]; // (X,Y,Z)
  367. ````
  368.  
  369. ### Objects within Models
  370.  
  371. Models are Objects that plug into the scene graph, containing child Objects of their own. The {{#crossLink "GLTFModel"}}{{/crossLink}}
  372. in the previous example loads its child Objects from the glTF scene node graph.
  373.  
  374. The root Objects within the GLTFModel will be available in the GLTFModel's {{#crossLink "GLTFModel/children:property"}}{{/crossLink}} and {{#crossLink "GLTFModel/childMap:property"}}{{/crossLink}}
  375. properties, while all its Objects and Meshes (at the leaves) will be available in the GLTFModel's {{#crossLink "GLTFModel/objects:property"}}{{/crossLink}} property.
  376.  
  377.  
  378. ````javascript
  379. models.childMap["engine"].childMap["engine#0"].highlighted = true;
  380. ````
  381.  
  382. ````javascript
  383. models.childMap["engine"].objects["engine#3.0"].highlighted=true;
  384. ````
  385.  
  386. ````javascript
  387. models.childMap["engine"].meshes["engine#3.0"].highlighted=true;
  388. ````
  389.  
  390. ### Applying a semantic data model
  391.  
  392. xeogl allows us to organize our Objects using a generic conceptual data model that describes the semantics of our application
  393. domain. We do this by assigning "entity classes" to those Objects that we consider to be *entities* within our domain, and then we're
  394. able to reference those Objects according to their entity classes.
  395.  
  396. #### entityType
  397.  
  398. In xeogl, we classify an Object as an entity by setting its {{#crossLink "Object/entityType:property"}}{{/crossLink}} to an arbitrary string
  399. value that represents its class. Once we've done that, we regard the Object as being an "entity" within our semantic data model, in
  400. addition to being a regular Object within our scene graph. Note that entities in xeogl are not to be confused with *entity-component systems*,
  401. which are a completely different concept.
  402.  
  403. This classification mechanism is useful for building IFC viewers on xeogl, in which case our entity classes would be the IFC
  404. element types. However, since xeogl's concept of entity classes is generic, our semantic model could include any arbitrary
  405. set of classes, such as "fluffy", "insulator", "essential" or "optional", for example.
  406.  
  407. This mechanism only goes as far as allowing us to assign entity classes to our Objects, for the purpose of finding them
  408. within the Scene using their classes. If we wanted to go a step further and model relationships between our classes,
  409. we would need to additionally use some sort of entity-relationship data structure, externally to xeogl, such as an IFC structure model
  410. in which the relation elements would reference our classes.
  411.  
  412. Objects that are not part of any semantic model, such as helpers and gizmos, would not get an ````entityType````, and so would
  413. be effectively invisible to maps and methods that deal with specifically with entities. Use component IDs and "lower-level" maps
  414. like {{#crossLink "Scene/components:property"}}Scene#components{{/crossLink}},
  415. {{#crossLink "Scene/objects:property"}}Scene#objects{{/crossLink}},
  416. {{#crossLink "Scene/meshes:property"}}Scene#meshes{{/crossLink}} and
  417. {{#crossLink "Scene/models:property"}}Scene#models{{/crossLink}} to work with such Objects as non-semantic scene elements,
  418. and "higher-level" maps like {{#crossLink "Scene/entities:property"}}Scene#entities{{/crossLink}} and
  419. {{#crossLink "Scene/entityTypes:property"}}Scene#entityTypes{{/crossLink}} to work with Objects that are entities.
  420.  
  421. To show how to use a semantic model with xeogl, let's redefine the Object hierarchy we created earlier, this
  422. time assigning some imaginary domain-specific entity classes to our table Mesh Objects:
  423.  
  424. ````javascript
  425. var boxGeometry = new xeogl.BoxGeometry(); // We'll reuse the same geometry for all our Meshes
  426.  
  427. var table = new xeogl.Group({
  428.  
  429. id: "table",
  430. rotation: [0, 50, 0],
  431. position: [0, 0, 0],
  432. scale: [1, 1, 1],
  433.  
  434. children: [
  435.  
  436. new xeogl.Mesh({ // Red table leg
  437. id: "redLeg",
  438. entityType: "supporting", // <<------------ Entity class
  439. position: [-4, -6, -4],
  440. scale: [1, 3, 1],
  441. rotation: [0, 0, 0],
  442. geometry: boxGeometry,
  443. material: new xeogl.PhongMaterial({
  444. diffuse: [1, 0.3, 0.3]
  445. })
  446. }),
  447.  
  448. new xeogl.Mesh({ // Green table leg
  449. id: "greenLeg",
  450. entityType: "supporting", // <<------------ Entity class
  451. position: [4, -6, -4],
  452. scale: [1, 3, 1],
  453. rotation: [0, 0, 0],
  454. geometry: boxGeometry,
  455. material: new xeogl.PhongMaterial({
  456. diffuse: [0.3, 1.0, 0.3]
  457. })
  458. }),
  459.  
  460. new xeogl.Mesh({// Blue table leg
  461. id: "blueLeg",
  462. entityType: "supporting", // <<------------ Entity class
  463. position: [4, -6, 4],
  464. scale: [1, 3, 1],
  465. rotation: [0, 0, 0],
  466. geometry: boxGeometry,
  467. material: new xeogl.PhongMaterial({
  468. diffuse: [0.3, 0.3, 1.0]
  469. })
  470. }),
  471.  
  472. new xeogl.Mesh({ // Yellow table leg
  473. id: "yellowLeg",
  474. entityType: "supporting", // <<------------ Entity class
  475. position: [-4, -6, 4],
  476. scale: [1, 3, 1],
  477. rotation: [0, 0, 0],
  478. geometry: boxGeometry,
  479. material: new xeogl.PhongMaterial({
  480. diffuse: [1.0, 1.0, 0.0]
  481. })
  482. })
  483.  
  484. new xeogl.Mesh({ // Purple table top
  485. id: "tableTop",
  486. entityType: "surface", // <<------------ Entity class
  487. position: [0, -3, 0],
  488. scale: [6, 0.5, 6],
  489. rotation: [0, 0, 0],
  490. geometry: boxGeometry,
  491. material: new xeogl.PhongMaterial({
  492. diffuse: [1.0, 0.3, 1.0]
  493. })
  494. })
  495. ]
  496. });
  497. ````
  498.  
  499. This time, we've set the {{#crossLink "Object/entityType:property"}}{{/crossLink}} property on our Mesh Objects, to
  500. assign our entity classes to them. Our arbitrary semantic model is very simple, with just two classes:
  501.  
  502. * "supporting" for entities that support things (eg. table legs), and
  503. * "surface" for entities that provide a surface that you can put things on (eg. table tops).
  504.  
  505. Note that we can assign entity classes to any component type that extends Object, including {{#crossLink "Group"}}{{/crossLink}},
  506. {{#crossLink "Mesh"}}{{/crossLink}}, {{#crossLink "Model"}}{{/crossLink}}, {{#crossLink "GLTFModel"}}{{/crossLink}} etc.
  507.  
  508. We can now conveniently work with our Mesh Objects as entities, in addition working with them as ordinary Objects.
  509.  
  510. We can find our entities in a dedicated map, that contains only the Objects that have the "entityType" property set:
  511.  
  512. ````javascript
  513. var yellowLegMesh = scene.entities["yellowLeg"];
  514. ````
  515.  
  516. We can get a map of all Objects of a given entity class:
  517.  
  518. ````javascript
  519. var supportingEntities = scene.entityTypes["supporting"];
  520. var yellowLegMesh = supportingEntities["yellowLeg"];
  521. ````
  522.  
  523. We can do state updates on entity Objects by their entity class, in a batch:
  524.  
  525. ````javascript
  526. scene.setVisible(["supporting"], false); // Hide the legs
  527. scene.setVisible(["supporting"], true); // Show the legs again
  528. scene.setHighlighted(["supporting", "surface"], true); // Highlight the legs and the table top
  529. ````
  530.  
  531. The {{#crossLink "Scene"}}{{/crossLink}} also has convenience maps dedicated to tracking the visibility, ghosted, highlighted
  532. and selected states of entity Objects:
  533.  
  534. ````javascript
  535. var yellowLegMesh = scene.visibleEntities["yellowLeg"];
  536. var isYellowLegVisible = yellowLegMesh !== undefined;
  537.  
  538. yellowLegMesh.highlighted = false;
  539. var isYellowLegHighlighted = scene.highlightedEntities["yellowLeg"];
  540. ````
  541.  
  542. * [Example](../../examples/#objects_entities)
  543.  
  544. #### Limitations with state inheritance
  545.  
  546. Note that you can't currently nest entity Objects within a hierarchy. If we were to set an entityType on our Group,
  547. say "furniture", and then do this:
  548.  
  549. ````javascript
  550. scene.setVisible(["furniture"], false); // Hide the table
  551. ````
  552.  
  553. Then all our entity Meshes would be hidden, even though they are not "furniture" entities. The entity classification
  554. system does not currently work alongside the way xeogl does state inheritance within Object hierarchies, so keep your
  555. entities non-hierarchical.
  556.  
  557. ### Destroying Objects
  558.  
  559. Call an Object's {{#crossLink "Component/destroy:method"}}Object#destroy(){{/crossLink}} method to destroy it:
  560.  
  561. ````JavaScript
  562. myObject.destroy();
  563. ````
  564.  
  565. That will also destroy all Objects in its subtree.
  566.  
  567. @class Object
  568. @module xeogl
  569. @submodule objects
  570. @constructor
  571. @param [owner] {Component} Owner component. When destroyed, the owner will destroy this component as well. Creates this component within the default {{#crossLink "Scene"}}{{/crossLink}} when omitted.
  572. @param [cfg] {*} Configs
  573. @param [cfg.id] {String} Optional ID, unique among all components in the parent scene, generated automatically when omitted.
  574. @param [cfg.guid] {String} Optional globally unique identifier. This is unique not only within the {{#crossLink "Scene"}}{{/crossLink}}, but throughout the entire universe.
  575. @param [cfg.meta] {String:Object} Optional map of user-defined metadata.
  576. @param [cfg.entityType] {String} Optional entity classification when using within a semantic data model.
  577. @param [cfg.parent] {Object} The parent.
  578. @param [cfg.position=[0,0,0]] {Float32Array} Local 3D position.
  579. @param [cfg.scale=[1,1,1]] {Float32Array} Local scale.
  580. @param [cfg.rotation=[0,0,0]] {Float32Array} Local rotation, as Euler angles given in degrees, for each of the X, Y and Z axis.
  581. @param [cfg.matrix=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1] {Float32Array} Local modelling transform matrix. Overrides the position, scale and rotation parameters.
  582. @param [cfg.visible=true] {Boolean} Indicates if visible.
  583. @param [cfg.culled=false] {Boolean} Indicates if culled from view.
  584. @param [cfg.pickable=true] {Boolean} Indicates if pickable.
  585. @param [cfg.clippable=true] {Boolean} Indicates if clippable.
  586. @param [cfg.collidable=true] {Boolean} Indicates if included in boundary calculations.
  587. @param [cfg.castShadow=true] {Boolean} Indicates if casting shadows.
  588. @param [cfg.receiveShadow=true] {Boolean} Indicates if receiving shadows.
  589. @param [cfg.outlined=false] {Boolean} Indicates if outline is rendered.
  590. @param [cfg.ghosted=false] {Boolean} Indicates if ghosted.
  591. @param [cfg.highlighted=false] {Boolean} Indicates if highlighted.
  592. @param [cfg.selected=false] {Boolean} Indicates if selected.
  593. @param [cfg.edges=false] {Boolean} Indicates if edges are emphasized.
  594. @param [cfg.aabbVisible=false] {Boolean} Indicates if axis-aligned World-space bounding box is visible.
  595. @param [cfg.colorize=[1.0,1.0,1.0]] {Float32Array} RGB colorize color, multiplies by the rendered fragment colors.
  596. @param [cfg.opacity=1.0] {Number} Opacity factor, multiplies by the rendered fragment alpha.
  597. @param [cfg.children] {Array(Object)} Children to add. Children must be in the same {{#crossLink "Scene"}}{{/crossLink}} and will be removed from whatever parents they may already have.
  598. @param [cfg.inheritStates=true] {Boolean} Indicates if children given to this constructor should inherit state from this parent as they are added. State includes {{#crossLink "Object/visible:property"}}{{/crossLink}}, {{#crossLink "Object/culled:property"}}{{/crossLink}}, {{#crossLink "Object/pickable:property"}}{{/crossLink}},
  599. {{#crossLink "Object/clippable:property"}}{{/crossLink}}, {{#crossLink "Object/castShadow:property"}}{{/crossLink}}, {{#crossLink "Object/receiveShadow:property"}}{{/crossLink}},
  600. {{#crossLink "Object/outlined:property"}}{{/crossLink}}, {{#crossLink "Object/ghosted:property"}}{{/crossLink}}, {{#crossLink "Object/highlighted:property"}}{{/crossLink}},
  601. {{#crossLink "Object/selected:property"}}{{/crossLink}}, {{#crossLink "Object/colorize:property"}}{{/crossLink}} and {{#crossLink "Object/opacity:property"}}{{/crossLink}}.
  602. @extends Component
  603. */
  604.  
  605. import {utils} from '../utils.js';
  606. import {Component} from '../component.js';
  607. import {Mesh} from './mesh.js';
  608. import {AABBGeometry} from '../geometry/aabbGeometry.js';
  609. import {PhongMaterial} from '../materials/phongMaterial.js';
  610. import {math} from '../math/math.js';
  611. import {componentClasses} from "./../componentClasses.js";
  612.  
  613. const type = "xeogl.Object";
  614. const angleAxis = new Float32Array(4);
  615. const q1 = new Float32Array(4);
  616. const q2 = new Float32Array(4);
  617. const xAxis = new Float32Array([1, 0, 0]);
  618. const yAxis = new Float32Array([0, 1, 0]);
  619. const zAxis = new Float32Array([0, 0, 1]);
  620.  
  621. const veca = new Float32Array(3);
  622. const vecb = new Float32Array(3);
  623.  
  624. const identityMat = math.identityMat4();
  625.  
  626. class xeoglObject extends Component {
  627.  
  628.  
  629. /**
  630. JavaScript class name for this Component.
  631.  
  632. For example: "xeogl.AmbientLight", "xeogl.MetallicMaterial" etc.
  633.  
  634. @property type
  635. @type String
  636. @final
  637. */
  638. get type() {
  639. return type;
  640. }
  641.  
  642. init(cfg) {
  643.  
  644. super.init(cfg);
  645.  
  646. this._guid = cfg.guid;
  647.  
  648. this._parent = null;
  649. this._childList = [];
  650. this._childMap = {};
  651. this._childIDs = null;
  652.  
  653. this._aabb = null;
  654. this._aabbDirty = true;
  655. this.scene._aabbDirty = true;
  656.  
  657. this._scale = math.vec3();
  658. this._quaternion = math.identityQuaternion();
  659. this._rotation = math.vec3();
  660. this._position = math.vec3();
  661.  
  662. this._worldMatrix = math.identityMat4();
  663. this._worldNormalMatrix = math.identityMat4();
  664.  
  665. this._localMatrixDirty = true;
  666. this._worldMatrixDirty = true;
  667. this._worldNormalMatrixDirty = true;
  668.  
  669. if (cfg.matrix) {
  670. this.matrix = cfg.matrix;
  671. } else {
  672. this.scale = cfg.scale;
  673. this.position = cfg.position;
  674. if (cfg.quaternion) {
  675. } else {
  676. this.rotation = cfg.rotation;
  677. }
  678. }
  679.  
  680. if (cfg.entityType) {
  681. this._entityType = cfg.entityType;
  682. this.scene._entityTypeAssigned(this, this._entityType); // Must assign type before setting properties
  683. }
  684.  
  685. // Properties
  686.  
  687. // If this component instance is a subclass of xeogl.Object that redefines these properties,
  688. // then it's the subclass's properties that are being set here
  689. // (eg. as redefined on xeogl.Mesh, xeogl.Model etc)
  690.  
  691. this.visible = cfg.visible;
  692. this.culled = cfg.culled;
  693. this.pickable = cfg.pickable;
  694. this.clippable = cfg.clippable;
  695. this.collidable = cfg.collidable;
  696. this.castShadow = cfg.castShadow;
  697. this.receiveShadow = cfg.receiveShadow;
  698. this.outlined = cfg.outlined;
  699. this.solid = cfg.solid;
  700. this.ghosted = cfg.ghosted;
  701. this.highlighted = cfg.highlighted;
  702. this.selected = cfg.selected;
  703. this.edges = cfg.edges;
  704. this.aabbVisible = cfg.aabbVisible;
  705.  
  706. this.layer = cfg.layer;
  707. this.colorize = cfg.colorize;
  708. this.opacity = cfg.opacity;
  709.  
  710. // Add children, which inherit state from this Object
  711.  
  712. if (cfg.children) {
  713. const children = cfg.children;
  714. for (let i = 0, len = children.length; i < len; i++) {
  715. this.addChild(children[i], cfg.inheritStates);
  716. }
  717. }
  718.  
  719. if (cfg.parent) {
  720. cfg.parent.addChild(this);
  721. }
  722.  
  723. this.scene._objectCreated(this);
  724. }
  725.  
  726. _setLocalMatrixDirty() {
  727. this._localMatrixDirty = true;
  728. this._setWorldMatrixDirty();
  729. }
  730.  
  731. _setWorldMatrixDirty() {
  732. this._worldMatrixDirty = true;
  733. this._worldNormalMatrixDirty = true;
  734. if (this._childList) {
  735. for (let i = 0, len = this._childList.length; i < len; i++) {
  736. this._childList[i]._setWorldMatrixDirty();
  737. }
  738. }
  739. }
  740.  
  741. _buildWorldMatrix() {
  742. const localMatrix = this.matrix;
  743. if (!this._parent) {
  744. for (let i = 0, len = localMatrix.length; i < len; i++) {
  745. this._worldMatrix[i] = localMatrix[i];
  746. }
  747. } else {
  748. math.mulMat4(this._parent.worldMatrix, localMatrix, this._worldMatrix);
  749. }
  750. this._worldMatrixDirty = false;
  751. }
  752.  
  753. _buildWorldNormalMatrix() {
  754. if (this._worldMatrixDirty) {
  755. this._buildWorldMatrix();
  756. }
  757. if (!this._worldNormalMatrix) {
  758. this._worldNormalMatrix = math.mat4();
  759. }
  760. math.inverseMat4(this._worldMatrix, this._worldNormalMatrix);
  761. math.transposeMat4(this._worldNormalMatrix);
  762. this._worldNormalMatrixDirty = false;
  763. }
  764.  
  765. _setSubtreeAABBsDirty(object) {
  766. object._aabbDirty = true;
  767. object.fire("boundary", true);
  768. if (object._childList) {
  769. for (let i = 0, len = object._childList.length; i < len; i++) {
  770. this._setSubtreeAABBsDirty(object._childList[i]);
  771. }
  772. }
  773. }
  774.  
  775. _setAABBDirty() {
  776. this._setSubtreeAABBsDirty(this);
  777. if (this.collidable) {
  778. for (let object = this; object; object = object._parent) {
  779. object._aabbDirty = true;
  780. object.fire("boundary", true);
  781. }
  782. }
  783. }
  784.  
  785. _updateAABB() {
  786. this.scene._aabbDirty = true;
  787. if (!this._aabb) {
  788. this._aabb = math.AABB3();
  789. }
  790. if (this._buildMeshAABB) {
  791. this._buildMeshAABB(this.worldMatrix, this._aabb); // Geometry
  792. } else { // Object | Group | Model
  793. math.collapseAABB3(this._aabb);
  794. let object;
  795. for (let i = 0, len = this._childList.length; i < len; i++) {
  796. object = this._childList[i];
  797. if (!object.collidable) {
  798. continue;
  799. }
  800. math.expandAABB3(this._aabb, object.aabb);
  801. }
  802. if (!this._aabbCenter) {
  803. this._aabbCenter = new Float32Array(3);
  804. }
  805. math.getAABB3Center(this._aabb, this._aabbCenter);
  806. }
  807. this._aabbDirty = false;
  808. }
  809.  
  810. /**
  811. Adds a child.
  812.  
  813. The child must be in the same {{#crossLink "Scene"}}{{/crossLink}}.
  814.  
  815. If the child already has a parent, will be removed from that parent first.
  816.  
  817. Does nothing if already a child.
  818.  
  819. @param {Object|String} object Instance or ID of the child to add.
  820. @param [inheritStates=false] Indicates if the child should inherit state from this parent as it is added. State includes
  821. {{#crossLink "Object/visible:property"}}{{/crossLink}}, {{#crossLink "Object/culled:property"}}{{/crossLink}}, {{#crossLink "Object/pickable:property"}}{{/crossLink}},
  822. {{#crossLink "Object/clippable:property"}}{{/crossLink}}, {{#crossLink "Object/castShadow:property"}}{{/crossLink}}, {{#crossLink "Object/receiveShadow:property"}}{{/crossLink}},
  823. {{#crossLink "Object/outlined:property"}}{{/crossLink}}, {{#crossLink "Object/ghosted:property"}}{{/crossLink}}, {{#crossLink "Object/highlighted:property"}}{{/crossLink}},
  824. {{#crossLink "Object/selected:property"}}{{/crossLink}}, {{#crossLink "Object/edges:property"}}{{/crossLink}}, {{#crossLink "Object/colorize:property"}}{{/crossLink}} and {{#crossLink "Object/opacity:property"}}{{/crossLink}}.
  825. @returns {Object} The child object.
  826. */
  827. addChild(object, inheritStates) {
  828. if (utils.isNumeric(object) || utils.isString(object)) {
  829. const objectId = object;
  830. object = this.scene.objects[objectId];
  831. if (!object) {
  832. this.warn("Object not found: " + utils.inQuotes(objectId));
  833. return;
  834. }
  835. } else if (utils.isObject(object)) {
  836. throw "addChild( * ) not implemented";
  837. const cfg = object;
  838. // object = new xeogl.Group(this.scene, cfg);
  839. if (!object) {
  840. return;
  841. }
  842. } else {
  843. if (!object.isType("xeogl.Object")) {
  844. this.error("Not a xeogl.Object: " + object.id);
  845. return;
  846. }
  847. if (object._parent) {
  848. if (object._parent.id === this.id) {
  849. this.warn("Already a child object: " + object.id);
  850. return;
  851. }
  852. object._parent.removeChild(object);
  853. }
  854. }
  855. const id = object.id;
  856. if (object.scene.id !== this.scene.id) {
  857. this.error("Object not in same Scene: " + object.id);
  858. return;
  859. }
  860. delete this.scene.rootObjects[object.id];
  861. this._childList.push(object);
  862. this._childMap[object.id] = object;
  863. this._childIDs = null;
  864. object._parent = this;
  865. if (!!inheritStates) {
  866. object.visible = this.visible;
  867. object.culled = this.culled;
  868. object.ghosted = this.ghosted;
  869. object.highlited = this.highlighted;
  870. object.selected = this.selected;
  871. object.edges = this.edges;
  872. object.outlined = this.outlined;
  873. object.clippable = this.clippable;
  874. object.pickable = this.pickable;
  875. object.collidable = this.collidable;
  876. object.castShadow = this.castShadow;
  877. object.receiveShadow = this.receiveShadow;
  878. object.colorize = this.colorize;
  879. object.opacity = this.opacity;
  880. }
  881. object._setWorldMatrixDirty();
  882. object._setAABBDirty();
  883. return object;
  884. }
  885.  
  886. /**
  887. Removes the given child.
  888.  
  889. @method removeChild
  890. @param {Object} object Child to remove.
  891. */
  892. removeChild(object) {
  893. for (let i = 0, len = this._childList.length; i < len; i++) {
  894. if (this._childList[i].id === object.id) {
  895. object._parent = null;
  896. this._childList = this._childList.splice(i, 1);
  897. delete this._childMap[object.id];
  898. this._childIDs = null;
  899. this.scene.rootObjects[object.id] = object;
  900. object._setWorldMatrixDirty();
  901. object._setAABBDirty();
  902. this._setAABBDirty();
  903. return;
  904. }
  905. }
  906. }
  907.  
  908. /**
  909. Removes all children.
  910.  
  911. @method removeChildren
  912. */
  913. removeChildren() {
  914. let object;
  915. for (let i = 0, len = this._childList.length; i < len; i++) {
  916. object = this._childList[i];
  917. object._parent = null;
  918. this.scene.rootObjects[object.id] = object;
  919. object._setWorldMatrixDirty();
  920. object._setAABBDirty();
  921. }
  922. this._childList = [];
  923. this._childMap = {};
  924. this._childIDs = null;
  925. this._setAABBDirty();
  926. }
  927.  
  928. /**
  929. Rotates about the given local axis by the given increment.
  930.  
  931. @method rotate
  932. @paream {Float32Array} axis Local axis about which to rotate.
  933. @param {Number} angle Angle increment in degrees.
  934. */
  935. rotate(axis, angle) {
  936. angleAxis[0] = axis[0];
  937. angleAxis[1] = axis[1];
  938. angleAxis[2] = axis[2];
  939. angleAxis[3] = angle * math.DEGTORAD;
  940. math.angleAxisToQuaternion(angleAxis, q1);
  941. math.mulQuaternions(this.quaternion, q1, q2);
  942. this.quaternion = q2;
  943. this._setLocalMatrixDirty();
  944. this._setAABBDirty();
  945. this._renderer.imageDirty();
  946. return this;
  947. }
  948.  
  949. /**
  950. Rotates about the given World-space axis by the given increment.
  951.  
  952. @method rotate
  953. @paream {Float32Array} axis Local axis about which to rotate.
  954. @param {Number} angle Angle increment in degrees.
  955. */
  956. rotateOnWorldAxis(axis, angle) {
  957. angleAxis[0] = axis[0];
  958. angleAxis[1] = axis[1];
  959. angleAxis[2] = axis[2];
  960. angleAxis[3] = angle * math.DEGTORAD;
  961. math.angleAxisToQuaternion(angleAxis, q1);
  962. math.mulQuaternions(q1, this.quaternion, q1);
  963. //this.quaternion.premultiply(q1);
  964. return this;
  965. }
  966.  
  967. /**
  968. Rotates about the local X-axis by the given increment.
  969.  
  970. @method rotateX
  971. @param {Number} angle Angle increment in degrees.
  972. */
  973. rotateX(angle) {
  974. return this.rotate(xAxis, angle);
  975. }
  976.  
  977. /**
  978. Rotates about the local Y-axis by the given increment.
  979.  
  980. @method rotateY
  981. @param {Number} angle Angle increment in degrees.
  982. */
  983. rotateY(angle) {
  984. return this.rotate(yAxis, angle);
  985. }
  986.  
  987. /**
  988. Rotates about the local Z-axis by the given increment.
  989.  
  990. @method rotateZ
  991. @param {Number} angle Angle increment in degrees.
  992. */
  993. rotateZ(angle) {
  994. return this.rotate(zAxis, angle);
  995. }
  996.  
  997. /**
  998. Translates along local space vector by the given increment.
  999.  
  1000. @method translate
  1001. @param {Float32Array} axis Normalized local space 3D vector along which to translate.
  1002. @param {Number} distance Distance to translate along the vector.
  1003. */
  1004. translate(axis, distance) {
  1005. math.vec3ApplyQuaternion(this.quaternion, axis, veca);
  1006. math.mulVec3Scalar(veca, distance, vecb);
  1007. math.addVec3(this.position, vecb, this.position);
  1008. this._setLocalMatrixDirty();
  1009. this._setAABBDirty();
  1010. this._renderer.imageDirty();
  1011. return this;
  1012. }
  1013.  
  1014. /**
  1015. Translates along the local X-axis by the given increment.
  1016.  
  1017. @method translateX
  1018. @param {Number} distance Distance to translate along the X-axis.
  1019. */
  1020. translateX(distance) {
  1021. return this.translate(xAxis, distance);
  1022. }
  1023.  
  1024. /**
  1025. * Translates along the local Y-axis by the given increment.
  1026. *
  1027. * @method translateX
  1028. * @param {Number} distance Distance to translate along the Y-axis.
  1029. */
  1030. translateY(distance) {
  1031. return this.translate(yAxis, distance);
  1032. }
  1033.  
  1034. /**
  1035. Translates along the local Z-axis by the given increment.
  1036.  
  1037. @method translateX
  1038. @param {Number} distance Distance to translate along the Z-axis.
  1039. */
  1040. translateZ(distance) {
  1041. return this.translate(zAxis, distance);
  1042. }
  1043.  
  1044. /**
  1045. Globally unique identifier.
  1046.  
  1047. This is unique not only within the {{#crossLink "Scene"}}{{/crossLink}}, but throughout the entire universe.
  1048.  
  1049. Only defined when given to the constructor.
  1050.  
  1051. @property guid
  1052. @type String
  1053. @final
  1054. */
  1055. get guid() {
  1056. return this._guid;
  1057. }
  1058.  
  1059. /**
  1060. Optional entity classification when using within a semantic data model.
  1061.  
  1062. See the Object documentation on "Applying a semantic data model" for usage.
  1063.  
  1064. @property entityType
  1065. @default null
  1066. @type String
  1067. @final
  1068. */
  1069. get entityType() {
  1070. return this._entityType;
  1071. }
  1072.  
  1073. //------------------------------------------------------------------------------------------------------------------
  1074. // Children and parent properties
  1075. //------------------------------------------------------------------------------------------------------------------
  1076.  
  1077. /**
  1078. Number of child {{#crossLink "Object"}}Objects{{/crossLink}}.
  1079.  
  1080. @property numChildren
  1081. @final
  1082. @type Number
  1083. */
  1084. get numChildren() {
  1085. return this._childList.length;
  1086. }
  1087.  
  1088. /**
  1089. Array of child {{#crossLink "Object"}}Objects{{/crossLink}}.
  1090.  
  1091. @property children
  1092. @final
  1093. @type Array
  1094. */
  1095. get children() {
  1096. return this._childList;
  1097. }
  1098.  
  1099. /**
  1100. Child {{#crossLink "Object"}}Objects{{/crossLink}} mapped to their IDs.
  1101.  
  1102. @property childMap
  1103. @final
  1104. @type {*}
  1105. */
  1106. get childMap() {
  1107. return this._childMap;
  1108. }
  1109.  
  1110. /**
  1111. IDs of child {{#crossLink "Object"}}Objects{{/crossLink}}.
  1112.  
  1113. @property childIDs
  1114. @final
  1115. @type Array
  1116. */
  1117. get childIDs() {
  1118. if (!this._childIDs) {
  1119. this._childIDs = Object.keys(this._childMap);
  1120. }
  1121. return this._childIDs;
  1122. }
  1123.  
  1124. /**
  1125. The parent.
  1126.  
  1127. The parent Group may also be set by passing the Object to the
  1128. Group/Model's {{#crossLink "Group/addChild:method"}}addChild(){{/crossLink}} method.
  1129.  
  1130. @property parent
  1131. @type Group
  1132. */
  1133. set parent(object) {
  1134. if (utils.isNumeric(object) || utils.isString(object)) {
  1135. const objectId = object;
  1136. object = this.scene.objects[objectId];
  1137. if (!object) {
  1138. this.warn("Group not found: " + utils.inQuotes(objectId));
  1139. return;
  1140. }
  1141. }
  1142. if (object.scene.id !== this.scene.id) {
  1143. this.error("Group not in same Scene: " + object.id);
  1144. return;
  1145. }
  1146. if (this._parent && this._parent.id === object.id) {
  1147. this.warn("Already a child of Group: " + object.id);
  1148. return;
  1149. }
  1150. object.addChild(this);
  1151. }
  1152.  
  1153. get parent() {
  1154. return this._parent;
  1155. }
  1156.  
  1157. //------------------------------------------------------------------------------------------------------------------
  1158. // Transform properties
  1159. //------------------------------------------------------------------------------------------------------------------
  1160.  
  1161. /**
  1162. Local translation.
  1163.  
  1164. @property position
  1165. @default [0,0,0]
  1166. @type {Float32Array}
  1167. */
  1168. set position(value) {
  1169. this._position.set(value || [0, 0, 0]);
  1170. this._setLocalMatrixDirty();
  1171. this._setAABBDirty();
  1172. this._renderer.imageDirty();
  1173. }
  1174.  
  1175. get position() {
  1176. return this._position;
  1177. }
  1178.  
  1179. /**
  1180. Local rotation, as Euler angles given in degrees, for each of the X, Y and Z axis.
  1181.  
  1182. @property rotation
  1183. @default [0,0,0]
  1184. @type {Float32Array}
  1185. */
  1186. set rotation(value) {
  1187. this._rotation.set(value || [0, 0, 0]);
  1188. math.eulerToQuaternion(this._rotation, "XYZ", this._quaternion);
  1189. this._setLocalMatrixDirty();
  1190. this._setAABBDirty();
  1191. this._renderer.imageDirty();
  1192. }
  1193.  
  1194. get rotation() {
  1195. return this._rotation;
  1196. }
  1197.  
  1198. /**
  1199. Local rotation quaternion.
  1200.  
  1201. @property quaternion
  1202. @default [0,0,0, 1]
  1203. @type {Float32Array}
  1204. */
  1205. set quaternion(value) {
  1206. this._quaternion.set(value || [0, 0, 0, 1]);
  1207. math.quaternionToEuler(this._quaternion, "XYZ", this._rotation);
  1208. this._setLocalMatrixDirty();
  1209. this._setAABBDirty();
  1210. this._renderer.imageDirty();
  1211. }
  1212.  
  1213. get quaternion() {
  1214. return this._quaternion;
  1215. }
  1216.  
  1217. /**
  1218. Local scale.
  1219.  
  1220. @property scale
  1221. @default [1,1,1]
  1222. @type {Float32Array}
  1223. */
  1224. set scale(value) {
  1225. this._scale.set(value || [1, 1, 1]);
  1226. this._setLocalMatrixDirty();
  1227. this._setAABBDirty();
  1228. this._renderer.imageDirty();
  1229. }
  1230.  
  1231. get scale() {
  1232. return this._scale;
  1233. }
  1234.  
  1235. /**
  1236. * Local matrix.
  1237. *
  1238. * @property matrix
  1239. * @default [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
  1240. * @type {Float32Array}
  1241. */
  1242. set matrix(value) {
  1243.  
  1244. if (!this.__localMatrix) {
  1245. this.__localMatrix = math.identityMat4();
  1246. }
  1247. this.__localMatrix.set(value || identityMat);
  1248. math.decomposeMat4(this.__localMatrix, this._position, this._quaternion, this._scale);
  1249. this._localMatrixDirty = false;
  1250. this._setWorldMatrixDirty();
  1251. this._setAABBDirty();
  1252. this._renderer.imageDirty();
  1253. }
  1254.  
  1255. get matrix() {
  1256. if (this._localMatrixDirty) {
  1257. if (!this.__localMatrix) {
  1258. this.__localMatrix = math.identityMat4();
  1259. }
  1260. math.composeMat4(this._position, this._quaternion, this._scale, this.__localMatrix);
  1261. this._localMatrixDirty = false;
  1262. }
  1263. return this.__localMatrix;
  1264. }
  1265.  
  1266. /**
  1267. * The World matrix.
  1268. *
  1269. * @property worldMatrix
  1270. * @type {Float32Array}
  1271. */
  1272. get worldMatrix() {
  1273. if (this._worldMatrixDirty) {
  1274. this._buildWorldMatrix();
  1275. }
  1276. return this._worldMatrix;
  1277. }
  1278.  
  1279. /**
  1280. * This World normal matrix.
  1281. *
  1282. * @property worldNormalMatrix
  1283. * @default [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
  1284. * @type {Float32Array}
  1285. */
  1286. get worldNormalMatrix() {
  1287. if (this._worldNormalMatrixDirty) {
  1288. this._buildWorldNormalMatrix();
  1289. }
  1290. return this._worldNormalMatrix;
  1291. }
  1292.  
  1293. // worldPosition: {
  1294. // get: function (optionalTarget) {
  1295. // var result = optionalTarget || new Vector3();
  1296. // this.updateMatrixWorld(true);
  1297. // return result.setFromMatrixPosition(this.matrixWorld);
  1298. // }
  1299. // },
  1300. //
  1301. // worldQuaternion: {
  1302. // get: function () {
  1303. // var position = new Vector3();
  1304. // var scale = new Vector3();
  1305. // return function getWorldQuaternion(optionalTarget) {
  1306. // var result = optionalTarget || new Quaternion();
  1307. // this.updateMatrixWorld(true);
  1308. // this.matrixWorld.decompose(position, result, scale);
  1309. // return result;
  1310. // };
  1311. // }()
  1312. // },
  1313. //
  1314. // worldRotation: {
  1315. // get: function () {
  1316. // var quaternion = new Quaternion();
  1317. // return function getWorldRotation(optionalTarget) {
  1318. // var result = optionalTarget || new Euler();
  1319. // this.getWorldQuaternion(quaternion);
  1320. // return result.setFromQuaternion(quaternion, this.rotation.order, false)
  1321. // };
  1322. // }
  1323. // }(),
  1324. //
  1325. // worldScale: {
  1326. // get: (function () {
  1327. // var position = new Float32Array(3);
  1328. // var quaternion = new Float32Array(4);
  1329. // return function getWorldScale(optionalTarget) {
  1330. // var result = optionalTarget || new Float32Array(3);
  1331. // math.decomposeMat4(this.worldMatrix, position, quaternion, result);
  1332. // return result;
  1333. // };
  1334. // })()
  1335. // },
  1336. //
  1337. // worldDirection: {
  1338. // get: (function () {
  1339. // var quaternion = new Quaternion();
  1340. // return function getWorldDirection(optionalTarget) {
  1341. // var result = optionalTarget || new Vector3();
  1342. // this.getWorldQuaternion(quaternion);
  1343. // return result.set(0, 0, 1).applyQuaternion(quaternion);
  1344. // };
  1345. // })()
  1346. // },
  1347.  
  1348. //------------------------------------------------------------------------------------------------------------------
  1349. // Boundary properties
  1350. //------------------------------------------------------------------------------------------------------------------
  1351.  
  1352. /**
  1353. World-space 3D axis-aligned bounding box (AABB).
  1354.  
  1355. Represented by a six-element Float32Array containing the min/max extents of the
  1356. axis-aligned volume, ie. ````[xmin, ymin,zmin,xmax,ymax, zmax]````.
  1357.  
  1358. @property aabb
  1359. @final
  1360. @type {Float32Array}
  1361. */
  1362. get aabb() {
  1363. if (this._aabbDirty) {
  1364. this._updateAABB();
  1365. }
  1366. return this._aabb;
  1367. }
  1368.  
  1369. /**
  1370. World-space 3D center.
  1371.  
  1372. @property center
  1373. @final
  1374. @type {Float32Array}
  1375. */
  1376. get center() {
  1377. if (this._aabbDirty) {
  1378. this._updateAABB();
  1379. }
  1380. return this._aabbCenter;
  1381. }
  1382.  
  1383. /**
  1384. Indicates if visible.
  1385.  
  1386. Only rendered when {{#crossLink "Object/visible:property"}}{{/crossLink}} is true and
  1387. {{#crossLink "Object/culled:property"}}{{/crossLink}} is false.
  1388.  
  1389. Each visible Object is registered in its {{#crossLink "Scene"}}{{/crossLink}}'s
  1390. {{#crossLink "Scene/visibleEntities:property"}}{{/crossLink}} map while its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
  1391. is set to a value.
  1392.  
  1393. @property visible
  1394. @default true
  1395. @type Boolean
  1396. */
  1397. set visible(visible) {
  1398. visible = visible !== false;
  1399. this._visible = visible;
  1400. for (let i = 0, len = this._childList.length; i < len; i++) {
  1401. this._childList[i].visible = visible;
  1402. }
  1403. if (this._entityType) {
  1404. this.scene._entityVisibilityUpdated(this, visible);
  1405. }
  1406. }
  1407.  
  1408. get visible() {
  1409. return this._visible;
  1410. }
  1411.  
  1412. /**
  1413. Indicates if highlighted.
  1414.  
  1415. Each highlighted Object is registered in its {{#crossLink "Scene"}}{{/crossLink}}'s
  1416. {{#crossLink "Scene/highlightedEntities:property"}}{{/crossLink}} map while its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
  1417. is set to a value.
  1418.  
  1419. @property highlighted
  1420. @default false
  1421. @type Boolean
  1422. */
  1423. set highlighted(highlighted) {
  1424. highlighted = !!highlighted;
  1425. this._highlighted = highlighted;
  1426. for (let i = 0, len = this._childList.length; i < len; i++) {
  1427. this._childList[i].highlighted = highlighted;
  1428. }
  1429. if (this._entityType) {
  1430. this.scene._entityHighlightedUpdated(this, highlighted);
  1431. }
  1432. }
  1433.  
  1434. get highlighted() {
  1435. return this._highlighted;
  1436. }
  1437.  
  1438. /**
  1439. Indicates if ghosted.
  1440.  
  1441. Each ghosted Object is registered in its {{#crossLink "Scene"}}{{/crossLink}}'s
  1442. {{#crossLink "Scene/ghostedEntities:property"}}{{/crossLink}} map while its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
  1443. is set to a value.
  1444.  
  1445. @property ghosted
  1446. @default false
  1447. @type Boolean
  1448. */
  1449. set ghosted(ghosted) {
  1450. ghosted = !!ghosted;
  1451. this._ghosted = ghosted;
  1452. for (let i = 0, len = this._childList.length; i < len; i++) {
  1453. this._childList[i].ghosted = ghosted;
  1454. }
  1455. if (this._entityType) {
  1456. this.scene._entityGhostedUpdated(this, ghosted);
  1457. }
  1458. }
  1459.  
  1460. get ghosted() {
  1461. return this._ghosted;
  1462. }
  1463.  
  1464. /**
  1465. Indicates if selected.
  1466.  
  1467. Each selected Object is registered in its {{#crossLink "Scene"}}{{/crossLink}}'s
  1468. {{#crossLink "Scene/selectedEntities:property"}}{{/crossLink}} map while its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
  1469. is set to a value.
  1470.  
  1471. @property selected
  1472. @default false
  1473. @type Boolean
  1474. */
  1475. set selected(selected) {
  1476. selected = !!selected;
  1477. this._selected = selected;
  1478. for (let i = 0, len = this._childList.length; i < len; i++) {
  1479. this._childList[i].selected = selected;
  1480. }
  1481. if (this._entityType) {
  1482. this.scene._entitySelectedUpdated(this, selected);
  1483. }
  1484. }
  1485.  
  1486. get selected() {
  1487. return this._selected;
  1488. }
  1489.  
  1490. /**
  1491. Indicates if edges are emphasized.
  1492.  
  1493. @property edges
  1494. @default false
  1495. @type Boolean
  1496. */
  1497. set edges(edges) {
  1498. edges = !!edges;
  1499. this._edges = edges;
  1500. for (let i = 0, len = this._childList.length; i < len; i++) {
  1501. this._childList[i].edges = edges;
  1502. }
  1503. }
  1504.  
  1505. get edges() {
  1506. return this._edges;
  1507. }
  1508.  
  1509. /**
  1510. Indicates if culled from view.
  1511.  
  1512. Only rendered when {{#crossLink "Object/visible:property"}}{{/crossLink}} is true and
  1513. {{#crossLink "Object/culled:property"}}{{/crossLink}} is false.
  1514.  
  1515. @property culled
  1516. @default false
  1517. @type Boolean
  1518. */
  1519. set culled(culled) {
  1520. culled = !!culled;
  1521. this._culled = culled;
  1522. for (let i = 0, len = this._childList.length; i < len; i++) {
  1523. this._childList[i].culled = culled;
  1524. }
  1525. }
  1526.  
  1527. get culled() {
  1528. return this._culled;
  1529. }
  1530.  
  1531. /**
  1532. Indicates if clippable.
  1533.  
  1534. Clipping is done by the {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Clips"}}{{/crossLink}} component.
  1535.  
  1536. @property clippable
  1537. @default true
  1538. @type Boolean
  1539. */
  1540. set clippable(clippable) {
  1541. clippable = clippable !== false;
  1542. this._clippable = clippable;
  1543. for (let i = 0, len = this._childList.length; i < len; i++) {
  1544. this._childList[i].clippable = clippable;
  1545. }
  1546. }
  1547.  
  1548. get clippable() {
  1549. return this._clippable;
  1550. }
  1551.  
  1552. /**
  1553. Indicates if included in boundary calculations.
  1554.  
  1555. @property collidable
  1556. @default true
  1557. @type Boolean
  1558. */
  1559. set collidable(collidable) {
  1560. collidable = collidable !== false;
  1561. this._collidable = collidable;
  1562. for (let i = 0, len = this._childList.length; i < len; i++) {
  1563. this._childList[i].collidable = collidable;
  1564. }
  1565. }
  1566.  
  1567. get collidable() {
  1568. return this._collidable;
  1569. }
  1570.  
  1571. /**
  1572. Whether or not to allow picking.
  1573.  
  1574. Picking is done via calls to {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}}.
  1575.  
  1576. @property pickable
  1577. @default true
  1578. @type Boolean
  1579. */
  1580. set pickable(pickable) {
  1581. pickable = pickable !== false;
  1582. this._pickable = pickable;
  1583. for (let i = 0, len = this._childList.length; i < len; i++) {
  1584. this._childList[i].pickable = pickable;
  1585. }
  1586. }
  1587.  
  1588. get pickable() {
  1589. return this._pickable;
  1590. }
  1591.  
  1592. /**
  1593. RGB colorize color, multiplies by the rendered fragment color.
  1594.  
  1595. @property colorize
  1596. @default [1.0, 1.0, 1.0]
  1597. @type Float32Array
  1598. */
  1599. set colorize(rgb) {
  1600. let colorize = this._colorize;
  1601. if (!colorize) {
  1602. colorize = this._colorize = new Float32Array(4);
  1603. colorize[3] = 1.0;
  1604. }
  1605. if (rgb) {
  1606. colorize[0] = rgb[0];
  1607. colorize[1] = rgb[1];
  1608. colorize[2] = rgb[2];
  1609. } else {
  1610. colorize[0] = 1;
  1611. colorize[1] = 1;
  1612. colorize[2] = 1;
  1613. }
  1614. for (let i = 0, len = this._childList.length; i < len; i++) {
  1615. this._childList[i].colorize = colorize;
  1616. }
  1617. }
  1618.  
  1619. get colorize() {
  1620. return this._colorize.slice(0, 3);
  1621. }
  1622.  
  1623. /**
  1624. Opacity factor, multiplies by the rendered fragment alpha.
  1625.  
  1626. This is a factor in range ````[0..1]````.
  1627.  
  1628. @property opacity
  1629. @default 1.0
  1630. @type Number
  1631. */
  1632. set opacity(opacity) {
  1633. let colorize = this._colorize;
  1634. if (!colorize) {
  1635. colorize = this._colorize = new Float32Array(4);
  1636. colorize[0] = 1;
  1637. colorize[1] = 1;
  1638. colorize[2] = 1;
  1639. }
  1640. colorize[3] = opacity !== null && opacity !== undefined ? opacity : 1.0;
  1641. for (let i = 0, len = this._childList.length; i < len; i++) {
  1642. this._childList[i].opacity = opacity;
  1643. }
  1644. }
  1645.  
  1646. get opacity() {
  1647. return this._colorize[3];
  1648. }
  1649.  
  1650. /**
  1651. Indicates if outlined.
  1652.  
  1653. @property outlined
  1654. @default false
  1655. @type Boolean
  1656. */
  1657. set outlined(outlined) {
  1658. outlined = !!outlined;
  1659. this._outlined = outlined;
  1660. for (let i = 0, len = this._childList.length; i < len; i++) {
  1661. this._childList[i].outlined = outlined;
  1662. }
  1663. }
  1664.  
  1665. get outlined() {
  1666. return this._outlined;
  1667. }
  1668.  
  1669. /**
  1670. Indicates if casting shadows.
  1671.  
  1672. @property castShadow
  1673. @default true
  1674. @type Boolean
  1675. */
  1676. set castShadow(castShadow) {
  1677. castShadow = !!castShadow;
  1678. this._castShadow = castShadow;
  1679. for (let i = 0, len = this._childList.length; i < len; i++) {
  1680. this._childList[i].castShadow = castShadow;
  1681. }
  1682. }
  1683.  
  1684. get castShadow() {
  1685. return this._castShadow;
  1686. }
  1687.  
  1688. /**
  1689. Indicates if receiving shadows.
  1690.  
  1691. @property receiveShadow
  1692. @default true
  1693. @type Boolean
  1694. */
  1695. set receiveShadow(receiveShadow) {
  1696. receiveShadow = !!receiveShadow;
  1697. this._receiveShadow = receiveShadow;
  1698. for (let i = 0, len = this._childList.length; i < len; i++) {
  1699. this._childList[i].receiveShadow = receiveShadow;
  1700. }
  1701. }
  1702.  
  1703. get receiveShadow() {
  1704. return this._receiveShadow;
  1705. }
  1706.  
  1707. /**
  1708. Indicates if the 3D World-space axis-aligned bounding box (AABB) is visible.
  1709.  
  1710. @property aabbVisible
  1711. @default false
  1712. @type {Boolean}
  1713. */
  1714. set aabbVisible(visible) {
  1715. if (!visible && !this._aabbHelper) {
  1716. return;
  1717. }
  1718. if (!this._aabbHelper) {
  1719. this._aabbHelper = new Mesh(this, {
  1720. geometry: new AABBGeometry(this, {
  1721. target: this
  1722. }),
  1723. material: new PhongMaterial(this, {
  1724. diffuse: [0.5, 1.0, 0.5],
  1725. emissive: [0.5, 1.0, 0.5],
  1726. lineWidth: 2
  1727. })
  1728. });
  1729. }
  1730. this._aabbHelper.visible = visible;
  1731. }
  1732.  
  1733. get aabbVisible() {
  1734. return this._aabbHelper ? this._aabbHelper.visible : false;
  1735. }
  1736.  
  1737. destroy() {
  1738. super.destroy();
  1739. if (this._parent) {
  1740. this._parent.removeChild(this);
  1741. }
  1742. if (this._entityType) {
  1743. const scene = this.scene;
  1744. scene._entityTypeRemoved(this, this._entityType);
  1745. if (this._visible) {
  1746. scene._entityVisibilityUpdated(this, false);
  1747. }
  1748. if (this._ghosted) {
  1749. scene._entityGhostedUpdated(this, false);
  1750. }
  1751. if (this._selected) {
  1752. scene._entitySelectedUpdated(this, false);
  1753. }
  1754. if (this._highlighted) {
  1755. scene._entityHighlightedUpdated(this, false);
  1756. }
  1757. }
  1758. if (this._childList.length) {
  1759. // Clone the _childList before iterating it, so our children don't mess us up when calling removeChild().
  1760. const tempChildList = this._childList.splice();
  1761. let object;
  1762. for (let i = 0, len = tempChildList.length; i < len; i++) {
  1763. object = tempChildList[i];
  1764. object.destroy();
  1765. }
  1766. }
  1767. this._childList = [];
  1768. this._childMap = {};
  1769. this._childIDs = null;
  1770. this._setAABBDirty();
  1771. this.scene._aabbDirty = true;
  1772. this.scene._objectDestroyed(this);
  1773. }
  1774. }
  1775.  
  1776. componentClasses[type] = xeoglObject;
  1777.  
  1778. export {xeoglObject};
  1779.