forked from: FloodFilledDynamicShadows = Optimized
Shadow-casting vertex finding algorithm reference:
http://board.flashkit.com/board/showthread.php?t=798017
Scanline flood algorithm reference:
http://lodev.org/cgtutor/floodfill.html#Scanline_Floodfill_Algorithm_With_Stack
Optimization : Paul Ollivier @ FLASHMAFIA 2014
http://wonderfl.net/user/FLASHMAFIA/codes
/**
* Copyright werW ( http://wonderfl.net/user/werW )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/1HnI
*/
// forked from FLASHMAFIA's FloodFilledDynamicShadows = Optimized
package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
[SWF(width = '512', height = '512')]
//
//
// Shadow-casting vertex finding algorithm reference:
// http://board.flashkit.com/board/showthread.php?t=798017
//
// Scanline flood algorithm reference:
// http://lodev.org/cgtutor/floodfill.html#Scanline_Floodfill_Algorithm_With_Stack
//
// Optimization : Paul Ollivier @ FLASHMAFIA 2014
// http://wonderfl.net/user/FLASHMAFIA/codes
//
//
final public class FloodShadowB01 extends Sprite//
{
private const SW : Number = 512;
private const SH : Number = 512;
//
private const NUM_OBSTACLES : uint = 256;
private const OBSTACLE_SIZE_MIN : Number = 2;
private const OBSTACLE_SIZE_MAX : Number = 16;
//
private var _obstacles : Vector.<Polygon>;
private var _dst : BitmapData;
//
private var _stackX : Vector.<Number> = new Vector.<Number>(512);
private var _stackY : Vector.<Number> = new Vector.<Number>(512);
private var _stackSize : int = 0;
private var _fcnt : int;
private var _th : Number = 0.0;
private var _tx : int;
private var _ty : int;
private var _x : int;
private var _y : int;
function FloodShadowB01()//
{
if (stage) onStage(null);
else addEventListener('addedToStage', onStage, false, 0, true);
}
final private function onStage(e : Event) : void//
{
if (e) removeEventListener(e.type, onStage);
stage.scaleMode = 'noScale';
stage.align = 'TL';
stage.quality = 'low';
stage.frameRate = 64;
stage.stageFocusRect = mouseEnabled = mouseChildren = tabEnabled = tabChildren = false;
opaqueBackground = stage.color = 0xFFFFFF;
_obstacles = new Vector.<Polygon>();
for (var i : int = 0; i < NUM_OBSTACLES; ++i)//
{
var obstacle : Polygon = new Polygon();
var oa : Number = 6.283185307179586 * Math.random();
var os : Number = OBSTACLE_SIZE_MIN + ((OBSTACLE_SIZE_MAX - OBSTACLE_SIZE_MIN) * Math.random());
var px : Number = SW * Math.random();
var oy : Number = SH * Math.random();
obstacle.v0 = new Vertex(px + os * Math.cos(oa), oy + os * Math.sin(oa));
oa += 1.570796326794897;
obstacle.v1 = new Vertex(px + os * Math.cos(oa), oy + os * Math.sin(oa));
oa += 1.570796326794897;
obstacle.v2 = new Vertex(px + os * Math.cos(oa), oy + os * Math.sin(oa));
oa += 1.570796326794897;
obstacle.v3 = new Vertex(px + os * Math.cos(oa), oy + os * Math.sin(oa));
oa += 1.570796326794897;
obstacle.v0.next = obstacle.v1;
obstacle.v1.prev = obstacle.v0;
obstacle.v1.next = obstacle.v2;
obstacle.v2.prev = obstacle.v1;
obstacle.v2.next = obstacle.v3;
obstacle.v3.prev = obstacle.v2;
obstacle.v3.next = obstacle.v0;
obstacle.v0.prev = obstacle.v3;
_obstacles.push(obstacle);
}
addChild(new Bitmap(_dst = new BitmapData(SW, SH, false)));
addEventListener(Event.ENTER_FRAME, oef);
}
final private function oef(e : Event) : void//
{
_fcnt++;
_dst.lock();
_dst.fillRect(_dst.rect, 0x0);
// ---------------------------------------------------------------------------------------- UI
if ((_fcnt & 31) == 1)//
{
_tx = SW * Math.random();
_ty = SH * Math.random();
}
_x += (_tx - _x) * 0.0044;
_y += (_ty - _y) * 0.0044;
var mx : int = _x;
var my : int = _y;
// ---------------------------------------------------------------------------------------- RENDER LIGHT
// light circle bounds
_th += 0.022;
var lightRadius : int = 222 + (88 * Math.sin(_th));
var cx : int = mx;
var cy : int = my;
var error : int = -lightRadius;
var px : int = lightRadius;
var py : int = 0;
while (px >= py)//
{
_dst.setPixel(cx + px, cy + py, 0x333333);
if (px) _dst.setPixel(cx - px, cy + py, 0x333333);
if (py) _dst.setPixel(cx + px, cy - py, 0x333333);
if (px && py) _dst.setPixel(cx - px, cy - py, 0x333333);
if (px != py)//
{
_dst.setPixel(cx + py, cy + px, 0x333333);
if (py) _dst.setPixel(cx - py, cy + px, 0x333333);
if (px) _dst.setPixel(cx + py, cy - px, 0x333333);
if (py && px) _dst.setPixel(cx - py, cy - px, 0x333333);
}
error += py;
++py;
error += py;
if (error >= 0)//
{
--px;
error -= px;
error -= px;
}
}
for (var i : uint = 0; i < NUM_OBSTACLES; ++i)//
{
var obstacle : Polygon = _obstacles[i];
dawBoxLine(_dst, obstacle.v0.x, obstacle.v0.y, obstacle.v1.x, obstacle.v1.y);
dawBoxLine(_dst, obstacle.v1.x, obstacle.v1.y, obstacle.v2.x, obstacle.v2.y);
dawBoxLine(_dst, obstacle.v2.x, obstacle.v2.y, obstacle.v3.x, obstacle.v3.y);
dawBoxLine(_dst, obstacle.v3.x, obstacle.v3.y, obstacle.v0.x, obstacle.v0.y);
processVertice(obstacle.v0, mx, my, lightRadius);
processVertice(obstacle.v1, mx, my, lightRadius);
processVertice(obstacle.v2, mx, my, lightRadius);
processVertice(obstacle.v3, mx, my, lightRadius);
}
// ---------------------------------------------------------------------------------------- RENDER LIGHT : FLOOD FILL
// non-recursive scanline flood fill algorithm + light color calculation
cx = mx;
cy = my;
_stackSize = 0;
var c0 : uint = _dst.getPixel(mx, my);
if (c0 == 0x808080) return;
var w : int = _dst.width - 1;
var h : int = _dst.height;
// pushStack(mx, my);
_stackX[_stackSize] = mx;
_stackY[_stackSize] = my;
++_stackSize;
//
while (_stackSize > 0)//
{
// pop stack
--_stackSize;
//
mx = _stackX[_stackSize];
my = _stackY[_stackSize];
while ((my) && (_dst.getPixel(mx, my) == c0))//
{
--my;
}
++my;
var spanLeft : int = 0;
var spanRight : int = 0;
while ((my < h) && (_dst.getPixel(mx, my) == c0))//
{
_dst.setPixel(mx, my, 0xFFFFFF);
// compute channel
var dx : int = mx - cx;
var dy : int = my - cy;
// light ray distance
var c : uint = ((1 - ((dx * dx + dy * dy) / (lightRadius * lightRadius)))) * 0xFF;
_dst.setPixel(mx, my, (0xFF << 24) | (c << 16) | (c << 8) | c);
var c1 : uint = _dst.getPixel(mx - 1, my);
if ((!spanLeft) && (mx) && (c1 == c0))//
{
_stackX[_stackSize] = mx - 1;
_stackY[_stackSize] = my;
++_stackSize;
//
spanLeft = 1;
}//
else if ((spanLeft) && (mx) && (c1 != c0))//
{
spanLeft = 0;
}
c1 = _dst.getPixel(mx + 1, my);
if ((!spanRight) && (mx < w) && (c1 == c0))//
{
_stackX[_stackSize] = mx + 1;
_stackY[_stackSize] = my;
++_stackSize;
//
spanRight = 1;
}//
else if ((spanRight) && (mx < w) && (c1 != c0))//
{
spanRight = 0;
}
++my;
}
}
// ---------------------------------------------------------------------------
_dst.unlock();
}
[Inline]
final private function processVertice(vertex : Vertex, mx : int, my : int, lightRadius : int) : void//
{
// get 3 adjacent vertices
var vprev : Vertex = vertex.prev;
var vnext : Vertex = vertex.next;
var dx0 : int = vertex.x - mx;
var dy0 : int = vertex.y - my;
// vertex is casting shadow <=> v0 cross v1 and v0 cross v2 has the same sign
if (((dx0 * (vprev.y - my)) - (dy0 * (vprev.x - mx))) * ((dx0 * (vnext.y - my)) - (dy0 * (vnext.x - mx))) >= 0)//
{
if (dy0 >= 0) {
if (dx0 >= 0.0) {
if (dy0 < 0.0) var atan2_dy0dx0 : Number = -(0.7853981633974483 - (0.7853981633974483 * ((dx0 - dy0) / (dx0 + dy0))));
else atan2_dy0dx0 = 0.7853981633974483 - (0.7853981633974483 * ((dx0 - dy0) / (dx0 + dy0)));
} else {
if (dy0 < 0.0) atan2_dy0dx0 = -(2.356194490192345 - (0.7853981633974483 * ((dx0 + dy0) / (dy0 - dx0))));
else atan2_dy0dx0 = 2.356194490192345 - (0.7853981633974483 * ((dx0 + dy0) / (dy0 - dx0)));
}
} else {
if (dx0 >= 0.0) {
if (dy0 < 0.0) atan2_dy0dx0 = -(0.7853981633974483 - (0.7853981633974483 * ((dx0 + dy0) / (dx0 - dy0))));
else atan2_dy0dx0 = 0.7853981633974483 - (0.7853981633974483 * ((dx0 + dy0) / (dx0 - dy0)));
} else {
if (dy0 < 0.0) atan2_dy0dx0 = -(2.356194490192345 - (0.7853981633974483 * ((dx0 - dy0) / (-dy0 - dx0))));
else atan2_dy0dx0 = 2.356194490192345 - (0.7853981633974483 * ((dx0 - dy0) / (-dy0 - dx0)));
}
}
// bresenham line
var x0 : int = vertex.x;
var y0 : int = vertex.y;
var x1 : int = vertex.x + (lightRadius * Math.cos(atan2_dy0dx0));
var y1 : int = vertex.y + (lightRadius * Math.sin(atan2_dy0dx0));
var dx : int = x1 - x0;
var dy : int = y1 - y0;
var sx : int = (dx >= 0) ? (1) : (-1);
var sy : int = (dy >= 0) ? (1) : ( -1);
if (dx < 0) dx = -dx;
if (dy < 0) dy = -dy;
var err : int = dx - dy;
while (1)//
{
_dst.setPixel(x0, y0, 0x202020);
if ((x0 == x1) && (y0 == y1)) break;
var e2 : int = err << 1;
if (e2 > -dy)//
{
err -= dy;
x0 += sx;
}
if (e2 < dx)//
{
err += dx;
y0 += sy;
}
}
}
}
private function dawBoxLine(dst : BitmapData, x0 : int, y0 : int, x1 : int, py1 : int) : void//
{
var dx : int = x1 - x0;
var dy : int = py1 - y0;
if (dx >= 0) var sx : int = 1;
else sx = -1;
if (dy >= 0) var sy : int = 1;
else sy = -1;
if (dx < 0) dx = -dx;
if (dy < 0) dy = -dy;
var err : int = dx - dy;
while (1)//
{
dst.setPixel(x0, y0, 0x404040);
if ((x0 == x1) && (y0 == py1)) break;
var e2 : int = err << 1;
if (e2 > -dy)//
{
err -= dy;
x0 += sx;
}
if (e2 < dx)//
{
err += dx;
y0 += sy;
}
}
}
}
}
internal class Vertex//
{
internal var prev : Vertex;
internal var next : Vertex;
internal var x : int;
internal var y : int;
function Vertex(x : Number, y : Number)//
{
this.x = x;
this.y = y;
}
}
internal class Polygon//
{
internal var v0 : Vertex;
internal var v1 : Vertex;
internal var v2 : Vertex;
internal var v3 : Vertex;
}