feat(actor3D): cast shadow below the actor.
This commit is contained in:
parent
c043bb8ecf
commit
a3b0f47127
4 changed files with 177 additions and 2 deletions
|
@ -27,7 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- **camera:** Add two new camera types: "middle" and "zoom".
|
||||
|
||||
- **world:** Add a fake 3D world, à la Zelda or BeatThemAll
|
||||
- **world:** Add a fake 3D world, à la Zelda or BeatThemAll, complete with shadow support
|
||||
|
||||
### Changed
|
||||
|
||||
|
|
|
@ -37,9 +37,14 @@ function Actor3D:new(world, type, x, y, z, w, h, d, isSolid)
|
|||
self:initHitboxes()
|
||||
self.world:registerShape(self)
|
||||
self.boxes = Boxes
|
||||
self.doCastShadows = true
|
||||
end
|
||||
|
||||
function Actor3D:destroy()
|
||||
self:removeOldShadowTargets()
|
||||
if self.box ~= nil then
|
||||
self.world:removeTerrain(self)
|
||||
end
|
||||
self.world:removeActor(self)
|
||||
self.mainHitbox:destroy()
|
||||
self.world:removeShape(self)
|
||||
|
@ -87,11 +92,19 @@ end
|
|||
|
||||
function Actor3D:move(dx, dy, dz)
|
||||
local cols, colNumber = {}, 0
|
||||
local oldx, oldy, oldz = self.x, self.y, self.z
|
||||
if (self.isDestroyed == false) then
|
||||
self.x, self.y, self.z, cols, colNumber = self.mainHitbox:checkCollision(dx, dy, dz, self.filter)
|
||||
self.mainHitbox:updatePosition()
|
||||
self.world:updateShape(self)
|
||||
end
|
||||
|
||||
if (oldx ~= self.x) or (oldy ~= self.y) or (oldz ~= self.z) or (self.shadowTargetsPrevious == nil) then
|
||||
if (self.doCastShadows) then
|
||||
self:castShadow()
|
||||
end
|
||||
end
|
||||
|
||||
return self.x, self.y, self.z, cols, colNumber
|
||||
end
|
||||
|
||||
|
@ -201,9 +214,71 @@ function Actor3D:checkHitboxCollisionsAtPoint(name, dx, dy, dz, filter)
|
|||
return x, y, z, cols, colNumber
|
||||
end
|
||||
|
||||
-- SHADOW FUNCTIONS
|
||||
-- Handle everything related to shadow
|
||||
|
||||
function Actor3D:castShadow()
|
||||
local shadowTargets = self.world:getTerrainInRect(self.x, self.y, self.w, self.d)
|
||||
-- initialize the shadowTargetsPrevious variable if it doesn't exist
|
||||
if (self.shadowTargetsPrevious == nil) then
|
||||
self.shadowTargetsPrevious = {}
|
||||
end
|
||||
|
||||
for i, target in ipairs(shadowTargets) do
|
||||
-- We test if the actor is below the current actor
|
||||
if (target ~= self) and (target.box ~= nil) then
|
||||
|
||||
if (target.z + target.d <= self.z + self.d) then
|
||||
-- Remove the target of the list of item targeted last update,
|
||||
-- in order to only have object no longer shadowed after the
|
||||
-- end of the loop
|
||||
for j, oldtarget in ipairs(self.shadowTargetsPrevious) do
|
||||
if (target == oldtarget) then
|
||||
table.remove(self.shadowTargetsPrevious, j)
|
||||
end
|
||||
end
|
||||
|
||||
-- We update the shadow source
|
||||
local x, y = math.floor(self.x - target.x), math.floor(self.y - target.y)
|
||||
target.box:setShadowSource(self, x, y)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- At this point, if a target is still in the shadowTargetsPrevious list,
|
||||
-- it mean that it's not shadowed. So we can simply remove the shadow.
|
||||
self:removeOldShadowTargets()
|
||||
|
||||
self.shadowTargetsPrevious = shadowTargets
|
||||
end
|
||||
|
||||
function Actor3D:removeOldShadowTargets()
|
||||
if (self.shadowTargetsPrevious ~= nil) then
|
||||
for i, target in ipairs(self.shadowTargetsPrevious) do
|
||||
if (target.box ~= nil) then
|
||||
target.box:removeShadowSource(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Actor3D:redrawShadowCanvas()
|
||||
if (self.box ~= nil) then
|
||||
self.box:redrawShadowCanvas()
|
||||
end
|
||||
end
|
||||
|
||||
-- DRAW FUNCTIONS
|
||||
-- Draw the actors.
|
||||
|
||||
function Actor3D:drawShadow(x, y)
|
||||
love.graphics.setColor(0, 0, 0, 1)
|
||||
love.graphics.rectangle("fill", x, y, self.w, self.h)
|
||||
utils.graphics.resetColor()
|
||||
end
|
||||
|
||||
function Actor3D:getShape()
|
||||
return (self.x), (self.y - self.z - self.d), self.w, (self.h + self.d)
|
||||
end
|
||||
|
|
|
@ -25,6 +25,8 @@ local Box3D = Object:extend()
|
|||
|
||||
function Box3D:new(owner, w, h, d)
|
||||
self.owner = owner
|
||||
self.world = owner.world
|
||||
self.cameras = self.world.cameras
|
||||
|
||||
self.w = w
|
||||
self.h = h
|
||||
|
@ -32,15 +34,20 @@ function Box3D:new(owner, w, h, d)
|
|||
|
||||
self.haveLine = true
|
||||
|
||||
self.shadowSources = {}
|
||||
self.needRedraw = false
|
||||
|
||||
self.texture = {}
|
||||
self:setTopTexture()
|
||||
self:setBottomTexture()
|
||||
self.texture.shadows = nil
|
||||
|
||||
self:register()
|
||||
end
|
||||
|
||||
function Box3D:register()
|
||||
self.owner.box = self
|
||||
self.world:registerTerrain(self.owner)
|
||||
end
|
||||
|
||||
function Box3D:setSizeFromOwner()
|
||||
|
@ -80,6 +87,60 @@ function Box3D:setBottomTexture()
|
|||
canvas:release()
|
||||
end
|
||||
|
||||
function Box3D:setShadowSource(actor, x, y)
|
||||
local foundShadow = false
|
||||
|
||||
for i,v in ipairs(self.shadowSources) do
|
||||
if (v.actor == actor) then
|
||||
if (v.x ~= x) or (v.y ~= y) then
|
||||
v.x = x
|
||||
v.y = y
|
||||
self.needRedraw = true
|
||||
end
|
||||
foundShadow = true
|
||||
end
|
||||
end
|
||||
|
||||
if (foundShadow == false) then
|
||||
local shadow = {}
|
||||
shadow.actor = actor
|
||||
shadow.x = x
|
||||
shadow.y = y
|
||||
self.needRedraw = true
|
||||
|
||||
table.insert(self.shadowSources, shadow)
|
||||
end
|
||||
end
|
||||
|
||||
function Box3D:removeShadowSource(actor)
|
||||
for i,v in ipairs(self.shadowSources) do
|
||||
if (v.actor == actor) then
|
||||
table.remove(self.shadowSources, i)
|
||||
self.needRedraw = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Box3D:redrawShadowCanvas()
|
||||
if (self.needRedraw) then
|
||||
print("DEBUG: Generating " .. #self.shadowSources .. " shadows")
|
||||
local canvas = love.graphics.newCanvas(self.w, self.h)
|
||||
love.graphics.setCanvas( canvas )
|
||||
for i,v in ipairs(self.shadowSources) do
|
||||
v.actor:drawShadow(v.x, v.y)
|
||||
end
|
||||
|
||||
love.graphics.setCanvas( )
|
||||
|
||||
local imagedata = canvas:newImageData()
|
||||
self.texture.shadows = love.graphics.newImage( imagedata )
|
||||
imagedata:release()
|
||||
canvas:release()
|
||||
|
||||
self.needRedraw = false
|
||||
end
|
||||
end
|
||||
|
||||
function Box3D:draw(x, y, z)
|
||||
love.graphics.setColor(0, 0, 0, 1)
|
||||
if (self.haveLine) then
|
||||
|
@ -88,6 +149,10 @@ function Box3D:draw(x, y, z)
|
|||
utils.graphics.resetColor()
|
||||
love.graphics.draw(self.texture.top, x, (y-z) - (self.d))
|
||||
love.graphics.draw(self.texture.bottom, x, (y-z) - (self.d) + (self.h))
|
||||
if (self.texture.shadows ~= nil) and (#self.shadowSources > 0) then
|
||||
print("DEBUG: Drawing " .. #self.shadowSources .. " shadow textures")
|
||||
love.graphics.draw(self.texture.shadows, x, (y-z) - (self.d))
|
||||
end
|
||||
end
|
||||
|
||||
return Box3D
|
||||
|
|
|
@ -46,6 +46,7 @@ function World3D:initActors()
|
|||
self.actors = {}
|
||||
self.bodies = Bump3D.newWorld(50)
|
||||
self:initShapes()
|
||||
self:initTerrain()
|
||||
end
|
||||
|
||||
function World3D:newActor(name, x, y, z)
|
||||
|
@ -161,6 +162,17 @@ function World3D:getBodiesInRect(x, y, w, h)
|
|||
return {} --self.bodies:queryRect(x, y, w, h)
|
||||
end
|
||||
|
||||
-- UPDATE
|
||||
-- Update everything in the world
|
||||
|
||||
function World3D:updateActors(dt)
|
||||
World3D.super.updateActors(self, dt)
|
||||
local actors = self:getActors()
|
||||
for i,v in ipairs(actors) do
|
||||
v:redrawShadowCanvas()
|
||||
end
|
||||
end
|
||||
|
||||
-- SHAPE SYSTEM
|
||||
-- Handle onscreen shapes
|
||||
|
||||
|
@ -190,6 +202,29 @@ function World3D:getShapeInRect(x, y, w, h)
|
|||
return self.shapes:queryRect(x, y, w, h)
|
||||
end
|
||||
|
||||
-- TERRAIN SYSTEM
|
||||
-- Handle onscreen shapes
|
||||
|
||||
function World3D:initTerrain()
|
||||
self.terrains = Bump.newWorld(50)
|
||||
end
|
||||
|
||||
function World3D:registerTerrain(actor)
|
||||
return self.terrains:add(actor, actor.x, actor.y, actor.w, actor.h)
|
||||
end
|
||||
|
||||
function World3D:updateTerrain(actor)
|
||||
return self.terrains:update(actor, actor.x, actor.y, actor.w, actor.h)
|
||||
end
|
||||
|
||||
function World3D:removeTerrain(actor)
|
||||
return self.terrains:remove(actor)
|
||||
end
|
||||
|
||||
function World3D:getTerrainInRect(x, y, w, h)
|
||||
return self.terrains:queryRect(x, y, w, h)
|
||||
end
|
||||
|
||||
-- DRAW FUNCTIONS
|
||||
-- Functions to draw the world
|
||||
|
||||
|
|
Loading…
Reference in a new issue