Having another look at the powerful drawTriangles method while playing with my shiny new CS5 reminded me that a while ago I was planning to do a full-blown cloth-like effect utilizing this little vintage experiment.
However, with the introduction of native 3D and numerous drawing API enhancements into Flash it is now possible to do this in a lot less less cumbersome fashion than before.
Consequently, please take a look at this wholesome virtual picnic tablecloth:
The real star of the show here is the drawTriangles method, which is rendering 800 textured triangles based on vertex, index and UV information supplied by Vectors (the grid fades in and out to show the mesh structure).
The wavy movement as well as the dark/light patches are achieved with a continuously redrawn perlinNoise map: it functions as sort of a height-map for the vertices, and it’s also scaled and blended with the texture to generate the dark/light areas corresponding to the height information.
One obvious way to improve the performance of this experiment would be to use fixed-size Vectors instead of dynamically changing ones.
I actually started off with that approach, but ran into mysterious problems trying to fill the Vectors with the correct data, and never quite managed to get the UV information come back the right way.
I have some theories regarding possible causes, but I eventually decided to move on for now and figure this problem out another day.
And now the (hopefully sufficiently commented) source:
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 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | // (c) edvardtoth.com package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.BitmapDataChannel; import flash.display.BlendMode; import flash.display.GradientType; import flash.display.InterpolationMethod; import flash.display.SpreadMethod; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageQuality; import flash.display.StageScaleMode; import flash.display.TriangleCulling; import flash.events.Event; import flash.geom.Matrix; import flash.geom.Matrix3D; import flash.geom.PerspectiveProjection; import flash.geom.Point; import flash.geom.Utils3D; import flash.geom.Vector3D; import flash.utils.getTimer; [SWF(width=550,height=550,frameRate=30,backgroundColor=0x0099ff)] public class DrawTriangleFabric extends Sprite { private const GRIDSIZE:int = 20; private const GRIDSIZE_MINUS_ONE:int = GRIDSIZE-1; private const OCTAVES:int = 3; private const SPEED:Number = 0.015; private var lineAlpha:Number = 0; private var lineAlphaMod:Number = 0.01; [Embed(source="assets.swf", symbol="Texture")] private var textureAsset:Class; private var textureSprite:Sprite; [Embed(source="assets.swf", symbol="Decal")] private var decalAsset:Class; private var decalSprite:Sprite; private var vertices:Vector.<Number> = new Vector.<Number>; private var uvtData:Vector.<Number> = new Vector.<Number>; private var indices:Vector.<int> = new Vector.<int>; private var projectedVerts:Vector.<Number> = new Vector.<Number>; private var noiseMap:BitmapData = new BitmapData(GRIDSIZE, GRIDSIZE, false); private var texture:BitmapData; private var view:PerspectiveProjection = new PerspectiveProjection(); private var canvas:Sprite = new Sprite(); private var transformMatrix:Matrix3D = new Matrix3D; private var scaleMatrix:Matrix = new Matrix(); public function DrawTriangleFabric() { // setup stage properties stage.scaleMode = StageScaleMode.NO_SCALE; stage.quality = StageQuality.MEDIUM; stage.align = StageAlign.TOP_LEFT; // generate texture from embedded asset textureSprite = new textureAsset() as Sprite; texture = new BitmapData (textureSprite.width, textureSprite.height, false); texture.draw (textureSprite); // center canvas canvas.x = stage.stageWidth * 0.5; canvas.y = stage.stageHeight * 0.5; addChild(canvas); // setup projection view.fieldOfView = 35; view.projectionCenter = new Point (stage.stageWidth * 0.5, stage.stageHeight * 0.5); // add decal decalSprite = new decalAsset() as Sprite; addChild (decalSprite); decalSprite.x = stage.stageWidth; decalSprite.y = 0; decalSprite.cacheAsBitmap = true; // position projection using a matrix transformMatrix.prependScale(.8,.03,.8); transformMatrix.appendRotation(-45, Vector3D.X_AXIS); transformMatrix.appendRotation(25, Vector3D.Y_AXIS); transformMatrix.appendRotation(180, Vector3D.Z_AXIS); transformMatrix.appendTranslation (-9.5,-7,-22); transformMatrix.append(view.toMatrix3D()); // prepare a matrix used to fit the noisemap to the bigger texturemap scaleMatrix.scale (texture.width / noiseMap.width, texture.height / noiseMap.height); addEventListener(Event.ENTER_FRAME, updateFrame, false, 0, true); } private function updateFrame(event:Event):void { // generate vertex, index and UV data for (var yPos:int = 0; yPos < GRIDSIZE; yPos++) { for (var xPos:int = 0; xPos < GRIDSIZE; xPos++) { // y coordinate (second value) is derived from clamped grayscale values of the noise map vertices.push (xPos-GRIDSIZE, -(noiseMap.getPixel(xPos, yPos) & 0xFF), yPos-GRIDSIZE); uvtData.push (xPos / GRIDSIZE_MINUS_ONE, yPos / GRIDSIZE_MINUS_ONE, 0); if (yPos < GRIDSIZE_MINUS_ONE && xPos < GRIDSIZE_MINUS_ONE) { // triangle 1 indices.push ( yPos * GRIDSIZE + xPos, yPos * GRIDSIZE + xPos + 1, (yPos + 1) * GRIDSIZE + xPos); // triangle 2 indices.push ( yPos * GRIDSIZE + xPos + 1, (yPos + 1) * GRIDSIZE + xPos + 1, (yPos + 1) * GRIDSIZE + xPos); } } } var offsets:Array = []; var offset:Number = getTimer() * SPEED; for(var i:uint = 0; i < OCTAVES; i++) { // offsets are used for animating the various octaves of the perlin noise map offsets.push(new Point(0, offset/(i+1))); } // render noiseMap noiseMap.perlinNoise(GRIDSIZE, GRIDSIZE, OCTAVES, 32, true, true, BitmapDataChannel.ALPHA, true, offsets); // refresh texture texture.draw (textureSprite); // draw noiseMap on top of texture with HARDLIGHT blendmode to achieve highlights/sheen texture.draw (noiseMap, scaleMatrix, null, BlendMode.HARDLIGHT, null, true); // project Utils3D.projectVectors(transformMatrix, vertices, projectedVerts, uvtData); // modify gridline alpha values lineAlpha += lineAlphaMod; if (lineAlpha >= 1.0 || lineAlpha <=0) { lineAlphaMod *= -1; } // draw canvas.graphics.clear(); canvas.graphics.lineStyle(1.0, 0x000000, lineAlpha); canvas.graphics.beginBitmapFill(texture, null, false, true); canvas.graphics.drawTriangles(projectedVerts, indices, uvtData, TriangleCulling.NONE); canvas.graphics.endFill(); // clear vectors vertices.length = 0; uvtData.length = 0; indices.length = 0; } } } |
0 comments
There are no comments yet...Kick things off by filling out the form below.
Leave a Comment