Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

recreate the old effect using shaders #317

Merged
merged 2 commits into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Effects/DynamicShadows/source/Main.hx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class Main extends Sprite
public function new()
{
super();
addChild(new FlxGame(640, 320, PlayState));

addChild(new FlxGame(640, 320, #if flash PlayStateFlash #else PlayStateShader #end));
}
}
146 changes: 26 additions & 120 deletions Effects/DynamicShadows/source/PlayState.hx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package;

import flixel.FlxCamera;
import flixel.addons.nape.FlxNapeSpace;
import flixel.addons.nape.FlxNapeTilemap;
import flixel.FlxG;
Expand All @@ -19,7 +20,7 @@ using flixel.util.FlxSpriteUtil;

/**
* This was based on a guide from this forum post: http://forums.tigsource.com/index.php?topic=8803.0
* Ported to HaxeFlixel by Xerosugar
* Ported to HaxeFlixel by Xerosugar, then converted to use Shaders by GeoKureli
*
* If you're feeling up the challenge, here's how YOU help can improve this demo:
* - Make it possible to extends the shadows to the edge of the screen
Expand All @@ -35,9 +36,6 @@ class PlayState extends FlxState
{
public static inline var TILE_SIZE:Int = 16;

static inline var SHADOW_COLOR = 0xff2a2963;
static inline var OVERLAY_COLOR = 0xff887fff;

/**
* Only contains non-collidabe tiles
*/
Expand All @@ -47,76 +45,62 @@ class PlayState extends FlxState
* The layer into which the actual "level" will be drawn, and also the one objects will collide with
*/
var foreground:FlxNapeTilemap;

/**
* The sprite that shadows will be drawn to
* Anything you want to show above the shadows
*/
var shadowCanvas:FlxSprite;

/**
* The sprite that the actual darkness and the gem's flare-like effect will be drawn to
*/
var shadowOverlay:FlxSprite;

var uiCam:FlxCamera;

/**
* The light source!
*/
var gem:Gem;

/**
* If there's a small gap between something (could be two tiles,
* even if they're right next to each other), this should cover it up for us
*/
var lineStyle:LineStyle = {color: SHADOW_COLOR, thickness: 1};

var infoText:FlxText;
var fps:FPS;

override public function create():Void
{
super.create();

FlxG.camera.bgColor = 0x5a81ad;

FlxNapeSpace.init();
FlxNapeSpace.space.gravity.setxy(0, 1200);
FlxNapeSpace.drawDebug = false; // You can toggle this on/off one by pressing 'D'

var background:FlxTilemap = new FlxTilemap();
background = new FlxTilemap();
background.loadMapFromCSV("assets/data/background.txt", "assets/images/tiles.png", TILE_SIZE, TILE_SIZE, null, 1, 1);
add(background);

// Note: The tilemap used in this demo was drawn with 'Tiled' (http://www.mapeditor.org/),
// but the level data was picked from the .tmx file and put into two separate
// .txt files for simplicity. If you wish to learn how to use Tiled with your project,
// have a look at this demo: http://haxeflixel.com/demos/TiledEditor/

// If we add the shadows *before* all of the foreground elements (stage included)
// they will only cover the background, which is usually what you'd want I'd guess :)
shadowCanvas = new FlxSprite();
shadowCanvas.blend = BlendMode.MULTIPLY;
shadowCanvas.makeGraphic(FlxG.width, FlxG.height, FlxColor.TRANSPARENT, true);
add(shadowCanvas);

foreground = new FlxNapeTilemap();
foreground.loadMapFromCSV("assets/data/foreground.txt", "assets/images/tiles.png", TILE_SIZE, TILE_SIZE, null, 1, 1);
add(foreground);

foreground.setupTileIndices([4]);
createProps();

shadowOverlay = new FlxSprite();
shadowOverlay.makeGraphic(FlxG.width, FlxG.height, FlxColor.TRANSPARENT, true);
shadowOverlay.blend = BlendMode.MULTIPLY;
add(shadowOverlay);

infoText = new FlxText(10, 10, 100, "");
add(infoText);

// This here is only used to get the current FPS in a simple way, without having to run the application in Debug mode
fps = new FPS(10, 10, 0xffffff);
FlxG.stage.addChild(fps);
fps.visible = false;

createCams();
}

function createCams()
{
// FlxG.camera draws the actual world. In this case, that means everything except infoText
FlxG.camera.bgColor = 0x5a81ad;
gem.camera = FlxG.camera;
background.camera = FlxG.camera;

// draws anything above the sahdows, in this case infoText
uiCam = new FlxCamera(0, 0, FlxG.width, FlxG.height);
FlxG.cameras.add(uiCam, false);
uiCam.bgColor = 0x0;
infoText.camera = uiCam;
}

function createProps():Void
Expand Down Expand Up @@ -154,97 +138,19 @@ class PlayState extends FlxState

override public function update(elapsed:Float):Void
{
super.update(elapsed);

infoText.text = "FPS: " + fps.currentFPS + "\n\nObjects can be dragged/thrown around.\n\nPress 'R' to restart.";

if (FlxG.keys.justPressed.R)
FlxG.resetState();

if (FlxG.keys.justPressed.D)
FlxNapeSpace.drawDebug = !FlxNapeSpace.drawDebug;

processShadows();
super.update(elapsed);
}

public function processShadows():Void
{
shadowCanvas.fill(FlxColor.TRANSPARENT);
shadowOverlay.fill(OVERLAY_COLOR);

shadowOverlay.drawCircle( // outer red circle
gem.body.position.x + FlxG.random.float(-.6, .6), gem.body.position.y + FlxG.random.float(-.6, .6),
(FlxG.random.bool(5) ? 16 : 16.5), 0xffff5f5f);

shadowOverlay.drawCircle( // inner red circle
gem.body.position.x + FlxG.random.float(-.25, .25), gem.body.position.y + FlxG.random.float(-.25, .25),
(FlxG.random.bool(5) ? 13 : 13.5), 0xffff7070);

for (body in FlxNapeSpace.space.bodies)
{
// We don't want to draw any shadows around the gem, since it's the light source
if (body.userData.type != "Gem")
processBodyShapes(body);
}
}

function processBodyShapes(body:Body)
{
for (shape in body.shapes)
{
var verts:Vec2List = shape.castPolygon.worldVerts;

for (i in 0...verts.length)
{
var startVertex:Vec2 = (i == 0) ? verts.at(verts.length - 1) : verts.at(i - 1);
processShapeVertex(startVertex, verts.at(i));
}
}
}

function processShapeVertex(startVertex:Vec2, endVertex:Vec2):Void
{
var tempLightOrigin:Vec2 = Vec2.get(gem.body.position.x + FlxG.random.float(-.3, 3), gem.body.position.y + FlxG.random.float(-.3, .3));

if (doesEdgeCastShadow(startVertex, endVertex, tempLightOrigin))
{
var projectedPoint:Vec2 = projectPoint(startVertex, tempLightOrigin);
var prevProjectedPt:Vec2 = projectPoint(endVertex, tempLightOrigin);
var vts:Array<FlxPoint> = [
FlxPoint.weak(startVertex.x, startVertex.y),
FlxPoint.weak(projectedPoint.x, projectedPoint.y),
FlxPoint.weak(prevProjectedPt.x, prevProjectedPt.y),
FlxPoint.weak(endVertex.x, endVertex.y)
];

shadowCanvas.drawPolygon(vts, SHADOW_COLOR, lineStyle);
}
}

function projectPoint(point:Vec2, light:Vec2):Vec2
{
var lightToPoint:Vec2 = point.copy();
lightToPoint.subeq(light);

var projectedPoint:Vec2 = point.copy();
return projectedPoint.addeq(lightToPoint.muleq(.45));
}

function doesEdgeCastShadow(start:Vec2, end:Vec2, light:Vec2):Bool
{
var startToEnd:Vec2 = end.copy();
startToEnd.subeq(start);

var normal:Vec2 = new Vec2(startToEnd.y, -1 * startToEnd.x);

var lightToStart:Vec2 = start.copy();
lightToStart.subeq(light);

return normal.dot(lightToStart) > 0;
}
}

@:enum
abstract Prop(Int) to Int
enum abstract Prop(Int) to Int
{
var BARREL = 5;
var GEM = 6;
Expand Down
Loading