/src/Stage3dEntity.as
ActionScript | 596 lines | 483 code | 70 blank | 43 comment | 48 complexity | 1bfa201be10495496d35b1950589181f MD5 | raw file
1// Stage3d game entity class version 1.3
2//
3package
4{
5import com.adobe.utils.*;
6import flash.display.Stage3D;
7import flash.display3D.Context3D;
8import flash.display3D.Context3DProgramType;
9import flash.display3D.Context3DTriangleFace;
10import flash.display3D.Context3DVertexBufferFormat;
11import flash.display3D.IndexBuffer3D;
12import flash.display3D.Program3D;
13import flash.display3D.VertexBuffer3D;
14import flash.display3D.*;
15import flash.display3D.textures.*;
16import flash.geom.Matrix;
17import flash.geom.Matrix3D;
18import flash.geom.Vector3D;
19
20public class Stage3dEntity
21{
22 // Matrix variables (position, rotation, etc.)
23 private var _transform:Matrix3D;
24 private var _inverseTransform:Matrix3D;
25 private var _transformNeedsUpdate:Boolean;
26 private var _valuesNeedUpdate:Boolean;
27 private var _x:Number = 0;
28 private var _y:Number = 0;
29 private var _z:Number = 0;
30 private var _rotationDegreesX:Number = 0;
31 private var _rotationDegreesY:Number = 0;
32 private var _rotationDegreesZ:Number = 0;
33 private var _scaleX:Number = 1;
34 private var _scaleY:Number = 1;
35 private var _scaleZ:Number = 1;
36 private const RAD_TO_DEG:Number = 180/Math.PI;
37
38 // Stage3d objects (public so they can be inherited)
39 public var context:Context3D;
40 public var vertexBuffer:VertexBuffer3D;
41 public var indexBuffer:IndexBuffer3D;
42 public var shader:Program3D;
43 public var texture:Texture;
44 public var mesh:Stage3dObjParser;
45 // Render modes:
46 public var cullingMode:String = Context3DTriangleFace.FRONT;
47 public var blendSrc:String = Context3DBlendFactor.ONE;
48 public var blendDst:String = Context3DBlendFactor.ZERO;
49 public var depthTestMode:String = Context3DCompareMode.LESS;
50 public var depthTest:Boolean = true;
51 public var depthDraw:Boolean = true;
52
53 // used only for stats
54 public var polycount:uint = 0;
55
56 // if this is set entity is "stuck" to another
57 public var following:Stage3dEntity;
58
59 // optimize what data we need to send to Stage3D
60 // this depends on what the shaders require
61 public var shaderUsesUV:Boolean = true;
62 public var shaderUsesRgba:Boolean = true;
63 public var shaderUsesNormals:Boolean = true; // false;
64
65 // Class Constructor
66 public function Stage3dEntity(
67 mydata:Class = null,
68 mycontext:Context3D = null,
69 myshader:Program3D = null,
70 mytexture:Texture = null,
71 modelscale:Number = 1,
72 flipAxis:Boolean = true,
73 flipTexture: Boolean = true)
74 {
75 _transform = new Matrix3D();
76 context = mycontext;
77 shader = myshader;
78 texture = mytexture;
79 if (mydata && context)
80 {
81 mesh = new Stage3dObjParser(
82 mydata, context, modelscale, flipAxis, flipTexture);
83 polycount = mesh.indexBufferCount;
84 trace("Mesh has " + polycount + " polygons.");
85 }
86 }
87
88 public function get transform():Matrix3D
89 {
90 if(_transformNeedsUpdate)
91 updateTransformFromValues();
92 return _transform;
93 }
94
95 public function set transform(value:Matrix3D):void
96 {
97 _transform = value;
98 _transformNeedsUpdate = false;
99 _valuesNeedUpdate = true;
100 }
101
102 // Position:
103
104 public function set position(value:Vector3D):void
105 {
106 _x = value.x;
107 _y = value.y;
108 _z = value.z;
109 _transformNeedsUpdate = true;
110 }
111
112 private var _posvec:Vector3D = new Vector3D();
113 public function get position():Vector3D
114 {
115 if(_valuesNeedUpdate)
116 updateValuesFromTransform();
117 // optimization: avoid creating temporary variable
118 // e.g. return new Vector3D(_x, _y, _z);
119 _posvec.setTo(_x, _y, _z);
120 return _posvec;
121 }
122
123 public function set x(value:Number):void
124 {
125 _x = value;
126 _transformNeedsUpdate = true;
127 }
128 public function get x():Number
129 {
130 if(_valuesNeedUpdate)
131 updateValuesFromTransform();
132 return _x;
133 }
134
135 public function set y(value:Number):void
136 {
137 _y = value;
138 _transformNeedsUpdate = true;
139 }
140 public function get y():Number
141 {
142 if(_valuesNeedUpdate)
143 updateValuesFromTransform();
144 return _y;
145 }
146
147 public function set z(value:Number):void
148 {
149 _z = value;
150 _transformNeedsUpdate = true;
151 }
152 public function get z():Number
153 {
154 if(_valuesNeedUpdate)
155 updateValuesFromTransform();
156 return _z;
157 }
158
159 // Rotation:
160
161 public function set rotationDegreesX(value:Number):void
162 {
163 _rotationDegreesX = value;
164 _transformNeedsUpdate = true;
165 }
166 public function get rotationDegreesX():Number
167 {
168 if(_valuesNeedUpdate)
169 updateValuesFromTransform();
170 return _rotationDegreesX;
171 }
172
173 public function set rotationDegreesY(value:Number):void
174 {
175 _rotationDegreesY = value;
176 _transformNeedsUpdate = true;
177 }
178 public function get rotationDegreesY():Number
179 {
180 if(_valuesNeedUpdate)
181 updateValuesFromTransform();
182 return _rotationDegreesY;
183 }
184
185 public function set rotationDegreesZ(value:Number):void
186 {
187 _rotationDegreesZ = value;
188 _transformNeedsUpdate = true;
189 }
190 public function get rotationDegreesZ():Number
191 {
192 if(_valuesNeedUpdate)
193 updateValuesFromTransform();
194 return _rotationDegreesZ;
195 }
196
197 // Scale:
198
199 public function set scale(vec:Vector3D):void
200 {
201 _scaleX = vec.x;
202 _scaleY = vec.y;
203 _scaleZ = vec.z;
204 _transformNeedsUpdate = true;
205 }
206 private var _scalevec:Vector3D = new Vector3D();
207 public function get scale():Vector3D
208 {
209 if(_valuesNeedUpdate)
210 updateValuesFromTransform();
211 //return new Vector3D(_scaleX, _scaleY, _scaleZ, 1.0);
212
213 // optimization: avoid creating a temporary variable
214 _scalevec.setTo(_scaleX, _scaleX, _scaleZ);
215 _scalevec.w = 1.0;
216 return _scalevec;
217 }
218 public function set scaleXYZ(value:Number):void
219 {
220 _scaleX = value;
221 _scaleY = value;
222 _scaleZ = value;
223 _transformNeedsUpdate = true;
224 }
225 public function get scaleXYZ():Number
226 {
227 if(_valuesNeedUpdate)
228 updateValuesFromTransform();
229 return _scaleX; // impossible to determine
230 _transformNeedsUpdate = true;
231 }
232 public function set scaleX(value:Number):void
233 {
234 _scaleX = value;
235 _transformNeedsUpdate = true;
236 }
237 public function get scaleX():Number
238 {
239 if(_valuesNeedUpdate)
240 updateValuesFromTransform();
241 return _scaleX;
242 }
243
244 public function set scaleY(value:Number):void
245 {
246 _scaleY = value;
247 _transformNeedsUpdate = true;
248 }
249 public function get scaleY():Number
250 {
251 if(_valuesNeedUpdate)
252 updateValuesFromTransform();
253 return _scaleY;
254 }
255
256 public function set scaleZ(value:Number):void
257 {
258 _scaleZ = value;
259 _transformNeedsUpdate = true;
260 }
261 public function get scaleZ():Number
262 {
263 if(_valuesNeedUpdate)
264 updateValuesFromTransform();
265 return _scaleZ;
266 }
267
268 // Update:
269
270 public function updateTransformFromValues():void
271 {
272 _transform.identity();
273
274 _transform.appendRotation(
275 _rotationDegreesX, Vector3D.X_AXIS);
276 _transform.appendRotation(
277 _rotationDegreesY, Vector3D.Y_AXIS);
278 _transform.appendRotation(
279 _rotationDegreesZ, Vector3D.Z_AXIS);
280
281 // avoid matrix error #2183:
282 // scale values must not be zero
283 if (_scaleX == 0) _scaleX = 0.0000001;
284 if (_scaleY == 0) _scaleY = 0.0000001;
285 if (_scaleZ == 0) _scaleZ = 0.0000001;
286 _transform.appendScale(_scaleX, _scaleY, _scaleZ);
287
288 _transform.appendTranslation(_x, _y, _z);
289
290 _transformNeedsUpdate = false;
291 }
292
293 public function updateValuesFromTransform():void
294 {
295 var d:Vector.<Vector3D> = _transform.decompose();
296
297 var position:Vector3D = d[0];
298 _x = position.x;
299 _y = position.y;
300 _z = position.z;
301
302 var rotation:Vector3D = d[1];
303 _rotationDegreesX = rotation.x*RAD_TO_DEG;
304 _rotationDegreesY = rotation.y*RAD_TO_DEG;
305 _rotationDegreesZ = rotation.z*RAD_TO_DEG;
306
307 var scale:Vector3D = d[2];
308 _scaleX = scale.x;
309 _scaleY = scale.y;
310 _scaleZ = scale.z;
311
312 _valuesNeedUpdate = false;
313 }
314
315 // Movement Utils:
316
317 // move according to the direction we are facing
318 public function moveForward(amt:Number):void
319 {
320 if (_transformNeedsUpdate)
321 updateTransformFromValues();
322 var v:Vector3D = frontvector;
323 v.scaleBy(-amt)
324 transform.appendTranslation(v.x, v.y, v.z);
325 _valuesNeedUpdate = true;
326 }
327 public function moveBackward(amt:Number):void
328 {
329 if (_transformNeedsUpdate)
330 updateTransformFromValues();
331 var v:Vector3D = backvector;
332 v.scaleBy(-amt)
333 transform.appendTranslation(v.x, v.y, v.z);
334 _valuesNeedUpdate = true;
335 }
336 public function moveUp(amt:Number):void
337 {
338 if (_transformNeedsUpdate)
339 updateTransformFromValues();
340 var v:Vector3D = upvector;
341 v.scaleBy(amt)
342 transform.appendTranslation(v.x, v.y, v.z);
343 _valuesNeedUpdate = true;
344 }
345 public function moveDown(amt:Number):void
346 {
347 if (_transformNeedsUpdate)
348 updateTransformFromValues();
349 var v:Vector3D = downvector;
350 v.scaleBy(amt)
351 transform.appendTranslation(v.x, v.y, v.z);
352 _valuesNeedUpdate = true;
353 }
354 public function moveLeft(amt:Number):void
355 {
356 if (_transformNeedsUpdate)
357 updateTransformFromValues();
358 var v:Vector3D = leftvector;
359 v.scaleBy(amt)
360 transform.appendTranslation(v.x, v.y, v.z);
361 _valuesNeedUpdate = true;
362 }
363 public function moveRight(amt:Number):void
364 {
365 if (_transformNeedsUpdate)
366 updateTransformFromValues();
367 var v:Vector3D = rightvector;
368 v.scaleBy(amt)
369 transform.appendTranslation(v.x, v.y, v.z);
370 _valuesNeedUpdate = true;
371 }
372
373 // optimization: these vectors are defined as constants
374 // to avoid creation of temporary variables each frame
375 private static const vecft:Vector3D = new Vector3D(0, 0, 1);
376 private static const vecbk:Vector3D = new Vector3D(0, 0, -1);
377 private static const veclf:Vector3D = new Vector3D(-1, 0, 0);
378 private static const vecrt:Vector3D = new Vector3D(1, 0, 0);
379 private static const vecup:Vector3D = new Vector3D(0, 1, 0);
380 private static const vecdn:Vector3D = new Vector3D(0, -1, 0);
381
382 public function get frontvector():Vector3D
383 {
384 if(_transformNeedsUpdate)
385 updateTransformFromValues();
386 return transform.deltaTransformVector(vecft);
387 }
388
389 public function get backvector():Vector3D
390 {
391 if(_transformNeedsUpdate)
392 updateTransformFromValues();
393 return transform.deltaTransformVector(vecbk);
394 }
395
396 public function get leftvector():Vector3D
397 {
398 if(_transformNeedsUpdate)
399 updateTransformFromValues();
400 return transform.deltaTransformVector(veclf);
401 }
402
403 public function get rightvector():Vector3D
404 {
405 if(_transformNeedsUpdate)
406 updateTransformFromValues();
407 return transform.deltaTransformVector(vecrt);
408 }
409
410 public function get upvector():Vector3D
411 {
412 if(_transformNeedsUpdate)
413 updateTransformFromValues();
414 return transform.deltaTransformVector(vecup);
415 }
416
417 public function get downvector():Vector3D
418 {
419 if(_transformNeedsUpdate)
420 updateTransformFromValues();
421 return transform.deltaTransformVector(vecdn);
422 }
423
424 // Handy Utils:
425
426 public function get rotationTransform():Matrix3D
427 {
428 var d:Vector.<Vector3D> = transform.decompose();
429 d[0] = new Vector3D();
430 d[1] = new Vector3D(1, 1, 1);
431 var t:Matrix3D = new Matrix3D();
432 t.recompose(d);
433 return t;
434 }
435
436 public function get reducedTransform():Matrix3D
437 {
438 var raw:Vector.<Number> = transform.rawData;
439 raw[3] = 0; // Remove translation.
440 raw[7] = 0;
441 raw[11] = 0;
442 raw[15] = 1;
443 raw[12] = 0;
444 raw[13] = 0;
445 raw[14] = 0;
446 var reducedTransform:Matrix3D = new Matrix3D();
447 reducedTransform.copyRawDataFrom(raw);
448 return reducedTransform;
449 }
450
451 public function get invRotationTransform():Matrix3D
452 {
453 var t:Matrix3D = rotationTransform;
454 t.invert();
455 return t;
456 }
457
458 public function get positionVector():Vector.<Number>
459 {
460 return Vector.<Number>([_x, _y, _z, 1.0]);
461 }
462
463 public function get inverseTransform():Matrix3D
464 {
465 _inverseTransform = transform.clone();
466 _inverseTransform.invert();
467
468 return _inverseTransform;
469 }
470
471 public function posString():String
472 {
473 if (_valuesNeedUpdate)
474 updateValuesFromTransform();
475
476 return _x.toFixed(2) + ','
477 + _y.toFixed(2) + ','
478 + _z.toFixed(2);
479 }
480
481 public function rotString():String
482 {
483 if (_valuesNeedUpdate)
484 updateValuesFromTransform();
485
486 return _rotationDegreesX.toFixed(2) + ','
487 + _rotationDegreesY.toFixed(2) + ','
488 + _rotationDegreesZ.toFixed(2);
489 }
490
491 public function follow(thisentity:Stage3dEntity):void
492 {
493 following = thisentity;
494 }
495
496 // create an exact duplicate in the game world
497 // whle re-using all Stage3d objects
498 public function clone():Stage3dEntity
499 {
500 if(_transformNeedsUpdate)
501 updateTransformFromValues();
502 var myclone:Stage3dEntity = new Stage3dEntity();
503 myclone.transform = this.transform.clone();
504 myclone.mesh = this.mesh;
505 myclone.texture = this.texture;
506 myclone.shader = this.shader;
507 myclone.vertexBuffer = this.vertexBuffer;
508 myclone.indexBuffer = this.indexBuffer;
509 myclone.context = this.context;
510 myclone.polycount = this.polycount;
511 myclone.shaderUsesNormals = this.shaderUsesNormals;
512 myclone.shaderUsesRgba = this.shaderUsesRgba;
513 myclone.shaderUsesUV = this.shaderUsesUV;
514 myclone.updateValuesFromTransform();
515 return myclone;
516 }
517
518 // optimization: reuse the same temporary matrix
519 private var _rendermatrix:Matrix3D = new Matrix3D();
520 // renders the entity, changing states only if required
521 public function render(
522 view:Matrix3D,
523 projection:Matrix3D,
524 statechanged:Boolean = true):void
525 {
526 // used only for debugging:
527 if (!mesh) trace("Missing mesh!");
528 if (!context) trace("Missing context!");
529 if (!shader) trace("Missing shader!");
530
531 // only render if these are set
532 if (!mesh) return;
533 if (!context) return;
534 if (!shader) return;
535
536 _rendermatrix.identity();
537 _rendermatrix.append(transform);
538 if (following) _rendermatrix.append(following.transform);
539 _rendermatrix.append(view);
540 _rendermatrix.append(projection);
541
542 // Set the vertex program register vc0 to our
543 // model view projection matrix = vc0
544 context.setProgramConstantsFromMatrix(
545 Context3DProgramType.VERTEX, 0, _rendermatrix, true);
546
547 // optimization: only change render state
548 // if the previously rendered actor is not
549 // using an identical mesh/shader as the current one
550 if (statechanged)
551 {
552 // Set the AGAL program
553 context.setProgram(shader);
554
555 // Set the fragment program register ts0 to a texture
556 if (texture) context.setTextureAt(0,texture);
557
558 // position (va0)
559 context.setVertexBufferAt(0, mesh.positionsBuffer,
560 0, Context3DVertexBufferFormat.FLOAT_3);
561
562 // tex coord (va1)
563 if (shaderUsesUV)
564 context.setVertexBufferAt(1, mesh.uvBuffer,
565 0, Context3DVertexBufferFormat.FLOAT_2);
566 else
567 context.setVertexBufferAt(1, null);
568
569 // vertex rgba (va2)
570 if (shaderUsesRgba)
571 context.setVertexBufferAt(2, mesh.colorsBuffer,
572 0, Context3DVertexBufferFormat.FLOAT_4);
573 else
574 context.setVertexBufferAt(2, null);
575
576 // vertex normal (va3)
577 if (shaderUsesNormals)
578 context.setVertexBufferAt(3, mesh.normalsBuffer,
579 0, Context3DVertexBufferFormat.FLOAT_3);
580 else
581 context.setVertexBufferAt(3, null);
582
583 context.setBlendFactors(blendSrc, blendDst);
584 context.setDepthTest(depthTest,depthTestMode);
585 context.setCulling(cullingMode);
586 context.setColorMask(true, true, true, depthDraw);
587 }
588
589 // render it
590 context.drawTriangles(mesh.indexBuffer,
591 0, mesh.indexBufferCount);
592
593 }
594
595} // end class
596} // end package