Da Fish in Sea

These are the voyages of Captain Observant

Lighting a 3D Object in Flash, Pt 1

| Comments

This is the first part in a tutorial on using a normal map to light a sphere in Flash.

First of all, we need some object to light.. for this I revisited the Sphere I did earlier, and improved the method of its construction, using radians instead of degrees, and also generating the indices in the same loop as the vertices. First, here is what it looks like with a flat, outlined fill…

flash 10 required

Since I just installed the snazzy CodeColorer plugin, here’s the code…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package com.dafishinsea.tutorials.normalmap
{
  import flash.display.BitmapData;
  import flash.display.Sprite;
  import flash.display.TriangleCulling;
  import flash.events.Event;
  import flash.geom.Matrix3D;
  import flash.geom.PerspectiveProjection;
  import flash.geom.Utils3D;
  import flash.geom.Vector3D;
  import flash.text.TextField;
  /**
  * procedural model of a Pumpkin to demonstrate use of normal map
  */
  [SWF(backgroundColor="0x000000", width="800", height="600", frameRate="20")]
  public class NormalMap1 extends Sprite
  {

      private var vertices:Vector.<Number>;
      private var projectedVerts:Vector.<Number>;
      private var numverts:int;
      private var indices:Vector.<int>;
      private var uvts:Vector.<Number>;
      private var texture:BitmapData;
      private var perspective:PerspectiveProjection;
      private var projMatrix:Matrix3D;
      private var sphereRadius:Number;
      private var sphereCenter:Vector3D;
      private var rows:int;//number of times the sphere is sliced to form segments
      private var cols:int;
      private const PI:Number = Math.PI;//half revolution in radians
      private const HALFPI:Number = Math.PI/2;//1/4 revolution in radians
      private const TWOPI:Number = 2*Math.PI;//full revolution in radians
      private var canvas:Sprite;
      private var r:Number = 0;
      //the number of segments = num slices - 1
      //this is the same as lines of longitude
      //thus there are twice as many as horizontal slices

      public function NormalMap1()
      {
          init();
          createMesh();
          addEventListener(Event.ENTER_FRAME, onEnterFrame);
          addEventListener(Event.RENDER, render);
          //render();
      }
      private function onEnterFrame(event:Event):void {
          r = r + 0.2;
          prerender();
          stage.invalidate();
      }


      /**
      * initialize common properties
      */
      private function init():void
      {
          perspective = root.transform.perspectiveProjection;
          texture = new BitmapData(800,800,false,0xCCCCCC);
          sphereRadius = 100;
          //sphereCenter = new Vector3D(stage.stageWidth/2, stage.stageHeight/2, 200);
          cols = 40;
          rows = 20;
          numverts = (cols+1)*(rows+1);
          vertices = new Vector.<Number>(numverts*3);//x,y,z for each vertex
          projectedVerts = new Vector.<Number>(numverts*3);
          uvts = new Vector.<Number>(numverts*3);
          indices = new Vector.<int>();
          //we need to work around the fact that Utils3D.projectVectors disregards the projection center
          //of the projectionMatrix it is passed .. it always projects around 0,0
          //so we must render things on a canvas centered on stage
          canvas = new Sprite();
          canvas.x = stage.stageWidth/2;
          canvas.y = stage.stageHeight/2;
          addChild(canvas);
      }

      /**
      * create the mesh of the Sphere
      */
      private function createMesh():void
      {
          var lon_incr:Number = TWOPI/cols;
          var lat_incr = PI/rows;
          var lon:Number = 0;//angle of rotation around the y axis, *in radians*
          var lat:Number = 0;//angle of rotation around the x axis
          var x:Number, y:Number, z:Number;
          var vnum:int = 0;
          var ind:int = 0;
          //a full rotation is PI radians
          for(var h:int = 0; h <= rows; ++h)
          {
              y = sphereRadius*Math.cos(lat);//need to shift angle downwards by 1/4 rev
              for(var v:int = 0; v <= cols; ++v)
              {
                  x = sphereRadius*Math.cos(lon)*Math.sin(lat);
                  z = sphereRadius*Math.sin(lon)*Math.sin(lat);//seen from above, z = y

                  //add vertex triplet
                  vertices[vnum] = x;
                  vertices[vnum+1] = y;
                  vertices[vnum+2] = z;
                  vnum+=3;
                  //add indices
                  if(h < rows && v < cols){
                      indices.push(ind, ind+1, ind + cols+1);
                      indices.push(ind + cols+1, ind+1, ind + cols + 2);
                  }
                  ind+=1;
                  lon += lon_incr;
              }
              lat += lat_incr;
          }
      }

      /**
      * prerender the sphere -- project vertices using rotated matrix
      */
      private function prerender():void
      {
          projMatrix = perspective.toMatrix3D();
          projMatrix.prependTranslation(0,0,400);
          projMatrix.prependRotation(r, new Vector3D(0,1,0));
          Utils3D.projectVectors(projMatrix, vertices, projectedVerts,uvts);
      }

      /**
      * render
      */
      private function render(event:Event):void
      {
          canvas.graphics.clear();
          canvas.graphics.beginFill(0xFFFFFF);
          canvas.graphics.lineStyle(1,0xcccccc,1);
          canvas.graphics.drawTriangles(projectedVerts, indices, null /* uvts */, TriangleCulling.POSITIVE);
          canvas.graphics.endFill();
      }
  }
}

And if you actually want to download the code, here’s the link:

Da Code

NormalMap1 (@GitHub)