PageRenderTime 59ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/sandy/as3/branches/3.0.2/src/sandy/materials/BitmapMaterial.as

http://sandy.googlecode.com/
ActionScript | 475 lines | 324 code | 45 blank | 106 comment | 52 complexity | 266d7d5bf26d99456603132116e92006 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.materials
  16. {
  17. import flash.display.BitmapData;
  18. import flash.display.Graphics;
  19. import flash.display.Sprite;
  20. import flash.filters.ColorMatrixFilter;
  21. import flash.geom.Matrix;
  22. import flash.geom.Point;
  23. import flash.geom.Rectangle;
  24. import flash.utils.Dictionary;
  25. import sandy.core.Scene3D;
  26. import sandy.core.data.Polygon;
  27. import sandy.core.data.Vertex;
  28. import sandy.materials.attributes.MaterialAttributes;
  29. import sandy.util.NumberUtil;
  30. /**
  31. * Displays a bitmap on the faces of a 3D shape..
  32. *
  33. * @author Thomas Pfeiffer - kiroukou
  34. * @author Xavier Martin - zeflasher - transparency managment
  35. * @author Makc for first renderRect implementation
  36. * @author James Dahl - optimization in renderRec method
  37. * @version 3.0
  38. * @date 26.07.2007
  39. */
  40. public class BitmapMaterial extends Material
  41. {
  42. /**
  43. * This property enable smooth bitmap rendering when set to true.
  44. * The default value is set to false to have the best performance first.
  45. * Enable this property have a performance impact, use it warefully
  46. */
  47. public var smooth:Boolean = false;
  48. /**
  49. * Precision of the bitmap mapping.
  50. * This material uses an affine linear mapping. It results in a lack of accuracy at rendering time when the surface to draw is too big.
  51. * An usual solution is to augment the number of polygon, but the performance cost can be quite big.
  52. * An other solution is to change this property value. The lower to more accurate the perspective correction is.
  53. * To disable the perspective correction, set this property to zero, which is also the default value
  54. * If you use the precision to solve the distortion issue, you can reduce the primitives quality (execpt if you are experimenting some sorting issues)
  55. */
  56. public var precision:uint = 0;
  57. /**
  58. * Maximum recurssion depth when using precision > 1 (which enables the perspective correction).
  59. * The bigger the number is, the more accurate the result will be.
  60. * Try to change this value to fits your needs to obtain the best performance.
  61. */
  62. public var maxRecurssionDepth:uint = 5;
  63. /**
  64. * Creates a new BitmapMaterial.
  65. * <p>Please note that we ue internally a copy of the constructor bitmapdata. Thatea mns in case you need to access this bitmapdata, you can't just use the same reference
  66. * but you shall use the BitmapMaterial#texture getter property to make it work.</p>
  67. * @param p_oTexture The bitmapdata for this material
  68. * @param p_oAttr The attributes for this material
  69. * @param p_nPrecision The precision of this material. Using a precision with 0 makes the material behave as before. Then 1 as precision is very high and requires a lot of computation but proceed a the best perpective mapping correction. Bigger values are less CPU intensive but also less accurate. Usually a value of 5 is enough.
  70. */
  71. public function BitmapMaterial( p_oTexture:BitmapData = null, p_oAttr:MaterialAttributes = null, p_nPrecision:uint = 0 )
  72. {
  73. super(p_oAttr);
  74. // --
  75. m_oType = MaterialType.BITMAP;
  76. // --
  77. var temp:BitmapData = new BitmapData( p_oTexture.width, p_oTexture.height, true, 0 );
  78. temp.draw( p_oTexture );
  79. texture = temp;
  80. // --
  81. m_oCmf = new ColorMatrixFilter();
  82. m_oPolygonMatrixMap = new Dictionary( true );
  83. precision = p_nPrecision;
  84. }
  85. /**
  86. * Renders this material on the face it dresses
  87. *
  88. * @param p_oScene The current scene
  89. * @param p_oPolygon The face to be rendered
  90. * @param p_mcContainer The container to draw on
  91. */
  92. public override function renderPolygon( p_oScene:Scene3D, p_oPolygon:Polygon, p_mcContainer:Sprite ):void
  93. {
  94. if( m_oTexture == null ) return;
  95. // --
  96. var l_points:Array, l_uv:Array;
  97. // --
  98. polygon = p_oPolygon;
  99. graphics = p_mcContainer.graphics;
  100. // --
  101. m_nRecLevel = 0;
  102. // --
  103. if( polygon.isClipped )
  104. {
  105. l_points = p_oPolygon.cvertices.slice();
  106. l_uv = p_oPolygon.caUVCoord.slice();
  107. _tesselatePolygon( l_points, l_uv );
  108. }
  109. else if( polygon.vertices.length > 3 )
  110. {
  111. l_points = p_oPolygon.vertices.slice();
  112. l_uv = p_oPolygon.aUVCoord.slice();
  113. _tesselatePolygon( l_points, l_uv );
  114. }
  115. else
  116. {
  117. l_points = p_oPolygon.vertices;
  118. l_uv = p_oPolygon.aUVCoord;
  119. // --
  120. map = (m_oPolygonMatrixMap[polygon.id] as Matrix );
  121. var v0:Vertex = l_points[0];
  122. var v1:Vertex = l_points[1];
  123. var v2:Vertex = l_points[2];
  124. if( precision == 0 )
  125. {
  126. renderTriangle(map.a, map.b, map.c, map.d, map.tx, map.ty, v0.sx, v0.sy, v1.sx, v1.sy, v2.sx, v2.sy );
  127. }
  128. else
  129. {
  130. renderRec( map.a, map.b, map.c, map.d, map.tx, map.ty,
  131. v0.sx, v0.sy, v0.wz,
  132. v1.sx, v1.sy, v1.wz,
  133. v2.sx, v2.sy, v2.wz);
  134. }
  135. }
  136. // --
  137. if( attributes ) attributes.draw( graphics, polygon, this, p_oScene ) ;
  138. // --
  139. l_points = null;
  140. l_uv = null;
  141. }
  142. protected function _tesselatePolygon ( p_aPoints:Array, p_aUv:Array ):void
  143. {
  144. var l_points: Array = p_aPoints.slice();
  145. var l_uv: Array = p_aUv.slice();
  146. // --
  147. if( l_points.length > 3 )
  148. {
  149. l_points = l_points.slice( 0, 3 );
  150. l_uv = l_uv.slice( 0, 3 );
  151. // --
  152. p_aPoints.splice( 1, 1 );
  153. p_aUv.splice( 1, 1 );
  154. // --
  155. _tesselatePolygon( p_aPoints, p_aUv );
  156. }
  157. // --
  158. map = _createTextureMatrix( l_uv );
  159. // --
  160. var v0:Vertex = l_points[0];
  161. var v1:Vertex = l_points[1];
  162. var v2:Vertex = l_points[2];
  163. if( precision == 0 )
  164. {
  165. renderTriangle(map.a, map.b, map.c, map.d, map.tx, map.ty, v0.sx, v0.sy, v1.sx, v1.sy, v2.sx, v2.sy );
  166. }
  167. else
  168. {
  169. renderRec( map.a, map.b, map.c, map.d, map.tx, map.ty,
  170. v0.sx, v0.sy, v0.wz,
  171. v1.sx, v1.sy, v1.wz,
  172. v2.sx, v2.sy, v2.wz);
  173. }
  174. // --
  175. l_points = null;
  176. l_uv = null;
  177. }
  178. protected function renderRec( ta:Number, tb:Number, tc:Number, td:Number, tx:Number, ty:Number,
  179. ax:Number, ay:Number, az:Number, bx:Number, by:Number, bz:Number, cx:Number, cy:Number, cz:Number):void
  180. {
  181. m_nRecLevel++;
  182. var ta2:Number = ta+ta;
  183. var tb2:Number = tb+tb;
  184. var tc2:Number = tc+tc;
  185. var td2:Number = td+td;
  186. var tx2:Number = tx+tx;
  187. var ty2:Number = ty+ty;
  188. var mabz:Number = 2 / (az + bz);
  189. var mbcz:Number = 2 / (bz + cz);
  190. var mcaz:Number = 2 / (cz + az);
  191. var mabx:Number = (ax*az + bx*bz)*mabz;
  192. var maby:Number = (ay*az + by*bz)*mabz;
  193. var mbcx:Number = (bx*bz + cx*cz)*mbcz;
  194. var mbcy:Number = (by*bz + cy*cz)*mbcz;
  195. var mcax:Number = (cx*cz + ax*az)*mcaz;
  196. var mcay:Number = (cy*cz + ay*az)*mcaz;
  197. var dabx:Number = ax + bx - mabx;
  198. var daby:Number = ay + by - maby;
  199. var dbcx:Number = bx + cx - mbcx;
  200. var dbcy:Number = by + cy - mbcy;
  201. var dcax:Number = cx + ax - mcax;
  202. var dcay:Number = cy + ay - mcay;
  203. var dsab:Number = (dabx*dabx + daby*daby);
  204. var dsbc:Number = (dbcx*dbcx + dbcy*dbcy);
  205. var dsca:Number = (dcax*dcax + dcay*dcay);
  206. var mabxHalf:Number = mabx*0.5;
  207. var mabyHalf:Number = maby*0.5;
  208. var azbzHalf:Number = (az+bz)*0.5;
  209. var mcaxHalf:Number = mcax*0.5;
  210. var mcayHalf:Number = mcay*0.5;
  211. var czazHalf:Number = (cz+az)*0.5;
  212. var mbcxHalf:Number = mbcx*0.5;
  213. var mbcyHalf:Number = mbcy*0.5;
  214. var bzczHalf:Number = (bz+cz)*0.5;
  215. if (( m_nRecLevel > maxRecurssionDepth ) || ((dsab <= precision) && (dsca <= precision) && (dsbc <= precision)))
  216. {
  217. renderTriangle(ta, tb, tc, td, tx, ty, ax, ay, bx, by, cx, cy);
  218. m_nRecLevel--; return;
  219. }
  220. if ((dsab > precision) && (dsca > precision) && (dsbc > precision) )
  221. {
  222. renderRec(ta2, tb2, tc2, td2, tx2, ty2,
  223. ax, ay, az, mabxHalf, mabyHalf, azbzHalf, mcaxHalf, mcayHalf, czazHalf);
  224. renderRec(ta2, tb2, tc2, td2, tx2-1, ty2,
  225. mabxHalf, mabyHalf, azbzHalf, bx, by, bz, mbcxHalf, mbcyHalf, bzczHalf);
  226. renderRec(ta2, tb2, tc2, td2, tx2, ty2-1,
  227. mcaxHalf, mcayHalf, czazHalf, mbcxHalf, mbcyHalf, bzczHalf, cx, cy, cz);
  228. renderRec(-ta2, -tb2, -tc2, -td2, -tx2+1, -ty2+1,
  229. mbcxHalf, mbcyHalf, bzczHalf, mcaxHalf, mcayHalf, czazHalf, mabxHalf, mabyHalf, azbzHalf);
  230. m_nRecLevel--; return;
  231. }
  232. var dmax:Number = Math.max(dsab, Math.max(dsca, dsbc));
  233. if (dsab == dmax)
  234. {
  235. renderRec(ta2, tb, tc2, td, tx2, ty,
  236. ax, ay, az, mabxHalf, mabyHalf, azbzHalf, cx, cy, cz);
  237. renderRec(ta2+tb, tb, tc2+td, td, tx2+ty-1, ty,
  238. mabxHalf, mabyHalf, azbzHalf, bx, by, bz, cx, cy, cz);
  239. m_nRecLevel--; return;
  240. }
  241. if (dsca == dmax)
  242. {
  243. renderRec(ta, tb2, tc, td2, tx, ty2,
  244. ax, ay, az, bx, by, bz, mcaxHalf, mcayHalf, czazHalf);
  245. renderRec(ta, tb2 + ta, tc, td2 + tc, tx, ty2+tx-1,
  246. mcaxHalf, mcayHalf, czazHalf, bx, by, bz, cx, cy, cz);
  247. m_nRecLevel--; return;
  248. }
  249. renderRec(ta-tb, tb2, tc-td, td2, tx-ty, ty2,
  250. ax, ay, az, bx, by, bz, mbcxHalf, mbcyHalf, bzczHalf);
  251. renderRec(ta2, tb-ta, tc2, td-tc, tx2, ty-tx,
  252. ax, ay, az, mbcxHalf, mbcyHalf, bzczHalf, cx, cy, cz);
  253. m_nRecLevel--;
  254. }
  255. protected function renderTriangle(a:Number, b:Number, c:Number, d:Number, tx:Number, ty:Number,
  256. v0x:Number, v0y:Number, v1x:Number, v1y:Number, v2x:Number, v2y:Number):void
  257. {
  258. var a2:Number = v1x - v0x;
  259. var b2:Number = v1y - v0y;
  260. var c2:Number = v2x - v0x;
  261. var d2:Number = v2y - v0y;
  262. matrix.a = a*a2 + b*c2;
  263. matrix.b = a*b2 + b*d2;
  264. matrix.c = c*a2 + d*c2;
  265. matrix.d = c*b2 + d*d2;
  266. matrix.tx = tx*a2 + ty*c2 + v0x;
  267. matrix.ty = tx*b2 + ty*d2 + v0y;
  268. graphics.lineStyle();
  269. graphics.beginBitmapFill(m_oTexture, matrix, repeat, smooth);
  270. graphics.moveTo(v0x, v0y);
  271. graphics.lineTo(v1x, v1y);
  272. graphics.lineTo(v2x, v2y);
  273. graphics.endFill();
  274. }
  275. protected function _createTextureMatrix( p_aUv:Array ):Matrix
  276. {
  277. var u0: Number = (p_aUv[0].u * m_oTiling.x + m_oOffset.x) * m_nWidth,
  278. v0: Number = (p_aUv[0].v * m_oTiling.y + m_oOffset.y) * m_nHeight,
  279. u1: Number = (p_aUv[1].u * m_oTiling.x + m_oOffset.x) * m_nWidth,
  280. v1: Number = (p_aUv[1].v * m_oTiling.y + m_oOffset.y) * m_nHeight,
  281. u2: Number = (p_aUv[2].u * m_oTiling.x + m_oOffset.x) * m_nWidth,
  282. v2: Number = (p_aUv[2].v * m_oTiling.y + m_oOffset.y) * m_nHeight;
  283. // -- Fix perpendicular projections. Not sure it is really useful here since there's no texture prjection. This will certainly solve the freeze problem tho
  284. if( (u0 == u1 && v0 == v1) || (u0 == u2 && v0 == v2) )
  285. {
  286. u0 -= (u0 > 0.05)? 0.05 : -0.05;
  287. v0 -= (v0 > 0.07)? 0.07 : -0.07;
  288. }
  289. if( u2 == u1 && v2 == v1 )
  290. {
  291. u2 -= (u2 > 0.05)? 0.04 : -0.04;
  292. v2 -= (v2 > 0.06)? 0.06 : -0.06;
  293. }
  294. // --
  295. var m:Matrix = new Matrix( (u1 - u0), (v1 - v0), (u2 - u0), (v2 - v0), u0, v0 );
  296. m.invert();
  297. return m;
  298. }
  299. /**
  300. * The texture ( bitmap ) of this material
  301. */
  302. public function get texture():BitmapData
  303. {
  304. return m_oTexture;
  305. }
  306. /**
  307. * @private
  308. */
  309. public function set texture( p_oTexture:BitmapData ):void
  310. {
  311. if( p_oTexture == m_oTexture )
  312. {
  313. return;
  314. }
  315. else
  316. {
  317. if( m_oTexture ) m_oTexture.dispose();
  318. if( m_orgTexture ) m_orgTexture.dispose();
  319. }
  320. // --
  321. var l_bReWrap:Boolean = false;
  322. if( m_nHeight != p_oTexture.height) l_bReWrap = true;
  323. else if( m_nWidth != p_oTexture.width) l_bReWrap = true;
  324. // --
  325. m_oTexture = p_oTexture;
  326. m_orgTexture = p_oTexture.clone();
  327. m_nHeight = m_oTexture.height;
  328. m_nWidth = m_oTexture.width;
  329. m_nInvHeight = 1/m_nHeight;
  330. m_nInvWidth = 1/m_nWidth;
  331. // -- We reinitialize the precomputed matrix
  332. if( l_bReWrap )
  333. {
  334. for( var l_sID:String in m_oPolygonMatrixMap )
  335. {
  336. var l_oPoly:Polygon = Polygon.POLYGON_MAP[ l_sID ];
  337. init( l_oPoly );
  338. }
  339. }
  340. }
  341. /**
  342. * Sets texture tiling and optional offset. Tiling is applied first.
  343. */
  344. public function setTiling( p_nW:Number, p_nH:Number, p_nU:Number = 0, p_nV:Number = 0 ):void
  345. {
  346. m_oTiling.x = p_nW;
  347. m_oTiling.y = p_nH;
  348. // --
  349. m_oOffset.x = p_nU;
  350. m_oOffset.y = p_nV;
  351. // --
  352. for( var l_sID:String in m_oPolygonMatrixMap )
  353. {
  354. var l_oPoly:Polygon = Polygon.POLYGON_MAP[ l_sID ];
  355. init( l_oPoly );
  356. }
  357. }
  358. /**
  359. * Changes the transparency of the texture.
  360. *
  361. * <p>The passed value is the percentage of opacity.</p>
  362. *
  363. * @param p_nValue A value between 0 and 1. (automatically constrained)
  364. */
  365. public function setTransparency( p_nValue:Number ):void
  366. {
  367. p_nValue = NumberUtil.constrain( p_nValue, 0, 1 );
  368. if( m_oCmf ) m_oCmf = null;
  369. var matrix:Array = [ 1, 0, 0, 0, 0,
  370. 0, 1, 0, 0, 0,
  371. 0, 0, 1, 0, 0,
  372. 0, 0, 0, p_nValue, 0];
  373. m_oCmf = new ColorMatrixFilter( matrix );
  374. texture.applyFilter( m_orgTexture, texture.rect, m_oPoint, m_oCmf );
  375. }
  376. public override function unlink( p_oPolygon:Polygon ):void
  377. {
  378. if( m_oPolygonMatrixMap[p_oPolygon.id] )
  379. delete m_oPolygonMatrixMap[p_oPolygon.id];
  380. // --
  381. super.unlink( p_oPolygon );
  382. }
  383. /**
  384. * Initiates this material.
  385. *
  386. * @param p_oPolygon The face dressed by this material
  387. */
  388. public override function init( p_oPolygon:Polygon ):void
  389. {
  390. if( p_oPolygon.vertices.length >= 3 )
  391. {
  392. var m:Matrix = null;
  393. // --
  394. if( m_nWidth > 0 && m_nHeight > 0 )
  395. {
  396. var l_aUV:Array = p_oPolygon.aUVCoord;
  397. if( l_aUV )
  398. {
  399. m = _createTextureMatrix( l_aUV );
  400. }
  401. }
  402. // --
  403. m_oPolygonMatrixMap[p_oPolygon.id] = m;
  404. }
  405. // --
  406. super.init( p_oPolygon );
  407. }
  408. public function toString():String
  409. {
  410. return 'sandy.materials.BitmapMaterial' ;
  411. }
  412. internal var polygon:Polygon;
  413. internal var graphics:Graphics;
  414. internal var map:Matrix = new Matrix();
  415. protected var m_oTexture:BitmapData;
  416. protected var m_orgTexture:BitmapData;
  417. private var m_nHeight:Number;
  418. private var m_nWidth:Number;
  419. private var m_nInvHeight:Number;
  420. private var m_nInvWidth:Number;
  421. private var m_nRecLevel:int = 0;
  422. protected var m_oPolygonMatrixMap:Dictionary;
  423. protected var m_oPoint:Point = new Point();
  424. protected var m_oCmf:ColorMatrixFilter;
  425. protected var matrix:Matrix = new Matrix();
  426. protected const m_oTiling:Point = new Point( 1, 1 );
  427. protected const m_oOffset:Point = new Point( 0, 0 );
  428. }
  429. }