244 lines
7.7 KiB
Lua
244 lines
7.7 KiB
Lua
package.path = package.path..";"..RL.GetBasePath().."../resources/lib/?.lua"
|
|
|
|
Util = require( "utillib" )
|
|
Vector2 = require( "vector2" )
|
|
Vector3 = require( "vector3" )
|
|
Color = require( "color" )
|
|
|
|
-- Defines
|
|
local RESOLUTION = Vector2:new( 1280, 1024 )
|
|
local TILE_SIZE_PX = 32
|
|
local LIGHTRENDER_SIZE = 1024 -- Maxinum light size.
|
|
local SHADOW_FOV = 45 -- Camera fov for shadow rendering.
|
|
local WALL_MESH_HEIGHT = math.tan( RL.DEG2RAD * ( 90 - SHADOW_FOV / 2 ) ) * LIGHTRENDER_SIZE / TILE_SIZE_PX / 2
|
|
|
|
print( "WALL_MESH_HEIGHT", WALL_MESH_HEIGHT )
|
|
|
|
local monitor = 0
|
|
local monitorPos = Vector2:newT( RL.GetMonitorPosition( monitor ) )
|
|
local monitorSize = Vector2:newT( RL.GetMonitorSize( monitor ) )
|
|
local winScale = 1
|
|
local winSize = Vector2:new( RESOLUTION.x * winScale, RESOLUTION.y * winScale )
|
|
|
|
local tileTex = nil
|
|
local lightTex = nil
|
|
local lightTexSize = Vector2:new()
|
|
local framebuffer = nil
|
|
local lightMap = nil -- Final image of all lights.
|
|
local lightRender = nil -- RenderTexture for individual light and shadow rendering.
|
|
local ambientLight = Color:new( 40, 40, 40 )
|
|
local wallSegs = {}
|
|
local shadowMesh = nil
|
|
local lights = {}
|
|
local camera = nil -- 3D camera for shadow rendering.
|
|
|
|
-- Init.
|
|
|
|
local function addLight( pos, color, radius )
|
|
table.insert( lights, {
|
|
pos = pos,
|
|
color = color,
|
|
radius = radius
|
|
} )
|
|
end
|
|
|
|
local function addWallSeg( p1, p2 )
|
|
table.insert( wallSegs, {
|
|
p1 = p1,
|
|
p2 = p2
|
|
} )
|
|
end
|
|
|
|
-- We will create 3D "wall" mesh for the shadows.
|
|
local function createShadowMesh()
|
|
addWallSeg( Vector2:new( 0, 0 ), Vector2:new( 500, 160 ) )
|
|
addWallSeg( Vector2:new( 200, 230 ), Vector2:new( 400, 320 ) )
|
|
addWallSeg( Vector2:new( 600, 500 ), Vector2:new( 800, 360 ) )
|
|
addWallSeg( Vector2:new( 800, 360 ), Vector2:new( 900, 500 ) )
|
|
addWallSeg( Vector2:new( 500, 400 ), Vector2:new( 460, 600 ) )
|
|
addWallSeg( Vector2:new( 300, 600 ), Vector2:new( 360, 800 ) )
|
|
addWallSeg( Vector2:new( 360, 800 ), Vector2:new( 550, 900 ) )
|
|
addWallSeg( Vector2:new( 760, 600 ), Vector2:new( 900, 500 ) )
|
|
addWallSeg( Vector2:new( 960, 500 ), Vector2:new( 1200, 420 ) )
|
|
addWallSeg( Vector2:new( 840, 800 ), Vector2:new( 960, 950 ) )
|
|
|
|
-- Box.
|
|
addWallSeg( Vector2:new( 4 * 32, 15 * 32 ), Vector2:new( 5 * 32, 15 * 32 ) )
|
|
addWallSeg( Vector2:new( 4 * 32, 15 * 32 ), Vector2:new( 4 * 32, 16 * 32 ) )
|
|
addWallSeg( Vector2:new( 4 * 32, 16 * 32 ), Vector2:new( 5 * 32, 16 * 32 ) )
|
|
addWallSeg( Vector2:new( 5 * 32, 15 * 32 ), Vector2:new( 5 * 32, 16 * 32 ) )
|
|
|
|
-- Create wall mesh.
|
|
local meshData = { vertices = {} }
|
|
|
|
for _, s in ipairs( wallSegs ) do
|
|
local seg = {
|
|
p1 = Vector2:new( s.p1.x / TILE_SIZE_PX, s.p1.y / TILE_SIZE_PX ),
|
|
p2 = Vector2:new( s.p2.x / TILE_SIZE_PX, s.p2.y / TILE_SIZE_PX )
|
|
}
|
|
table.insert( meshData.vertices, Vector3:new( seg.p1.x, WALL_MESH_HEIGHT, seg.p1.y ) )
|
|
table.insert( meshData.vertices, Vector3:new( seg.p1.x, 0, seg.p1.y ) )
|
|
table.insert( meshData.vertices, Vector3:new( seg.p2.x, 0, seg.p2.y ) )
|
|
table.insert( meshData.vertices, Vector3:new( seg.p1.x, WALL_MESH_HEIGHT, seg.p1.y ) )
|
|
table.insert( meshData.vertices, Vector3:new( seg.p2.x, 0, seg.p2.y ) )
|
|
table.insert( meshData.vertices, Vector3:new( seg.p2.x, WALL_MESH_HEIGHT, seg.p2.y ) )
|
|
end
|
|
shadowMesh = RL.GenMeshCustom( meshData, false )
|
|
end
|
|
|
|
function RL.init()
|
|
RL.SetWindowState( RL.FLAG_WINDOW_RESIZABLE )
|
|
RL.SetWindowState( RL.FLAG_VSYNC_HINT )
|
|
RL.SetWindowSize( winSize )
|
|
RL.SetWindowPosition( { monitorPos.x + monitorSize.x / 2 - winSize.x / 2, monitorPos.y + monitorSize.y / 2 - winSize.y / 2 } )
|
|
RL.SetWindowTitle( "2D Lights" )
|
|
|
|
framebuffer = RL.LoadRenderTexture( RESOLUTION )
|
|
lightMap = RL.LoadRenderTexture( RESOLUTION )
|
|
lightRender = RL.LoadRenderTexture( { LIGHTRENDER_SIZE, LIGHTRENDER_SIZE } )
|
|
tileTex = RL.LoadTexture( RL.GetBasePath().."../resources/images/tile.png" )
|
|
lightTex = RL.LoadTexture( RL.GetBasePath().."../resources/images/light.png" )
|
|
lightTexSize = Vector2:newT( RL.GetTextureSize( lightTex ) )
|
|
|
|
RL.SetTextureFilter( tileTex, RL.TEXTURE_FILTER_TRILINEAR )
|
|
RL.SetTextureFilter( lightTex, RL.TEXTURE_FILTER_TRILINEAR )
|
|
|
|
createShadowMesh()
|
|
|
|
addLight( Vector2:new( 230, 480 ), Color:newT( RL.ORANGE ), 512 )
|
|
addLight( Vector2:new( 600, 200 ), Color:newT( RL.RED ), 512 )
|
|
addLight( Vector2:new( 384, 520 ), Color:newT( RL.GREEN ), 400 )
|
|
addLight( Vector2:new( 880, 750 ), Color:newT( RL.BLUE ), 300 )
|
|
addLight( Vector2:new( 800, 500 ), Color:newT( RL.PURPLE ), 512 )
|
|
addLight( Vector2:new( 200, 760 ), Color:newT( RL.WHITE ), 400 )
|
|
|
|
-- Stress test
|
|
|
|
-- for i = 1, 300 do
|
|
-- addLight( Vector2:new( math.random( 20, RESOLUTION.x - 20 ), math.random( 20, RESOLUTION.y - 20 ) ),
|
|
-- Color:new( { math.random( 40, 255 ), math.random( 40, 255 ), math.random( 40, 255 ), 255 } ),
|
|
-- 128
|
|
-- )
|
|
-- end
|
|
|
|
-- Camera for shadow rendering.
|
|
camera = RL.CreateCamera3D()
|
|
RL.SetCamera3DPosition( camera, { 0, 5, 0 } )
|
|
RL.SetCamera3DTarget( camera, { 0, 0, -0.001 } )
|
|
RL.SetCamera3DUp( camera, { 0, 1, 0 } )
|
|
RL.SetCamera3DFovy( camera, SHADOW_FOV )
|
|
|
|
RL.SetMouseScale( { 1 / winScale, 1 / winScale } )
|
|
-- Render both sides of the shadow. Could be usefull to set off to prevent self shadowing.
|
|
RL.rlDisableBackfaceCulling()
|
|
end
|
|
|
|
-- Update.
|
|
|
|
function RL.update( delta )
|
|
lights[1].pos:setT( RL.GetMousePosition() )
|
|
end
|
|
|
|
-- Drawing.
|
|
|
|
local function drawLight( light )
|
|
RL.BeginTextureMode( lightRender )
|
|
RL.ClearBackground( RL.BLANK )
|
|
RL.DrawTexturePro(
|
|
lightTex,
|
|
{ 0, 0, lightTexSize.x, lightTexSize.y },
|
|
{ LIGHTRENDER_SIZE / 2, LIGHTRENDER_SIZE / 2, light.radius * 2, light.radius * 2 },
|
|
{ light.radius, light.radius },
|
|
0.0,
|
|
light.color
|
|
)
|
|
-- Draw shadows.
|
|
local cameraPos = Vector2:new( light.pos.x / TILE_SIZE_PX, light.pos.y / TILE_SIZE_PX )
|
|
|
|
RL.SetCamera3DPosition( camera, { 0, WALL_MESH_HEIGHT, 0 } )
|
|
RL.SetCamera3DTarget( camera, { 0, 0, -0.0001 } )
|
|
|
|
RL.rlSetBlendFactors( RL.RL_ZERO, RL.RL_ONE_MINUS_SRC_COLOR, RL.RL_FUNC_ADD )
|
|
RL.BeginBlendMode( RL.BLEND_CUSTOM )
|
|
RL.BeginMode3D( camera )
|
|
RL.DrawMesh(
|
|
shadowMesh,
|
|
RL.GetMaterialDefault(),
|
|
RL.MatrixMultiply(
|
|
RL.MatrixTranslate( { -cameraPos.x, 0, -cameraPos.y } ),
|
|
RL.MatrixScale( { 1, 1, -1 } ) -- Flip mesh.
|
|
)
|
|
)
|
|
RL.EndMode3D()
|
|
RL.EndBlendMode()
|
|
RL.EndTextureMode()
|
|
-- Draw light to lightmap.
|
|
RL.BeginTextureMode( lightMap )
|
|
RL.BeginBlendMode( RL.BLEND_ADDITIVE )
|
|
RL.DrawTexturePro(
|
|
RL.GetRenderTextureTexture( lightRender ),
|
|
{ 0, 0, LIGHTRENDER_SIZE, LIGHTRENDER_SIZE },
|
|
{ light.pos.x, light.pos.y, LIGHTRENDER_SIZE, LIGHTRENDER_SIZE },
|
|
{ LIGHTRENDER_SIZE / 2, LIGHTRENDER_SIZE / 2 },
|
|
0.0,
|
|
light.color
|
|
)
|
|
RL.EndBlendMode()
|
|
RL.EndTextureMode()
|
|
end
|
|
|
|
local function drawLights()
|
|
RL.BeginTextureMode( lightMap )
|
|
RL.ClearBackground( ambientLight )
|
|
RL.EndTextureMode()
|
|
|
|
for _, light in ipairs( lights ) do
|
|
drawLight( light )
|
|
end
|
|
end
|
|
|
|
function RL.draw()
|
|
RL.ClearBackground( RL.BLACK )
|
|
|
|
drawLights()
|
|
-- Draw to framebuffer.
|
|
RL.BeginTextureMode( framebuffer )
|
|
RL.ClearBackground( RL.BLACK )
|
|
-- Floor.
|
|
RL.DrawTextureRec(
|
|
tileTex,
|
|
{ 0, 0, RESOLUTION.x, RESOLUTION.y },
|
|
{ 0, 0 },
|
|
RL.WHITE
|
|
)
|
|
-- Wall lines.
|
|
for _, seg in ipairs( wallSegs ) do
|
|
RL.DrawLine( seg.p1, seg.p2, 3.0, RL.BLACK )
|
|
end
|
|
-- Lightmap.
|
|
RL.rlSetBlendFactors( RL.RL_DST_COLOR, RL.RL_SRC_COLOR, RL.RL_FUNC_ADD )
|
|
RL.BeginBlendMode( RL.BLEND_CUSTOM )
|
|
RL.DrawTexturePro(
|
|
RL.GetRenderTextureTexture( lightMap ),
|
|
{ 0, 0, RESOLUTION.x, -RESOLUTION.y },
|
|
{ 0, 0, RESOLUTION.x, RESOLUTION.y },
|
|
{ 0, 0 },
|
|
0.0,
|
|
RL.WHITE
|
|
)
|
|
RL.EndBlendMode()
|
|
RL.EndTextureMode()
|
|
|
|
-- Draw framebuffer to window.
|
|
RL.DrawTexturePro(
|
|
RL.GetRenderTextureTexture( framebuffer ),
|
|
{ 0, 0, RESOLUTION.x, -RESOLUTION.y },
|
|
{ 0, 0, winSize.x, winSize.y },
|
|
{ 0, 0 },
|
|
0.0,
|
|
RL.WHITE
|
|
)
|
|
-- RL.DrawRectangle( { 0, 0, 96, 32 }, RL.BLACK )
|
|
-- RL.DrawFPS( { 10, 10 } )
|
|
end
|