Shadow casting test
shadow mapping test (warning: CPU killer)
package
{
import flash.display.*;
import flash.events.*;
import flash.filters.*;
import flash.geom.*;
import sandy.core.*;
import sandy.core.data.*;
import sandy.core.scenegraph.*;
import sandy.primitive.*;
import sandy.materials.*;
import sandy.materials.attributes.*;
import sandy.view.*;
// shadow mapping test (warning: CPU killer)
[SWF (width=465, height=465, backgroundColor=0, frameRate=10)]
public class Test extends BasicView
{
private const S:int = 150;
private const Q:int = 10;
private const Y:Number = 30;
private var angle:Number = 0;
private var texture:BitmapData;
private var hmap:BitmapData, lmap:BitmapData, tmap:BitmapData;
private var blur:BlurFilter = new BlurFilter (2, 2, 2);
private var stmp:Shape = new Shape;
public function Test()
{
super (); init (465, 465); render ();
// planes suck - use hexagonal grid instead
var p:Plane3D = addHorizontalPlane (S, S, Q,
new Point3D (0, -0.6 * S, 0));
texture = new BitmapData (S, S, false, 0);
p.appearance = makeBitmapAppearance (texture);
// engine light
p.appearance.frontMaterial.attributes =
new MaterialAttributes (new LightAttributes (false, 0.6));
p.appearance.lightingEnable = true;
// build random terrain
// this is not really smooth terrain, but whatever
var r:Number = 0;
for each (var v:Vertex in p.geometry.aVertex) {
//r = 0.5 * (r + Math.random ()); v.y = Y * r;
if (Math.random () < 0.1) v.y = Y / 2;
}
// update face normals
for each (var face:Polygon in p.aPolygons) face.updateNormal ();
// render terrain heightmap
renderHeightMap (p, graphics, S);
hmap = new BitmapData (S, S, false, 0); hmap.draw (this);
// noisy texture
tmap = new BitmapData (S, S, false, 0);
tmap.noise (123, 200, 255, 7, true);
tmap.draw (tmap, null, new ColorTransform (1, 0xEF / 255.0, 0x7F / 255.0));
// make light map
lmap = new BitmapData (S, S, false, 0);
var b:Bitmap = new Bitmap (lmap); b.x = S; addChild (b);
// "sun"
graphics.beginFill (0xFFEF7F); graphics.drawCircle (465 * 0.5, 465 * 0.5, 10);
}
override public function simpleRender(e:Event = null):void {
// spin light (map coords)
angle = mouseX / 100;
var light:Point3D = new Point3D (Math.sin (angle), Math.cos (angle), 0.25); light.normalize ();
// face light (real coords)
camera.x = +2 * S * light.x;
camera.y = +2 * S * light.z;
camera.z = -2 * S * light.y;
camera.lookAt (0, 0, 0);
scene.light.setDirection (camera.x, -camera.y, camera.z);
// re-build shadow map
lmap.lock ();
lmap.fillRect (lmap.rect, 0);
var m:Number = 0; m1.identity ();
while (m < 255) {
m1.tx += light.x; m1.ty += light.y; m += 2; // TODO: m = f (light)
stmp.graphics.clear ();
stmp.graphics.beginBitmapFill (hmap, m1, true, true);
stmp.graphics.drawRect (0, 0, S, S);
stmp.graphics.endFill ();
lmap.draw (stmp, null, new ColorTransform (1, 1, 1, 1, -m, -m, -m),
BlendMode.LIGHTEN, null, true);
}
lmap.draw (hmap, null, null, BlendMode.DIFFERENCE);
// transform into light map
lmap.draw (lmap, null, new ColorTransform ( -10, -10, -10, 1, 255, 255, 255));
lmap.threshold (lmap, lmap.rect, lmap.rect.topLeft, "<", 0x808080, 0xFF404030, 0xFFFFFF);
lmap.threshold (lmap, lmap.rect, lmap.rect.topLeft, ">", 0x808080, 0xFFFFFFFF, 0xFFFFFF);
lmap.unlock ();
lmap.applyFilter (lmap, lmap.rect, lmap.rect.topLeft, blur);
texture.draw (tmap); texture.draw (lmap, null, null, BlendMode.MULTIPLY);
super.simpleRender (e);
}
private function renderHeightMap (p:Plane3D, g:Graphics, s:int):void {
for each (var t:Polygon in p.aPolygons)
renderHeightMapElement (t, g, s);
}
private var m1:Matrix = new Matrix;
private var m2:Matrix = new Matrix;
private function renderHeightMapElement (t:Polygon, g:Graphics, s:int):void {
var h0:Number = t.a.y / Y;
var h1:Number = t.b.y / Y;
var h2:Number = t.c.y / Y;
var v0:Number = -100, v1:Number = 0, v2:Number = 100;
var u0:Number = (h0 - 0.5) * (32768 * 0.05);
var u1:Number = (h1 - 0.5) * (32768 * 0.05);
var u2:Number = (h2 - 0.5) * (32768 * 0.05);
var uv:UVCoord = UVCoord (t.aUVCoord [0]); m2.tx = uv.u * s; m2.ty = uv.v * s;
if ( (int (u0 * 0.1) == int (u1 * 0.1)) && (int (u1 * 0.1) == int (u2 * 0.1)) ) {
// solid color case
g.beginFill ( 0x10101 * Math.round (0xFF * h0) );
} else {
// gradient case
if ((u2 - u1) * (u1 - u0) > 0) {
var tmp:Number = v1; v1 = v2; v2 = tmp;
}
m1.a = u1 - u0; m1.b = v1 - v0;
m1.c = u2 - u0; m1.d = v2 - v0;
m1.tx = u0; m1.ty = v0;
m1.invert ();
uv = UVCoord (t.aUVCoord [1]);
m2.a = uv.u * s - m2.tx; m2.b = uv.v * s - m2.ty;
uv = UVCoord (t.aUVCoord [2]);
m2.c = uv.u * s - m2.tx; m2.d = uv.v * s - m2.ty;
m1.concat (m2);
g.beginGradientFill ("linear", [0, 0xFFFFFF], [1, 1], [0, 255], m1);
}
g.moveTo (m2.tx, m2.ty); for each (uv in t.aUVCoord) g.lineTo (uv.u * s, uv.v * s);
g.endFill ();
}
}
}