PageRenderTime 2620ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/src/sandy/core/scenegraph/Shape3D.hx

http://github.com/sandy3d/sandy-hx
Haxe | 982 lines | 601 code | 84 blank | 297 comment | 130 complexity | c1c9be7972ac6491e30c5894bcacbedc MD5 | raw file
  1. package sandy.core.scenegraph;
  2. import flash.Lib;
  3. import flash.display.Sprite;
  4. import flash.events.Event;
  5. import flash.events.MouseEvent;
  6. import flash.geom.Point;
  7. #if js
  8. import Html5Dom;
  9. #end
  10. import sandy.bounds.BBox;
  11. import sandy.bounds.BSphere;
  12. import sandy.core.Scene3D;
  13. import sandy.core.data.BSPNode;
  14. import sandy.core.data.Matrix4;
  15. import sandy.core.data.Point3D;
  16. import sandy.core.data.Polygon;
  17. import sandy.core.data.UVCoord;
  18. import sandy.core.data.Vertex;
  19. import sandy.events.BubbleEvent;
  20. import sandy.events.Shape3DEvent;
  21. import sandy.materials.Appearance;
  22. import sandy.materials.Material;
  23. import sandy.materials.WireFrameMaterial;
  24. import sandy.math.IntersectionMath;
  25. import sandy.view.CullingState;
  26. import sandy.view.Frustum;
  27. import sandy.util.ArrayUtil;
  28. import sandy.HaxeTypes;
  29. /**
  30. * The Shape3D class is the base class of all true 3D shapes.
  31. *
  32. * <p>It represents a node in the object tree of the world.<br/>
  33. * A Shape3D is a leaf node and can not have any child nodes.</p>
  34. * <p>It must be the child of a branch group or a transform group,
  35. * but transformations can be applied to the Shape directly.</p>
  36. *
  37. * @author Thomas Pfeiffer - kiroukou
  38. * @author Niel Drummond - haXe port
  39. * @author Russell Weir - haXe port
  40. *
  41. */
  42. class Shape3D extends ATransformable, implements IDisplayable
  43. {
  44. /**
  45. * Animated flag.
  46. * <p>If the geometry vertices are dynamically modified by some animation engine or mathematic function, some polygon may disapear with no reason.
  47. * The normal Point3D is used to compute the polygon visibility, and if you don't update the normal Point3D after the vertices modifications, there's an error.
  48. * To fix that problem, Sandy3D offers that new property appeared in 3.0.3 release, which once set to true, automatically update the normal Point3Ds for you.
  49. * As a performance warning, don't set this value to true if your model geometry isn't animated.</p>
  50. */
  51. public var animated:Bool;
  52. /**
  53. * The array of polygons building this object.
  54. */
  55. public var aPolygons:Array<Polygon>;
  56. /**
  57. * Array containing the visible polygons of that shape.
  58. * Contente is available after the SCENE_RENDER_DISPLAYLIST
  59. * event of the current scene has been dispatched
  60. */
  61. public var aVisiblePolygons(default,null) : Array<Polygon>;
  62. /**
  63. * The container for this object.
  64. * This container property exist if the useSingleContainer is set to true.
  65. * It is a direct access to the Shape3D container to, for example, apply nice effects such as filters etc.
  66. */
  67. public var container(__getContainer,null):Sprite;
  68. /**
  69. * The depth of this object.
  70. * In case the useSingleContainer mode is enabled (default mode), this
  71. * value returns the means depth of the Shape in the camera frame.
  72. * This value is mainly used as a z-sorting value.
  73. */
  74. public var depth(__getDepth,__setDepth):Float;
  75. /**
  76. * <p>
  77. * Enable the Frustum clipping on the visible polygons.
  78. * Enable this when you need a perfect intersection between the camera and some object shapes.
  79. * In case you need to make the camera look inside and outide a box, or other immerssive things.</p>
  80. *
  81. * <p>Important: Enable the clipping makes process a bit slower, especially with big scenes.</p>
  82. *
  83. * <p>Specify if this object polygons should be clipped against the camera frustum planes.</p>
  84. */
  85. public var enableClipping(__getEnableClipping,__setEnableClipping):Bool;
  86. /**
  87. * Should forced depth be enable for this object?.
  88. *
  89. * <p>If true it is possible to force this object to be drawn at a specific depth,<br/>
  90. * if false the normal Z-sorting algorithm is applied.</p>
  91. * <p>When correctly used, this feature allows you to aVoid some Z-sorting problems.</p>
  92. */
  93. public var enableForcedDepth:Bool;
  94. /**
  95. * <p>
  96. * Enable the Frustum near plane clipping on the visible polygons.
  97. * Enable this when you need a perfect intersection between the front camera plane.
  98. * This is mainly used when you need the camera to move on a long plane.</p>
  99. *
  100. * <p>Important: Enable the clipping makes process a bit slower, especially with big scenes.</p>
  101. */
  102. public var enableNearClipping:Bool;
  103. /**
  104. * The forced depth for this object.
  105. *
  106. * <p>To make this feature work, you must enable the ForcedDepth system too.<br/>
  107. * The higher the depth is, the sooner the more far the object will be represented.</p>
  108. */
  109. public var forcedDepth:Float;
  110. /**
  111. * The geometry of this object.
  112. */
  113. public var geometry(__getGeometry,__setGeometry):Geometry3D;
  114. /**
  115. * Change the geometryCenter of the Shape3D.
  116. * To change the geometryCenter point of a shape, simply set this geometryCenter property.
  117. * The geometryCenter property requires a Point3D. This Point3D is an position offset relative to the original geometry one.
  118. * For example, a Sphere primitive creates automatically a geometry which center is the 0,0,0 position. If you rotate this sphere as this,
  119. * it will rotate around its center.
  120. * Now if you set the geometryCenter property, this rotation center will change.
  121. *
  122. * The updateBoundingVolumes method which does update the bounding volumes to enable a correct frustum culling is automatically called.
  123. *
  124. * @example To change the geometryCenter center at runtime
  125. * <listing version="3.1">
  126. * var l_oSphere:Sphere = new Sphere("mySphere", 50, 3 );
  127. * // Change the rotation reference to -50 offset in Y direction from the orinal one
  128. * // and that corresponds to the bottom of the sphere
  129. * l_oSphere.geometryCenter = new Point3D( 0, -50, 0 );
  130. * l_oSphere.rotateZ = 45;
  131. * </listing>
  132. */
  133. public var geometryCenter(__getGeometryCenter,__setGeometryCenter):Point3D;
  134. /**
  135. * Returns the material currently used by the renderer
  136. * @return Material the material used to render
  137. */
  138. public var material(__getMaterial,__setMaterial):Material;
  139. /**
  140. * No sorting.
  141. * Only convex shapes are guaranteed to display correctly in this mode.
  142. */
  143. public static inline var SORT_NONE:Int = 0;
  144. /**
  145. * Average distance sorting.
  146. * Default sorting mode.
  147. * Carefully designed models will display just fine, but ordering problems are common.
  148. * This is also the only possible sorting mode with <code>useSingleContainer</code> set to <code>false</code>.
  149. */
  150. public static inline var SORT_AVGZ:Int = 1;
  151. /**
  152. * In this mode mesh is sorted using BSP tree, but no faces are split for you (means sorting problems are still possible).
  153. * Experimental.
  154. */
  155. public static inline var SORT_LAZY_BSP:Int = 2;
  156. /**
  157. * In this mode mesh is sorted using BSP tree, but no tree is built for you (you need to set <code>bsp</code> property yourself).
  158. * Experimental.
  159. */
  160. public static inline var SORT_CUSTOM_BSP:Int = 3;
  161. /**
  162. * Root node of BSP tree.
  163. */
  164. public var bsp:BSPNode;
  165. #if (js && SANDY_WEBGL)
  166. public var vertexPositionBuffer(default,null):WebGLBuffer;
  167. public var texCoordBuffer(default,null):WebGLBuffer;
  168. public var indicesBuffer(default,null):WebGLBuffer;
  169. public var shaderProgram(default,null):WebGLProgram;
  170. public var uniforms(default,null):Hash<WebGLUniformLocation>;
  171. #end
  172. /**
  173. * Creates a 3D object
  174. *
  175. * <p>This creates a new 3D geometry object. That object will handle the rendering of a static Geometry3D object into a real 3D object and finally to the 2D camera representation.</p>
  176. *
  177. * @param p_sName A string identifier for this object
  178. * @param p_oGeometry The geometry of this object
  179. * @param p_oAppearance The appearance of this object. If no apperance is given, the DEFAULT_APPEARANCE will be applied.
  180. * @param p_bUseSingleContainer Whether tis object should use a single container to draw on
  181. */
  182. public function new( ?p_sName:String = "", ?p_oGeometry:Geometry3D, ?p_oAppearance:Appearance, ?p_bUseSingleContainer:Bool=true )
  183. {
  184. // public initializers
  185. aPolygons = new Array();
  186. enableNearClipping = false;
  187. enableClipping = false;
  188. enableForcedDepth = false;
  189. forcedDepth = 0;
  190. animated = false;
  191. aVisiblePolygons = new Array();
  192. // private initializers
  193. m_bEv = false;
  194. m_oGeomCenter = new Point3D();
  195. m_bBackFaceCulling = true;
  196. m_bWasOver = false;
  197. m_bUseSingleContainer = true;
  198. m_nDepth = 0;
  199. m_bMouseInteractivity = false;
  200. m_bForcedSingleContainer = false;
  201. m_nSortingMode = SORT_AVGZ;
  202. super( p_sName );
  203. // -- Add this graphical object to the World display list
  204. m_oContainer = new Sprite();
  205. m_oContainer.name = name;
  206. // --
  207. geometry = p_oGeometry;
  208. // -- HACK to make sure that the correct container system will be applied
  209. m_bUseSingleContainer = !p_bUseSingleContainer;
  210. #if (js && SANDY_WEBGL)
  211. // -- useSingleContainer is forced on openGL, because polygon-based occlusion is not yet implemented here
  212. if (Lib.mOpenGL)
  213. {
  214. useSingleContainer = true;
  215. uniforms = new Hash();
  216. } else
  217. #end
  218. useSingleContainer = p_bUseSingleContainer;
  219. // --
  220. appearance = ( p_oAppearance != null ) ? p_oAppearance : new Appearance( new WireFrameMaterial() );
  221. // --
  222. updateBoundingVolumes();
  223. }
  224. /**
  225. * Clears the graphics object of this object's container.
  226. *
  227. * <p>The the graphics that were drawn on the Graphics object is erased,
  228. * and the fill and line style settings are reset.</p>
  229. */
  230. public function clear():Void
  231. {
  232. if( m_oContainer != null )
  233. m_oContainer.graphics.clear();
  234. changed = true;
  235. }
  236. /**
  237. * This method returns a clone of this Shape3D.
  238. * The current appearance will be applied, and the geometry is cloned (not referenced to curent one).
  239. *
  240. * @param p_sName The name of the new shape you are going to create
  241. * @param p_bKeepTransform Boolean value which, if set to true, applies the current local transformations to the cloned shape. Default value is false.
  242. *
  243. * @return The clone
  244. */
  245. public function clone( ?p_sName:String = "", ?p_bKeepTransform:Bool=false ):Shape3D
  246. {
  247. var o = new Shape3D( p_sName, null, appearance, m_bUseSingleContainer);
  248. o.copy(this, p_bKeepTransform);
  249. return o;
  250. }
  251. /**
  252. * Tests this node against the camera frustum to get its visibility.
  253. *
  254. * <p>If this node and its children are not within the frustum,
  255. * the node is set to cull and it would not be displayed.<p/>
  256. * <p>The method also updates the bounding volumes to make the more accurate culling system possible.<br/>
  257. * First the bounding sphere is updated, and if intersecting,
  258. * the bounding box is updated to perform the more precise culling.</p>
  259. * <p><b>[MANDATORY] The update method must be called first!</b></p>
  260. *
  261. * @param p_oScene The current scene
  262. * @param p_oFrustum The frustum of the current camera
  263. * @param p_oViewMatrix The view martix of the curren camera
  264. * @param p_bChanged
  265. */
  266. public override function cull( p_oFrustum:Frustum, p_oViewMatrix:Matrix4, p_bChanged:Bool ):Void
  267. {
  268. super.cull( p_oFrustum, p_oViewMatrix, p_bChanged );
  269. if( culled == Frustum.OUTSIDE ) return;
  270. /////////////////////////
  271. //// BOUNDING SPHERE ////
  272. /////////////////////////
  273. boundingSphere.transform( viewMatrix );
  274. culled = p_oFrustum.sphereInFrustum( boundingSphere );
  275. // --
  276. if( culled == Frustum.INTERSECT )
  277. {
  278. ////////////////////////
  279. //// BOUNDING BOX ////
  280. ////////////////////////
  281. culled = p_oFrustum.boxInFrustum( boundingBox.transform( viewMatrix ) );
  282. }
  283. // --
  284. if( culled != CullingState.OUTSIDE && m_oAppearance != null )
  285. {
  286. scene.renderer.addToDisplayList(this);
  287. }
  288. if( m_bEv || m_bMouseInteractivity )
  289. {
  290. if( m_bWasOver == true && m_oLastContainer.hitTestPoint(m_oLastContainer.mouseX, m_oLastContainer.mouseY) == false )
  291. {
  292. m_oEB.dispatchEvent( new Shape3DEvent( MouseEvent.MOUSE_OUT, this, m_oLastEvent.polygon, m_oLastEvent.uv, m_oLastEvent.point, m_oLastEvent.event ) );
  293. m_bWasOver = false;
  294. if( m_oLastContainer != m_oContainer )
  295. {
  296. m_oLastEvent.polygon._onTextureInteraction( m_oLastEvent.event );
  297. m_oLastEvent.polygon._stopMouseInteraction();
  298. }
  299. }
  300. }
  301. }
  302. /**
  303. * Performs a z-sorting and renders the objects visible polygons.
  304. *
  305. * <p>The method is called only if the object renders on a single container<br/>
  306. * - ( useSingleContainer = true ).</p>
  307. *
  308. * @param p_oScene The current scene
  309. * @param p_oContainer The container to draw on
  310. */
  311. public function display( ?p_oContainer:Sprite ):Void
  312. {
  313. #if (js && SANDY_WEBGL)
  314. if (Lib.mOpenGL)
  315. m_oContainer.MatrixUniforms = callback( m_oAppearance.frontMaterial.setMatrixUniformsGL, this, m_oContainer );
  316. else
  317. #end
  318. // --
  319. // not using static consts here for speed
  320. if (m_nSortingMode < SORT_LAZY_BSP ) {
  321. // old sorting methods
  322. if ((m_nSortingMode == SORT_AVGZ ) || (m_bBackFaceCulling == false)) {
  323. ArrayUtil.sortOnLite(aVisiblePolygons,["m_nDepth"],ArrayUtil.SORT_NUMERIC | ArrayUtil.SORT_DESCENDING);
  324. }
  325. for (l_oFace in aVisiblePolygons)
  326. l_oFace.display (m_oContainer);
  327. } else {
  328. // new experimental BSP sorting
  329. var camPt:Point3D = new Point3D (
  330. scene.camera.modelMatrix.n14,
  331. scene.camera.modelMatrix.n24,
  332. scene.camera.modelMatrix.n34
  333. ); // cam -> world
  334. invModelMatrix.transform (camPt); // world -> local
  335. displayBSPTree (bsp, camPt);
  336. }
  337. }
  338. private function displayBSPTree (tree:BSPNode, camPt:Point3D):Void {
  339. var face:Polygon;
  340. var dist:Float = tree.plane.a * camPt.x + tree.plane.b * camPt.y + tree.plane.c * camPt.z + tree.plane.d;
  341. if (dist > 0) {
  342. // display negative, this, positive
  343. if (tree.negative != null)
  344. displayBSPTree (tree.negative, camPt);
  345. for (face in tree.faces)
  346. if (face.visible) // aVisiblePolygons.indexOf?
  347. face.display (m_oContainer);
  348. if (tree.positive != null)
  349. displayBSPTree (tree.positive, camPt);
  350. } else {
  351. // display positive, this, negative
  352. if (tree.positive != null)
  353. displayBSPTree (tree.positive, camPt);
  354. for (face in tree.faces)
  355. if (face.visible) // aVisiblePolygons.indexOf?
  356. face.display (m_oContainer);
  357. if (tree.negative != null)
  358. displayBSPTree (tree.negative, camPt);
  359. }
  360. }
  361. /**
  362. * Destroy this object and all its faces
  363. * container object is removed, and graphics cleared. All polygons have their
  364. */
  365. public override function destroy():Void
  366. {
  367. // FIXME Fix it - it should be more like
  368. if( m_oGeometry != null ) m_oGeometry.dispose();
  369. if( m_oAppearance != null ) m_oAppearance.dispose();
  370. // --
  371. clear();
  372. if( m_oContainer != null )
  373. {
  374. if( m_oContainer.parent != null ) m_oContainer.parent.removeChild( m_oContainer );
  375. m_oContainer = null;
  376. }
  377. // --
  378. __destroyPolygons();
  379. m_oGeometry = null;
  380. aVisiblePolygons = null;
  381. aPolygons = null;
  382. boundingBox = null;
  383. boundingSphere = null;
  384. // --
  385. super.destroy();
  386. }
  387. /**
  388. * Sets SORT_NONE or SORT_AVGZ sorting mode. Deprecated.
  389. * @internal this is now here for backward compatibility only.
  390. */
  391. public function setConvexFlag (convex:Bool):Void
  392. {
  393. sortingMode = convex ? SORT_NONE : SORT_AVGZ;
  394. }
  395. /**
  396. * Changes the backface culling side.
  397. *
  398. * When you want to display a cube and you are outside the cube, you see its external faces.<br/>
  399. * The internal faces are not drawn due to back face culling
  400. *
  401. * In case you are inside the cube, by default Sandy's engine still doesn't draw the internal faces
  402. * (because you should not be in there).
  403. *
  404. * If you need to be only inside the cube, you can call this method to change which side is culled.
  405. * The faces will be visible only from the interior of the cube.
  406. *
  407. * If you want to be both on the inside and the outside, you want to make the faces visible from on both sides.
  408. * In that case you just have to set enableBackFaceCulling to false.
  409. */
  410. public function swapCulling():Void
  411. {
  412. for( v in aPolygons )
  413. {
  414. v.swapCulling();
  415. }
  416. changed = true;
  417. }
  418. /**
  419. * Returns a string representation of this object
  420. *
  421. * @return The fully qualified name of this object and its geometry
  422. */
  423. public override function toString ():String
  424. {
  425. return "sandy.core.scenegraph.Shape3D" + " " + m_oGeometry.toString();
  426. }
  427. /**
  428. * Updates the bounding volumes of this object.
  429. */
  430. public override function updateBoundingVolumes():Void
  431. {
  432. if( m_oGeometry != null )
  433. {
  434. boundingBox = BBox.create( m_oGeometry.aVertex );
  435. boundingSphere.resetFromBox(boundingBox);
  436. if( parent != null )
  437. parent.onChildBoundsChanged(this);
  438. }
  439. }
  440. /////////////////////////////////////////////////////////////////////
  441. ///// Getters / Setters /////
  442. /////////////////////////////////////////////////////////////////////
  443. // appearance
  444. private override function __getAppearance():Appearance
  445. {
  446. return m_oAppearance;
  447. }
  448. private override function __setAppearance( p_oApp:Appearance ):Appearance
  449. {
  450. // Now we register to the update event
  451. m_oAppearance = p_oApp;
  452. // --
  453. if( m_oGeometry != null )
  454. {
  455. for ( v in aPolygons )
  456. v.appearance = m_oAppearance;
  457. }
  458. #if (js && SANDY_WEBGL)
  459. if (Lib.mOpenGL)
  460. {
  461. m_oContainer.graphics.mShaderGL = m_oAppearance.frontMaterial.m_oShaderGL;
  462. m_oAppearance.frontMaterial.initGL( this, m_oContainer );
  463. var bufferData = new Hash();
  464. bufferData.set("aVertPos", { data: this.m_oGeometry.glVertices(), size:3 } );
  465. bufferData.set("aTexCoord", { data: this.m_oGeometry.glTexCoords(), size:2 });
  466. bufferData.set("aVertNorm", { data: this.m_oGeometry.glNormals(), size:3 });
  467. m_oContainer.SetBuffers(bufferData, this.m_oGeometry.glIndices());
  468. }
  469. #end
  470. changed = true;
  471. return p_oApp;
  472. }
  473. // container
  474. private function __getContainer():Sprite
  475. {
  476. return m_oContainer;
  477. }
  478. // depth
  479. private function __getDepth():Float
  480. {
  481. return m_nDepth;
  482. }
  483. private function __setDepth( p_nDepth:Float ):Float
  484. {
  485. m_nDepth = p_nDepth;
  486. changed = true;
  487. return p_nDepth;
  488. }
  489. // enableBackFaceCulling
  490. private override function __getEnableBackFaceCulling():Bool
  491. {
  492. return m_bBackFaceCulling;
  493. }
  494. private override function __setEnableBackFaceCulling( b:Bool ):Bool
  495. {
  496. if( b != m_bBackFaceCulling )
  497. {
  498. m_bBackFaceCulling = b;
  499. changed = true;
  500. }
  501. return b;
  502. }
  503. // enableClipping
  504. private function __getEnableClipping():Bool
  505. {
  506. return m_bClipping;
  507. }
  508. private override function __setEnableClipping( p_bClippingValue:Bool ):Bool
  509. {
  510. m_bClipping = p_bClippingValue;
  511. return p_bClippingValue;
  512. }
  513. // enableEvents (override from Node.hx)
  514. private override function __getEnableEvents():Bool
  515. {
  516. return m_bEv;
  517. }
  518. private override function __setEnableEvents( b:Bool ):Bool
  519. {
  520. // no change
  521. if( b == m_bEv )
  522. return b;
  523. if( b )
  524. subscribeEvents();
  525. else
  526. unsubscribeEvents();
  527. m_bEv = b;
  528. return b;
  529. }
  530. // enableInteractivity (from Node.hx)
  531. private override function __getEnableInteractivity():Bool
  532. {
  533. return m_bMouseInteractivity;
  534. }
  535. private override function __setEnableInteractivity( p_bState:Bool ):Bool
  536. {
  537. if( p_bState != m_bMouseInteractivity )
  538. {
  539. changed = true;
  540. // --
  541. if( p_bState )
  542. {
  543. if( m_bUseSingleContainer == true )
  544. {
  545. useSingleContainer = false;
  546. m_bForcedSingleContainer = true;
  547. }
  548. }
  549. else
  550. {
  551. if( m_bForcedSingleContainer == true )
  552. {
  553. useSingleContainer = true;
  554. m_bForcedSingleContainer = false;
  555. }
  556. }
  557. // --
  558. for ( l_oPolygon in aPolygons )
  559. {
  560. l_oPolygon.enableInteractivity = p_bState;
  561. }
  562. m_bMouseInteractivity = p_bState;
  563. }
  564. return p_bState;
  565. }
  566. // geometry
  567. private function __getGeometry():Geometry3D
  568. {
  569. return m_oGeometry;
  570. }
  571. private function __setGeometry( p_geometry:Geometry3D ):Geometry3D
  572. {
  573. if( p_geometry == null ) return null;
  574. // TODO shall we clone the geometry?
  575. m_oGeometry = p_geometry;
  576. updateBoundingVolumes();
  577. // -- we generate the possible missing normals
  578. m_oGeometry.generateFaceNormals();//Must be called first
  579. m_oGeometry.generateVertexNormals();//must be called second
  580. // --
  581. #if (js && SANDY_WEBGL)
  582. if (Lib.mOpenGL)
  583. {
  584. // TODO: check if appearance and buffers are set
  585. }
  586. #end
  587. __destroyPolygons();
  588. __generatePolygons( m_oGeometry );
  589. changed = true;
  590. return p_geometry;
  591. }
  592. // geometryCenter
  593. private function __getGeometryCenter():Point3D
  594. {
  595. return m_oGeomCenter;
  596. }
  597. private function __setGeometryCenter( p_oGeomCenter:Point3D ):Point3D
  598. {
  599. var l_oDiff:Point3D = p_oGeomCenter.clone();
  600. l_oDiff.sub( m_oGeomCenter );
  601. // --
  602. if( m_oGeometry != null )
  603. {
  604. for ( l_oVertex in m_oGeometry.aVertex )
  605. {
  606. l_oVertex.x += l_oDiff.x;
  607. l_oVertex.y += l_oDiff.y;
  608. l_oVertex.z += l_oDiff.z;
  609. }
  610. }
  611. // --
  612. m_oGeomCenter.copy( p_oGeomCenter );
  613. // --
  614. updateBoundingVolumes();
  615. changed = true;
  616. return p_oGeomCenter;
  617. }
  618. // material
  619. public function __getMaterial():Material
  620. {
  621. return ( aPolygons[0].visible ) ? m_oAppearance.frontMaterial : m_oAppearance.backMaterial;
  622. }
  623. public function __setMaterial(v):Material
  624. {
  625. return throw "not implemented";
  626. }
  627. // scene (from Node.hx)
  628. private override function __setScene( p_oScene:Scene3D )
  629. {
  630. super.__setScene(p_oScene);
  631. if(aPolygons != null) {
  632. for( l_oPoly in aPolygons )
  633. {
  634. l_oPoly.scene = null;
  635. l_oPoly.scene = p_oScene;
  636. }
  637. }
  638. return p_oScene;
  639. }
  640. // useSingleContainer (from Node.hx)
  641. private override function __getUseSingleContainer ():Bool
  642. {
  643. return m_bUseSingleContainer;
  644. }
  645. private override function __setUseSingleContainer( p_bUseSingleContainer:Bool ):Bool
  646. {
  647. var l_oFace:Polygon;
  648. // No change
  649. if( p_bUseSingleContainer == m_bUseSingleContainer )
  650. return p_bUseSingleContainer;
  651. // update enableEvents that relies on useSingleContainer
  652. var useEvents = enableEvents;
  653. unsubscribeEvents();
  654. // --
  655. if( p_bUseSingleContainer )
  656. {
  657. for ( l_oFace in aPolygons )
  658. {
  659. if( l_oFace.container.parent != null )
  660. {
  661. l_oFace.container.graphics.clear();
  662. l_oFace.container.parent.removeChild( l_oFace.container );
  663. this.broadcaster.removeChild( l_oFace.broadcaster );
  664. }
  665. }
  666. }
  667. else
  668. {
  669. if( m_oContainer.parent != null )
  670. {
  671. m_oContainer.graphics.clear();
  672. m_oContainer.parent.removeChild( m_oContainer );
  673. }
  674. // --
  675. for ( l_oFace in aPolygons )
  676. {
  677. this.broadcaster.addChild( l_oFace.broadcaster );
  678. // we reset the polygon container to the original one, and add it to the world container
  679. l_oFace.container.graphics.clear();
  680. }
  681. }
  682. m_bUseSingleContainer = p_bUseSingleContainer;
  683. // reapply events
  684. if(useEvents)
  685. subscribeEvents();
  686. //--
  687. changed = true;
  688. return p_bUseSingleContainer;
  689. }
  690. /**
  691. * Faces sorting method.
  692. * With <code>useSingleContainer</code> set to <code>false</code> only <code>SORT_AVGZ</code> is possible.
  693. */
  694. public var sortingMode(__getSortingMode,__setSortingMode):Int;
  695. private inline function __getSortingMode ():Int {
  696. return m_bUseSingleContainer ? m_nSortingMode : SORT_AVGZ;
  697. }
  698. private inline function __setSortingMode (mode:Int):Int {
  699. if (m_bUseSingleContainer) {
  700. if (mode == SORT_LAZY_BSP) {
  701. bsp = BSPNode.makeLazyBSP (aPolygons, 0.01 * boundingSphere.radius);
  702. }
  703. m_nSortingMode = mode;
  704. changed = true;
  705. }
  706. return mode;
  707. }
  708. /////////////////////////////////////////////////////////////////////
  709. ///// PRIVATE /////
  710. /////////////////////////////////////////////////////////////////////
  711. private override function copy( src:sandy.core.scenegraph.Node, includeTransforms:Bool=false, includeGeometry:Bool=true ) : Void
  712. {
  713. if(!Std.is(src,Shape3D))
  714. throw "Invalid src";
  715. var o:Shape3D = cast src;
  716. var finalEvents = o.enableEvents;
  717. super.copy( src, includeTransforms );
  718. if(includeGeometry)
  719. geometry = o.geometry.clone();
  720. animated = o.animated;
  721. // aPolygons - set by geometry
  722. // aVisiblePolygons - ignore
  723. enableForcedDepth = o.enableForcedDepth;
  724. enableNearClipping = o.enableNearClipping;
  725. forcedDepth = o.forcedDepth;
  726. //m_oAppearance
  727. appearance = o.m_oAppearance;
  728. //m_bEv = o.m_bEv;
  729. //enableEvents (in Node.hx)
  730. //m_oGeomCenter
  731. geometryCenter = o.m_oGeomCenter.clone();
  732. //m_bBackFaceCulling (in Node.hx)
  733. m_bWasOver = false;
  734. m_oLastEvent = null;
  735. m_oLastContainer = null;
  736. //m_oGeometry - above
  737. //m_bUseSingleContainer (Node.hx)
  738. //m_oContainer - don't set
  739. //m_bMouseInteractivity (enableInteractivity) (in Node.hx)
  740. m_nDepth = o.m_nDepth;
  741. unsubscribeEvents();
  742. if(finalEvents)
  743. subscribeEvents();
  744. }
  745. private function __destroyPolygons():Void
  746. {
  747. if( aPolygons != null && aPolygons.length > 0 )
  748. {
  749. var i:Int = 0, l:Int = aPolygons.length;
  750. while( i<l )
  751. {
  752. if( broadcaster != null ) broadcaster.removeChild( aPolygons[i].broadcaster );
  753. if( aPolygons[i] != null ) aPolygons[i].destroy();
  754. // --
  755. aPolygons[i] = null;
  756. // --
  757. i ++;
  758. }
  759. }
  760. aPolygons.splice(0,aPolygons.length);
  761. }
  762. private function __generatePolygons( p_oGeometry:Geometry3D ):Void
  763. {
  764. var i:Int = 0, j:Int = 0, l:Int = p_oGeometry.aFacesVertexID.length;
  765. aPolygons = new Array();
  766. // --
  767. for( i in 0...l )
  768. {
  769. aPolygons[i] = new Polygon( this, p_oGeometry, p_oGeometry.aFacesVertexID[i], p_oGeometry.aFacesUVCoordsID[i], i, i );
  770. if( m_oAppearance != null ) aPolygons[i].appearance = m_oAppearance;
  771. this.broadcaster.addChild( aPolygons[i].broadcaster );
  772. }
  773. }
  774. private function _onInteraction( p_oEvt:Event ):Void
  775. {
  776. // we need to get the polygon which has been clicked.
  777. var l_oClick:Point = new Point( m_oContainer.mouseX, m_oContainer.mouseY );
  778. var l_oA:Point = new Point(), l_oB:Point = new Point(), l_oC:Point = new Point();
  779. var l_oPoly:Polygon;
  780. var l_aSId:Array<Int> = ArrayUtil.indicesOfSorted(aPolygons, ['m_nDepth'] , ArrayUtil.SORT_NUMERIC);
  781. var l:Int = aPolygons.length, j:Int;
  782. for( j in 0...l )
  783. {
  784. l_oPoly = aPolygons[ l_aSId[ j ] ];
  785. if( !l_oPoly.visible && m_bBackFaceCulling ) continue;
  786. // --
  787. var l_nSize:Int = l_oPoly.vertices.length;
  788. var l_nTriangles:Int = l_nSize - 2;
  789. for( i in 0...l_nTriangles )
  790. {
  791. l_oA.x = l_oPoly.vertices[i].sx; l_oA.y = l_oPoly.vertices[i].sy;
  792. l_oB.x = l_oPoly.vertices[i+1].sx; l_oB.y = l_oPoly.vertices[i+1].sy;
  793. l_oC.x = l_oPoly.vertices[(i+2)%l_nSize].sx; l_oC.y = l_oPoly.vertices[(i+2)%l_nSize].sy;
  794. // --
  795. if( IntersectionMath.isPointInTriangle2D( l_oClick, l_oA, l_oB, l_oC ) )
  796. {
  797. var l_oUV:UVCoord = l_oPoly.getUVFrom2D( l_oClick );
  798. var l_oPt3d:Point3D = l_oPoly.get3DFrom2D( l_oClick );
  799. m_oLastContainer = m_oContainer;
  800. m_oLastEvent = new Shape3DEvent( p_oEvt.type, this, l_oPoly, l_oUV, l_oPt3d, p_oEvt );
  801. m_oEB.dispatchEvent( m_oLastEvent );
  802. // to be able to dispatch mouse out event
  803. if( p_oEvt.type == MouseEvent.MOUSE_OVER )
  804. m_bWasOver = true;
  805. return;
  806. }
  807. }
  808. }
  809. }
  810. private function subscribeEvents()
  811. {
  812. if( m_bUseSingleContainer == false )
  813. {
  814. for ( v in aPolygons )
  815. {
  816. v.enableEvents = true;
  817. }
  818. }
  819. else
  820. {
  821. m_oContainer.addEventListener(MouseEvent.CLICK, _onInteraction,false,0,true);
  822. m_oContainer.addEventListener(MouseEvent.MOUSE_UP, _onInteraction,false,0,true);
  823. m_oContainer.addEventListener(MouseEvent.MOUSE_DOWN, _onInteraction,false,0,true);
  824. m_oContainer.addEventListener(MouseEvent.ROLL_OVER, _onInteraction,false,0,true);
  825. m_oContainer.addEventListener(MouseEvent.ROLL_OUT, _onInteraction,false,0,true);
  826. m_oContainer.addEventListener(MouseEvent.DOUBLE_CLICK, _onInteraction,false,0,true);
  827. m_oContainer.addEventListener(MouseEvent.MOUSE_MOVE, _onInteraction,false,0,true);
  828. m_oContainer.addEventListener(MouseEvent.MOUSE_OVER, _onInteraction,false,0,true);
  829. m_oContainer.addEventListener(MouseEvent.MOUSE_OUT, _onInteraction,false,0,true);
  830. m_oContainer.addEventListener(MouseEvent.MOUSE_WHEEL, _onInteraction,false,0,true);
  831. }
  832. }
  833. private function unsubscribeEvents()
  834. {
  835. for ( v in aPolygons )
  836. {
  837. v.enableEvents = false;
  838. }
  839. m_oContainer.removeEventListener(MouseEvent.CLICK, _onInteraction);
  840. m_oContainer.removeEventListener(MouseEvent.MOUSE_UP, _onInteraction);
  841. m_oContainer.removeEventListener(MouseEvent.MOUSE_DOWN, _onInteraction);
  842. m_oContainer.removeEventListener(MouseEvent.ROLL_OVER, _onInteraction);
  843. m_oContainer.removeEventListener(MouseEvent.ROLL_OUT, _onInteraction);
  844. m_oContainer.removeEventListener(MouseEvent.DOUBLE_CLICK, _onInteraction);
  845. m_oContainer.removeEventListener(MouseEvent.MOUSE_MOVE, _onInteraction);
  846. m_oContainer.removeEventListener(MouseEvent.MOUSE_OVER, _onInteraction);
  847. m_oContainer.removeEventListener(MouseEvent.MOUSE_OUT, _onInteraction);
  848. m_oContainer.removeEventListener(MouseEvent.MOUSE_WHEEL, _onInteraction);
  849. }
  850. /**
  851. * Updates polygons, face and vertex normals when geometry vertex values have changed.
  852. * Do not call if the geometry has been modified in any way other than
  853. * when the x,y,z positions of some vertices have changed. If the provided
  854. * geometry is not the same as the existing geometry, this will have the
  855. * same effect as assigning a new geometry.
  856. *
  857. * @param p_oGeometry Geometry object which must be the same size as existing geometry
  858. */
  859. private function updateForGeometryChange( p_oGeometry:Geometry3D, updateNormals:Bool=true, updateBounds:Bool=true ) : Void
  860. {
  861. if(m_oGeometry == null || m_oGeometry.aFacesVertexID.length != p_oGeometry.aFacesVertexID.length) {
  862. __setGeometry( p_oGeometry );
  863. return;
  864. }
  865. m_oGeometry = p_oGeometry;
  866. if(updateBounds)
  867. updateBoundingVolumes();
  868. if(updateNormals)
  869. m_oGeometry.updateFaceNormals(); // Must be called first
  870. //m_oGeometry.updateVertexNormals(); // Vertex normals already tied to face normals
  871. var l:Int = m_oGeometry.aFacesVertexID.length;
  872. // --
  873. for( i in 0...l )
  874. {
  875. aPolygons[i].update( m_oGeometry.aFacesVertexID[i] );
  876. }
  877. changed = true;
  878. }
  879. // ______________
  880. // [PRIVATE] DATA________________________________________________
  881. private var m_oAppearance:Appearance ; // The Appearance of this Shape3D
  882. private var m_bEv:Bool; // The event system state (enable or not)
  883. private var m_oGeomCenter:Point3D;
  884. private var m_bBackFaceCulling:Bool;
  885. private var m_bClipping:Bool;
  886. // interaction
  887. public var m_bWasOver:Bool;
  888. public var m_oLastEvent:Shape3DEvent;
  889. public var m_oLastContainer:Sprite;
  890. /** Geometry of this object */
  891. private var m_oGeometry:Geometry3D;
  892. private var m_bUseSingleContainer:Bool;
  893. public var m_nDepth:Float;
  894. private var m_oContainer:Sprite;
  895. private var m_bMouseInteractivity:Bool;
  896. private var m_bForcedSingleContainer:Bool;
  897. private var m_nSortingMode:Int;
  898. }