PageRenderTime 2873ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/sandy/as3/branches/3.0.2/src/sandy/core/scenegraph/Shape3D.as

http://sandy.googlecode.com/
ActionScript | 858 lines | 485 code | 60 blank | 313 comment | 102 complexity | b2397a96c291c8aacc0b92cbc4b8b0d8 MD5 | raw file
  1. /*
  2. # ***** BEGIN LICENSE BLOCK *****
  3. Copyright the original author or authors.
  4. Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.mozilla.org/MPL/MPL-1.1.html
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. # ***** END LICENSE BLOCK *****
  14. */
  15. package sandy.core.scenegraph
  16. {
  17. import flash.display.Sprite;
  18. import flash.events.Event;
  19. import flash.events.MouseEvent;
  20. import flash.geom.Point;
  21. import flash.utils.Dictionary;
  22. import sandy.bounds.BBox;
  23. import sandy.bounds.BSphere;
  24. import sandy.core.SandyFlags;
  25. import sandy.core.Scene3D;
  26. import sandy.core.data.Matrix4;
  27. import sandy.core.data.Polygon;
  28. import sandy.core.data.Vector;
  29. import sandy.core.data.Vertex;
  30. import sandy.events.BubbleEvent;
  31. import sandy.materials.Appearance;
  32. import sandy.materials.Material;
  33. import sandy.materials.WireFrameMaterial;
  34. import sandy.math.IntersectionMath;
  35. import sandy.view.CullingState;
  36. import sandy.view.Frustum;
  37. import sandy.core.data.UVCoord;
  38. import sandy.events.Shape3DEvent;
  39. /**
  40. * The Shape3D class is the base class of all true 3D shapes.
  41. *
  42. * <p>It represents a node in the object tree of the world.<br/>
  43. * A Shape3D is a leaf node and can not have any child nodes.</p>
  44. * <p>It must be the child of a branch group or a transform group,
  45. * but transformations can be applied to the Shape directly.</p>
  46. *
  47. * @author Thomas Pfeiffer - kiroukou
  48. * @version 3.0
  49. * @date 26.07.2007
  50. */
  51. public class Shape3D extends ATransformable implements IDisplayable
  52. {
  53. /**
  54. * Default material for the DEFAULT_APPEARANCE object
  55. */
  56. public static var DEFAULT_MATERIAL:Material = new WireFrameMaterial();
  57. /**
  58. * Default appearance for Shape3D instances. If no apperance is given, this default one will be applied using the DEFAULT_MATERIAL as front and back material
  59. */
  60. public static var DEFAULT_APPEARANCE:Appearance = new Appearance( DEFAULT_MATERIAL );
  61. /**
  62. * The array of polygons building this object.
  63. */
  64. public var aPolygons:Array = new Array();
  65. /**
  66. * <p>
  67. * Enable the Frustum near plane clipping on the visible polygons.
  68. * Enable this when you need a perfect intersection between the front camera plane.
  69. * This is mainly used when you need the camera to move on a long plane.</p>
  70. *
  71. * <p>Important: Enable the clipping makes process a bit slower, especially with big scenes.</p>
  72. */
  73. public var enableNearClipping:Boolean = false;
  74. /**
  75. * <p>
  76. * Enable the Frustum clipping on the visible polygons.
  77. * Enable this when you need a perfect intersection between the camera and some object shapes.
  78. * In case you need to make the camera look inside and outide a box, or other immerssive things.</p>
  79. *
  80. * <p>Important: Enable the clipping makes process a bit slower, especially with big scenes.</p>
  81. *
  82. * <p>Specify if this object polygons should be clipped against the camera frustum planes.</p>
  83. */
  84. public var enableClipping:Boolean = false;
  85. /**
  86. * Should forced depth be enable for this object?.
  87. *
  88. * <p>If true it is possible to force this object to be drawn at a specific depth,<br/>
  89. * if false the normal Z-sorting algorithm is applied.</p>
  90. * <p>When correctly used, this feature allows you to avoid some Z-sorting problems.</p>
  91. */
  92. public var enableForcedDepth:Boolean = false;
  93. /**
  94. * The forced depth for this object.
  95. *
  96. * <p>To make this feature work, you must enable the ForcedDepth system too.<br/>
  97. * The higher the depth is, the sooner the more far the object will be represented.</p>
  98. */
  99. public var forcedDepth:Number = 0;
  100. /**
  101. * Creates a 3D object
  102. *
  103. * <p>[<b>Todo</b>: some more explanations]</p>
  104. *
  105. * @param p_sName A string identifier for this object
  106. * @param p_oGeometry The geometry of this object
  107. * @param p_oAppearance The appearance of this object. If no apperance is given, the DEFAULT_APPEARANCE will be applied.
  108. * @param p_bUseSingleContainer Whether tis object should use a single container to draw on
  109. */
  110. public function Shape3D( p_sName:String = "", p_oGeometry:Geometry3D = null, p_oAppearance:Appearance = null, p_bUseSingleContainer:Boolean=true )
  111. {
  112. super( p_sName );
  113. // -- Add this graphical object to the World display list
  114. m_oContainer = new Sprite();
  115. // --
  116. geometry = p_oGeometry;
  117. // -- HACK to make sure that the correct container system will be applied
  118. m_bUseSingleContainer = !p_bUseSingleContainer;
  119. useSingleContainer = p_bUseSingleContainer;
  120. // --
  121. appearance = ( p_oAppearance ) ? p_oAppearance : Shape3D.DEFAULT_APPEARANCE;
  122. // --
  123. updateBoundingVolumes();
  124. }
  125. /**
  126. * setter that allow user to change the way to render this object.
  127. * set to true, the shape will be rendered into a single Sprite object, which is accessible through the container property.
  128. * set to false, the container property does not target anything, but all the polygons will be rendered into their own dedidated container.
  129. *
  130. * <p>If true, this object renders itself on a single container ( Sprite ),<br/>
  131. * if false, each polygon is rendered on its own container.</p>
  132. */
  133. override public function set useSingleContainer( p_bUseSingleContainer:Boolean ):void
  134. {
  135. var l_oFace:Polygon;
  136. // --
  137. if( p_bUseSingleContainer == m_bUseSingleContainer ) return;
  138. // --
  139. if( p_bUseSingleContainer )
  140. {
  141. for each( l_oFace in aPolygons )
  142. {
  143. if( l_oFace.container.parent )
  144. {
  145. l_oFace.container.graphics.clear();
  146. l_oFace.container.parent.removeChild( l_oFace.container );
  147. this.broadcaster.removeChild( l_oFace.broadcaster );
  148. }
  149. }
  150. }
  151. else
  152. {
  153. if( m_oContainer.parent )
  154. {
  155. m_oContainer.graphics.clear();
  156. m_oContainer.parent.removeChild( m_oContainer );
  157. }
  158. // --
  159. for each( l_oFace in aPolygons )
  160. {
  161. this.broadcaster.addChild( l_oFace.broadcaster );
  162. // we reset the polygon container to the original one, and add it to the world container
  163. l_oFace.container.graphics.clear();
  164. }
  165. }
  166. m_bUseSingleContainer = p_bUseSingleContainer;
  167. }
  168. /**
  169. * @private
  170. */
  171. public function get useSingleContainer ():Boolean
  172. {return m_bUseSingleContainer;}
  173. /**
  174. * Updates the bounding volumes of this object.
  175. */
  176. public function updateBoundingVolumes():void
  177. {
  178. if( m_oGeometry )
  179. {
  180. boundingSphere = BSphere.create( m_oGeometry.aVertex );
  181. boundingBox = BBox.create( m_oGeometry.aVertex );
  182. }
  183. }
  184. /**
  185. * Tests this node against the camera frustum to get its visibility.
  186. *
  187. * <p>If this node and its children are not within the frustum,
  188. * the node is set to cull and it would not be displayed.<p/>
  189. * <p>The method also updates the bounding volumes to make the more accurate culling system possible.<br/>
  190. * First the bounding sphere is updated, and if intersecting,
  191. * the bounding box is updated to perform the more precise culling.</p>
  192. * <p><b>[MANDATORY] The update method must be called first!</b></p>
  193. *
  194. * @param p_oScene The current scene
  195. * @param p_oFrustum The frustum of the current camera
  196. * @param p_oViewMatrix The view martix of the curren camera
  197. * @param p_bChanged
  198. */
  199. public override function cull( p_oScene:Scene3D, p_oFrustum:Frustum, p_oViewMatrix:Matrix4, p_bChanged:Boolean ):void
  200. {
  201. super.cull( p_oScene, p_oFrustum, p_oViewMatrix, p_bChanged );
  202. if( culled == Frustum.OUTSIDE ) return;
  203. /////////////////////////
  204. //// BOUNDING SPHERE ////
  205. /////////////////////////
  206. if( !boundingSphere.uptodate ) boundingSphere.transform( viewMatrix );
  207. culled = p_oFrustum.sphereInFrustum( boundingSphere );
  208. // --
  209. if( culled == Frustum.INTERSECT && boundingBox )
  210. {
  211. ////////////////////////
  212. //// BOUNDING BOX ////
  213. ////////////////////////
  214. if( !boundingBox.uptodate ) boundingBox.transform( viewMatrix );
  215. culled = p_oFrustum.boxInFrustum( boundingBox );
  216. }
  217. m_bClipped = ((culled == CullingState.INTERSECT) && ( enableClipping || enableNearClipping ));
  218. }
  219. /**
  220. * Renders this 3D object.
  221. *
  222. * @param p_oScene The current scene
  223. * @param p_oCamera The current camera
  224. */
  225. public override function render( p_oScene:Scene3D, p_oCamera:Camera3D ):void
  226. {
  227. // IF no appearance has bene applied, no display
  228. if( m_oAppearance == null ) return;
  229. var m11:Number, m21:Number, m31:Number,
  230. m12:Number, m22:Number, m32:Number,
  231. m13:Number, m23:Number, m33:Number,
  232. m14:Number, m24:Number, m34:Number,
  233. x:Number, y:Number, z:Number, tx:Number, ty:Number, tz:Number;
  234. var l_nZNear:Number = p_oCamera.near, l_aPoints:Array = m_oGeometry.aVertex,
  235. l_oMatrix:Matrix4 = viewMatrix, l_oFrustum:Frustum = p_oCamera.frustrum,
  236. l_aVertexNormals:Array = m_oGeometry.aVertexNormals,
  237. l_oVertexNormal:Vertex, l_oVertex:Vertex, l_oFace:Polygon, l_nMinZ:Number;
  238. // -- Now we can transform the objet vertices into the camera coordinates
  239. l_oMatrix = viewMatrix;
  240. m11 = l_oMatrix.n11; m21 = l_oMatrix.n21; m31 = l_oMatrix.n31;
  241. m12 = l_oMatrix.n12; m22 = l_oMatrix.n22; m32 = l_oMatrix.n32;
  242. m13 = l_oMatrix.n13; m23 = l_oMatrix.n23; m33 = l_oMatrix.n33;
  243. m14 = l_oMatrix.n14; m24 = l_oMatrix.n24; m34 = l_oMatrix.n34;
  244. for each( l_oVertex in l_aPoints )
  245. {
  246. l_oVertex.wx = (x=l_oVertex.x) * m11 + (y=l_oVertex.y) * m12 + (z=l_oVertex.z) * m13 + m14;
  247. l_oVertex.wy = x * m21 + y * m22 + z * m23 + m24;
  248. l_oVertex.wz = x * m31 + y * m32 + z * m33 + m34;
  249. l_oVertex.projected = false;
  250. }
  251. // -- The polygons will be clipped, we shall allocate a new array container the clipped vertex.
  252. m_aVisiblePoly = [];
  253. m_nVisiblePoly = 0;
  254. m_nDepth = 0;
  255. for each( l_oFace in aPolygons )
  256. {
  257. l_oFace.isClipped = false;
  258. // --
  259. x = l_oFace.normal.x ; y = l_oFace.normal.y ; z = l_oFace.normal.z ;
  260. // --
  261. tx = x * m11+ y * m12+ z * m13;
  262. ty = x * m21+ y * m22+ z * m23;
  263. tz = x * m31+ y * m32+ z * m33;
  264. // -- visibility computation
  265. x = l_oFace.a.wx*tx + l_oFace.a.wy*ty + l_oFace.a.wz*tz;
  266. l_oFace.visible = x < 0;
  267. // --
  268. if( l_oFace.visible || !m_bBackFaceCulling)
  269. {
  270. l_oFace.precompute();
  271. l_nMinZ = l_oFace.minZ;
  272. // --
  273. if( m_bClipped && enableClipping ) // NEED COMPLETE CLIPPING
  274. {
  275. l_oFace.clip( l_oFrustum );
  276. // -- We project the vertices
  277. if( l_oFace.cvertices.length > 2 )
  278. {
  279. p_oCamera.projectArray( l_oFace.cvertices );
  280. if( !enableForcedDepth ) m_nDepth += l_oFace.m_nDepth;
  281. else l_oFace.depth = forcedDepth;
  282. // -- we manage the display list depending on the mode choosen
  283. m_aVisiblePoly[int(m_nVisiblePoly++)] = l_oFace;
  284. }
  285. }
  286. else if( enableNearClipping && l_nMinZ < l_nZNear ) // PARTIALLY VISIBLE
  287. {
  288. l_oFace.clipFrontPlane( l_oFrustum );
  289. // -- We project the vertices
  290. if( l_oFace.cvertices.length > 2 )
  291. {
  292. p_oCamera.projectArray( l_oFace.cvertices );
  293. if( !enableForcedDepth ) m_nDepth += l_oFace.m_nDepth;
  294. else l_oFace.depth = forcedDepth;
  295. // -- we manage the display list depending on the mode choosen
  296. m_aVisiblePoly[int(m_nVisiblePoly++)] = l_oFace;
  297. }
  298. }
  299. else if( l_nMinZ >= l_nZNear )
  300. {
  301. p_oCamera.projectArray( l_oFace.vertices );
  302. if( !enableForcedDepth ) m_nDepth += l_oFace.m_nDepth;
  303. else l_oFace.depth = forcedDepth;
  304. // -- we manage the display list depending on the mode choosen
  305. m_aVisiblePoly[int(m_nVisiblePoly++)] = l_oFace;
  306. }
  307. else
  308. continue;
  309. if( l_oFace.hasAppearanceChanged )
  310. {
  311. if( p_oScene.materialManager.isRegistered( l_oFace.appearance.frontMaterial ) == false )
  312. {
  313. p_oScene.materialManager.register( l_oFace.appearance.frontMaterial );
  314. }
  315. if( l_oFace.appearance.frontMaterial != l_oFace.appearance.backMaterial )
  316. {
  317. if( p_oScene.materialManager.isRegistered( l_oFace.appearance.backMaterial ) == false )
  318. {
  319. p_oScene.materialManager.register( l_oFace.appearance.backMaterial );
  320. }
  321. }
  322. l_oFace.hasAppearanceChanged = false;
  323. }
  324. }
  325. }
  326. // --
  327. if( m_bUseSingleContainer )
  328. {
  329. if(enableForcedDepth) m_nDepth = forcedDepth;
  330. else m_nDepth /= m_aVisiblePoly.length;
  331. p_oCamera.addToDisplayList( this );
  332. }
  333. else
  334. {
  335. p_oCamera.addArrayToDisplayList( m_aVisiblePoly );
  336. }
  337. var l_nFlags:int = appearance.flags;
  338. if( l_nFlags == 0 ) return;
  339. var i:int;
  340. l_oMatrix = modelMatrix;
  341. m11 = l_oMatrix.n11; m21 = l_oMatrix.n21; m31 = l_oMatrix.n31;
  342. m12 = l_oMatrix.n12; m22 = l_oMatrix.n22; m32 = l_oMatrix.n32;
  343. m13 = l_oMatrix.n13; m23 = l_oMatrix.n23; m33 = l_oMatrix.n33;
  344. if( appearance.flags & SandyFlags.POLYGON_NORMAL_WORLD )
  345. {
  346. // -- Now we transform the normals.
  347. for each( var l_oPoly:Polygon in m_aVisiblePoly )
  348. {
  349. l_oVertex = l_oPoly.normal;
  350. l_oVertex.wx = (x=l_oVertex.x) * m11 + (y=l_oVertex.y) * m12 + (z=l_oVertex.z) * m13;
  351. l_oVertex.wy = x * m21 + y * m22 + z * m23;
  352. l_oVertex.wz = x * m31 + y * m32 + z * m33;
  353. }
  354. }
  355. if( appearance.flags & SandyFlags.VERTEX_NORMAL_WORLD )
  356. {
  357. // -- Now we transform the normals.
  358. i = m_oGeometry.aVertexNormals.length;
  359. while( --i > -1 )
  360. {
  361. if( m_oGeometry.aVertex[int(i)].projected )
  362. {
  363. l_oVertex = m_oGeometry.aVertexNormals[int(i)];
  364. l_oVertex.wx = (x=l_oVertex.x) * m11 + (y=l_oVertex.y) * m12 + (z=l_oVertex.z) * m13;
  365. l_oVertex.wy = x * m21 + y * m22 + z * m23;
  366. l_oVertex.wz = x * m31 + y * m32 + z * m33;
  367. }
  368. }
  369. }
  370. }
  371. public function get visiblePolygonsCount():uint
  372. {
  373. return m_nVisiblePoly;
  374. }
  375. /**
  376. * Clears the graphics object of this object's container.
  377. *
  378. * <p>The the graphics that were drawn on the Graphics object is erased,
  379. * and the fill and line style settings are reset.</p>
  380. */
  381. public function clear():void
  382. {
  383. if( m_oContainer ) m_oContainer.graphics.clear();
  384. }
  385. /**
  386. * Performs a z-sorting and renders the objects visible polygons.
  387. *
  388. * <p>The method is called only if the object renders on a single container<br/>
  389. * - ( useSingleContainer = true ).</p>
  390. *
  391. * @param p_oScene The current scene
  392. * @param p_oContainer The container to draw on
  393. */
  394. public function display( p_oScene:Scene3D, p_oContainer:Sprite = null ):void
  395. {
  396. m_aVisiblePoly.sortOn( "depth", Array.NUMERIC | Array.DESCENDING );
  397. // --
  398. for each( var l_oPoly:Polygon in m_aVisiblePoly )
  399. {
  400. l_oPoly.display( p_oScene, m_oContainer );
  401. }
  402. }
  403. /**
  404. * The contianer for this object.
  405. * This container property exist if the useSingleContainer is set to true.
  406. * It is a direct access to the Shape3D container to, for example, apply nice effects such as filters etc.
  407. */
  408. public function get container():Sprite
  409. {return m_oContainer;}
  410. /**
  411. * The depth of this object.
  412. * In case the useSingleContainer mode is enabled (default mode), this value returns the means depth of the Shape in the camera frame.
  413. * This value is mainly used as a z-sorting value.
  414. */
  415. public function get depth():Number
  416. {return m_nDepth;}
  417. /**
  418. * This property call allows you to get the geometryCenter offset vector of the Shape.
  419. * Modifying this vector will impact the way the shape is rendered, mainly its rotation center.
  420. *
  421. * @return a vector which corresponds to the 2 directions offset.
  422. */
  423. public function get geometryCenter():Vector
  424. {return m_oGeomCenter;}
  425. /**
  426. * Change the geometryCenter of the Shape3D.
  427. * To change the geometryCenter point of a shape, simply set this geometryCenter property.
  428. * The geometryCenter property requires a vector. This vector is an position offset relative to the original geometry one.
  429. * For example, a Sphere primitive creates automatically a geometry which center is the 0,0,0 position. If you rotate this sphere as this,
  430. * it will rotate around its center.
  431. * Now if you set the geometryCenter property, this rotation center will change.
  432. *
  433. * The updateBoundingVolumes method which does update the bounding volumes to enable a correct frustum culling is automatically called.
  434. *
  435. * @example To change the geometryCenter center at runtime
  436. * <listing version="3.0">
  437. * var l_oSphere:Sphere = new Sphere("mySphere", 50, 3 );
  438. * // Change the rotation reference to -50 offset in Y direction from the orinal one
  439. * // and that corresponds to the bottom of the sphere
  440. * l_oSphere.geometryCenter = new Vector( 0, -50, 0 );
  441. * l_oSphere.rotateZ = 45;
  442. * </listing>
  443. */
  444. public function set geometryCenter( p_oGeomCenter:Vector ):void
  445. {
  446. var l_oDiff:Vector = p_oGeomCenter.clone();
  447. l_oDiff.sub( m_oGeomCenter );
  448. // --
  449. if( m_oGeometry )
  450. {
  451. for each( var l_oVertex:Vertex in m_oGeometry.aVertex )
  452. {
  453. l_oVertex.x += l_oDiff.x;
  454. l_oVertex.y += l_oDiff.y;
  455. l_oVertex.z += l_oDiff.z;
  456. }
  457. }
  458. // --
  459. m_oGeomCenter.copy( p_oGeomCenter );
  460. // --
  461. updateBoundingVolumes();
  462. }
  463. /**
  464. * The appearance of this object.
  465. */
  466. override public function set appearance( p_oApp:Appearance ):void
  467. {
  468. // Now we register to the update event
  469. m_oAppearance = p_oApp;
  470. // --
  471. if( m_oGeometry )
  472. {
  473. for each( var v:Polygon in aPolygons )
  474. v.appearance = m_oAppearance;
  475. }
  476. }
  477. /**
  478. * @private
  479. */
  480. public function get appearance():Appearance
  481. {
  482. return m_oAppearance;
  483. }
  484. /**
  485. * The geometry of this object.
  486. */
  487. public function set geometry( p_geometry:Geometry3D ):void
  488. {
  489. if( p_geometry == null ) return;
  490. // TODO shall we clone the geometry?
  491. m_oGeometry = p_geometry;
  492. updateBoundingVolumes();
  493. // -- we generate the possible missing normals
  494. m_oGeometry.generateFaceNormals();//Must be called first
  495. m_oGeometry.generateVertexNormals();//must be called second
  496. // --
  497. __destroyPolygons();
  498. __generatePolygons( m_oGeometry );
  499. }
  500. /**
  501. * @private
  502. */
  503. public function get geometry():Geometry3D
  504. {
  505. return m_oGeometry;
  506. }
  507. /**
  508. * Should back face culling be enabled for this object?.
  509. *
  510. * <p>If set to false all faces of this object are drawn.<br/>
  511. * A true value enables the back face culling algorithm - Default true</p>
  512. */
  513. override public function set enableBackFaceCulling( b:Boolean ):void
  514. {
  515. if( b != m_bBackFaceCulling )
  516. {
  517. m_bBackFaceCulling = b;
  518. changed = true;
  519. }
  520. }
  521. /**
  522. * @private
  523. */
  524. public function get enableBackFaceCulling():Boolean
  525. {
  526. return m_bBackFaceCulling;
  527. }
  528. /**
  529. * Enable the interactivity on this shape and its polygon.
  530. * Be careful, this mode have some requirements :
  531. * - to have useSingleContainer set to false. It is done automatically if enabled
  532. *
  533. * The original settings are back to their original state when the mode is disabled
  534. */
  535. override public function set enableInteractivity( p_bState:Boolean ):void
  536. {
  537. if( p_bState != m_bMouseInteractivity )
  538. {
  539. if( p_bState )
  540. {
  541. if( m_bUseSingleContainer == true )
  542. {
  543. m_bUseSingleContainer = false;
  544. m_bForcedSingleContainer = true;
  545. }
  546. }
  547. else
  548. {
  549. if( m_bForcedSingleContainer == true )
  550. {
  551. useSingleContainer = true;
  552. m_bForcedSingleContainer = false;
  553. }
  554. }
  555. // --
  556. for each( var l_oPolygon:Polygon in aPolygons )
  557. {
  558. l_oPolygon.enableInteractivity = p_bState;
  559. }
  560. m_bMouseInteractivity = p_bState;
  561. }
  562. }
  563. public function get enableInteractivity():Boolean
  564. { return m_bMouseInteractivity; }
  565. /**
  566. * Enables the event system for mouse events.
  567. *
  568. * <p>When set to true, the onPress, onRollOver and onRollOut events are broadcast.<br/>
  569. * The event system is enabled or disabled for all faces of this object.<br/>
  570. * As an alternative, you have the possibility to enable events only for specific faces.</p>
  571. *
  572. * <p>Once this feature is enabled, the animation is more CPU intensive.</p>
  573. *
  574. * <p>Example
  575. * <code>
  576. * var l_oShape:Shape3D = new Sphere("sphere");
  577. * l_oShape.enableEvents = true;
  578. * l_oShape.addEventListener( MouseEvent.CLICK, onClick );
  579. *
  580. * function onClick( p_eEvent:Shape3DEvent ):void
  581. * {
  582. * var l_oPoly:Polygon = ( p_eEvent.polygon );
  583. * var l_oPointAtClick:Vector = p_eEvent.point;
  584. * // -- get the normalized uv of the point under mouse position
  585. * var l_oIntersectionUV:UVCoord = p_eEvent.uv;
  586. * // -- get the correct material
  587. * var l_oMaterial:BitmapMaterial = (l_oPoly.visible ? l_oPoly.appearance.frontMaterial : l_oPoly.appearance.backMaterial) as BitmapMaterial;
  588. * }
  589. * </code>
  590. */
  591. override public function set enableEvents( b:Boolean ):void
  592. {
  593. // To use only when use Single container is disabled
  594. var v:Polygon = null;
  595. if( b )
  596. {
  597. if( !m_bEv )
  598. {
  599. if( m_bUseSingleContainer == false )
  600. {
  601. for each( v in aPolygons )
  602. {
  603. v.enableEvents = true;
  604. }
  605. }
  606. else
  607. {
  608. m_oContainer.addEventListener(MouseEvent.CLICK, _onInteraction);
  609. m_oContainer.addEventListener(MouseEvent.MOUSE_UP, _onInteraction);
  610. m_oContainer.addEventListener(MouseEvent.MOUSE_DOWN, _onInteraction);
  611. m_oContainer.addEventListener(MouseEvent.ROLL_OVER, _onInteraction);
  612. m_oContainer.addEventListener(MouseEvent.ROLL_OUT, _onInteraction);
  613. m_oContainer.addEventListener(MouseEvent.DOUBLE_CLICK, _onInteraction);
  614. m_oContainer.addEventListener(MouseEvent.MOUSE_MOVE, _onInteraction);
  615. m_oContainer.addEventListener(MouseEvent.MOUSE_OVER, _onInteraction);
  616. m_oContainer.addEventListener(MouseEvent.MOUSE_OUT, _onInteraction);
  617. m_oContainer.addEventListener(MouseEvent.MOUSE_WHEEL, _onInteraction);
  618. }
  619. }
  620. }
  621. else if( !b && m_bEv )
  622. {
  623. if( m_bUseSingleContainer == false )
  624. {
  625. for each( v in aPolygons )
  626. {
  627. v.enableEvents = false;
  628. }
  629. }
  630. else
  631. {
  632. m_oContainer.removeEventListener(MouseEvent.CLICK, _onInteraction);
  633. m_oContainer.removeEventListener(MouseEvent.MOUSE_UP, _onInteraction);
  634. m_oContainer.removeEventListener(MouseEvent.MOUSE_DOWN, _onInteraction);
  635. m_oContainer.removeEventListener(MouseEvent.ROLL_OVER, _onInteraction);
  636. m_oContainer.removeEventListener(MouseEvent.ROLL_OUT, _onInteraction);
  637. m_oContainer.removeEventListener(MouseEvent.DOUBLE_CLICK, _onInteraction);
  638. m_oContainer.removeEventListener(MouseEvent.MOUSE_MOVE, _onInteraction);
  639. m_oContainer.removeEventListener(MouseEvent.MOUSE_OVER, _onInteraction);
  640. m_oContainer.removeEventListener(MouseEvent.MOUSE_OUT, _onInteraction);
  641. m_oContainer.removeEventListener(MouseEvent.MOUSE_WHEEL, _onInteraction);
  642. }
  643. }
  644. m_bEv = b;
  645. }
  646. protected function _onInteraction( p_oEvt:Event ):void
  647. {
  648. // we need to get the polygon which has been clicked.
  649. var l_oClick:Point = new Point( m_oContainer.mouseX, m_oContainer.mouseY );
  650. var l_oA:Point = new Point(), l_oB:Point = new Point(), l_oC:Point = new Point();
  651. var l_oPoly:Polygon;
  652. var l_aSId:Array = aPolygons.sortOn( 'depth', Array.NUMERIC | Array.RETURNINDEXEDARRAY );
  653. var l:int = aPolygons.length, j:int;
  654. for( j = 0; j < l; j += 1 )
  655. //j = l;
  656. //while( --j > -1 )
  657. {
  658. l_oPoly = aPolygons[ l_aSId[ int(j) ] ];
  659. if( !l_oPoly.visible && m_bBackFaceCulling ) continue;
  660. // --
  661. var l_nSize:int = l_oPoly.vertices.length;
  662. var l_nTriangles:int = l_nSize - 2;
  663. for( var i:int = 0; i < l_nTriangles; i++ )
  664. {
  665. l_oA.x = l_oPoly.vertices[i].sx; l_oA.y = l_oPoly.vertices[i].sy;
  666. l_oB.x = l_oPoly.vertices[i+1].sx; l_oB.y = l_oPoly.vertices[i+1].sy;
  667. l_oC.x = l_oPoly.vertices[(i+2)%l_nSize].sx; l_oC.y = l_oPoly.vertices[(i+2)%l_nSize].sy;
  668. // --
  669. if( IntersectionMath.isPointInTriangle2D( l_oClick, l_oA, l_oB, l_oC ) )
  670. {
  671. var l_oUV:UVCoord = l_oPoly.getUVFrom2D( l_oClick );
  672. var l_oPt3d:Vector = l_oPoly.get3DFrom2D( l_oClick );
  673. m_oEB.broadcastEvent( new Shape3DEvent( p_oEvt.type, this, l_oPoly, l_oUV, l_oPt3d, p_oEvt ) );
  674. return;
  675. }
  676. }
  677. }
  678. //m_oEB.broadcastEvent( new BubbleEvent( p_oEvt.type, this, p_oEvt ) );
  679. }
  680. /**
  681. * Changes the backface culling side.
  682. *
  683. * <p>When you want to display a cube and you are the cube, you see its external faces.<br/>
  684. * The internal faces are not drawn due to back face culling</p>
  685. *
  686. * <p>In case you are inside the cube, by default Sandy's engine still doesn't draw the internal faces
  687. * (because you should not be in there).</p>
  688. *
  689. * <p>If you need to be only inside the cube, you can call this method to change which side is culled.<br/>
  690. * The faces will be visible only from the interior of the cube.</p>
  691. *
  692. * <p>If you want to be both on the inside and the outside, you want to make the faces visible from on both sides.<br/>
  693. * In that case you just have to set enableBackFaceCulling to false.</p>
  694. */
  695. public function swapCulling():void
  696. {
  697. for each( var v:Polygon in aPolygons )
  698. {
  699. v.swapCulling();
  700. }
  701. changed = true;
  702. }
  703. /**
  704. * Destroy this object and all its faces
  705. * container object is removed, and graphics cleared. All polygons have their
  706. */
  707. public override function destroy():void
  708. {
  709. // FIXME Fix it - it should be more like
  710. m_oGeometry.dispose();
  711. // --
  712. clear();
  713. if( m_oContainer.parent ) m_oContainer.parent.removeChild( m_oContainer );
  714. if( m_oContainer ) m_oContainer = null;
  715. // --
  716. __destroyPolygons();
  717. // --
  718. super.destroy();
  719. }
  720. /**
  721. * This method returns a clone of this Shape3D.
  722. * The current appearance will be applied, and the geometry is cloned (not referenced to curent one).
  723. *
  724. * @param p_sName The name of the new shape you are going to create
  725. * @param p_bKeepTransform Boolean value which, if set to true, applies the current local transformations to the cloned shape. Default value is false.
  726. *
  727. * @return The clone
  728. */
  729. public function clone( p_sName:String="", p_bKeepTransform:Boolean = false ):Shape3D
  730. {
  731. var l_oClone:Shape3D = new Shape3D( p_sName, geometry.clone(), appearance, m_bUseSingleContainer );
  732. // --
  733. if( p_bKeepTransform == true ) l_oClone.matrix = this.matrix;
  734. // --
  735. return l_oClone;
  736. }
  737. /**
  738. * Returns a string representation of this object
  739. *
  740. * @return The fully qualified name of this object and its geometry
  741. */
  742. public override function toString ():String
  743. {
  744. return "sandy.core.scenegraph.Shape3D" + " " + m_oGeometry.toString();
  745. }
  746. ///////////////////
  747. ///// PRIVATE /////
  748. ///////////////////
  749. private function __destroyPolygons():void
  750. {
  751. if( aPolygons != null && aPolygons.length > 0 )
  752. {
  753. var i:int, l:int = aPolygons.length;
  754. while( i<l )
  755. {
  756. if( broadcaster != null ) broadcaster.removeChild( aPolygons[i].broadcaster );
  757. if( aPolygons[i] ) Polygon( aPolygons[int(i)] ).destroy();
  758. // --
  759. aPolygons[int(i)] = null;
  760. // --
  761. i ++;
  762. }
  763. }
  764. aPolygons.splice(0);
  765. aPolygons = null;
  766. }
  767. private function __generatePolygons( p_oGeometry:Geometry3D ):void
  768. {
  769. var i:int = 0, j:int = 0, l:int = p_oGeometry.aFacesVertexID.length;
  770. aPolygons = new Array( l );
  771. // --
  772. for( i=0; i < l; i += 1 )
  773. {
  774. aPolygons[i] = new Polygon( this, p_oGeometry, p_oGeometry.aFacesVertexID[i], p_oGeometry.aFacesUVCoordsID[i], i, i );
  775. if( m_oAppearance ) aPolygons[int(i)].appearance = m_oAppearance;
  776. this.broadcaster.addChild( aPolygons[int(i)].broadcaster );
  777. }
  778. }
  779. // ______________
  780. // [PRIVATE] DATA________________________________________________
  781. private var m_oAppearance:Appearance ; // The Appearance of this Shape3D
  782. private var m_bEv:Boolean = false; // The event system state (enable or not)
  783. protected var m_oGeomCenter:Vector = new Vector();
  784. private var m_bBackFaceCulling:Boolean = true;
  785. private var m_bClipped:Boolean = false;
  786. /** Geometry of this object */
  787. private var m_oGeometry:Geometry3D;
  788. protected var m_bUseSingleContainer:Boolean = true;
  789. protected var m_nDepth:Number = 0;
  790. protected var m_oContainer:Sprite;
  791. private var m_aToProject:Array = new Array();
  792. private var m_aVisiblePoly:Array = new Array();
  793. private var m_nVisiblePoly:int;
  794. private var m_bMouseInteractivity:Boolean = false;
  795. private var m_bForcedSingleContainer:Boolean = false;
  796. }
  797. }