mirror of
https://github.com/DramaticShape/DramaticShapeVoxelMod.git
synced 2026-08-12 10:00:50 +02:00
Added overworld battles
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
-- Overworld battles: the two mons, as geometry standing on the map.
|
||||
--
|
||||
-- Not pics composited over a picture of the world -- quads INSIDE it, drawn
|
||||
-- in the same 3D pass as the terrain, from the same camera, through the same
|
||||
-- shader. Which means they get everything the world gets and nothing has to
|
||||
-- be faked for them: the depth buffer decides what is in front of what, the
|
||||
-- sun pass sees them and throws their real silhouettes across the ground,
|
||||
-- and their size on screen is whatever standing on that tile at that
|
||||
-- distance actually looks like.
|
||||
--
|
||||
-- One quad, standing upright with its feet on the cell and yawed to face the
|
||||
-- camera. Upright rather than leaned back, unlike the free-roam mode's
|
||||
-- character cards: those lean because that camera looks DOWN and a standing
|
||||
-- card would foreshorten to nothing, and this one looks along the ground
|
||||
-- from about a foot above it, where a card standing up is simply correct.
|
||||
--
|
||||
-- The shader's alpha discard cuts the mon's exact outline out of the quad,
|
||||
-- so a Pokemon is its own silhouette against the world with no matte, no
|
||||
-- billboard edge and no sorting to get wrong.
|
||||
|
||||
-- the mod namespace (see main.lua): V.require loads a sibling module
|
||||
local V = ...
|
||||
|
||||
local Mat4 = V.require("Mat4")
|
||||
local Voxel3D = V.require("Voxel3D")
|
||||
|
||||
local BattleBillboard = {}
|
||||
|
||||
-- How wide a full-size (7x7 tile) mon stands, in world pixels. One overworld
|
||||
-- square, so a Pokemon covers the tile it is on and no more; a species whose
|
||||
-- pic is smaller than the full buffer comes out proportionally smaller,
|
||||
-- which is how the artwork's own size differences survive the trip.
|
||||
BattleBillboard.FULL_W = 16
|
||||
BattleBillboard.FULL_PIC = 56 -- the pic size FULL_W refers to
|
||||
|
||||
-- A hair of camera-ward bias, so a card standing ON the ground plane wins
|
||||
-- the depth test against it instead of z-fighting the tile it is rooted to.
|
||||
BattleBillboard.PULL = 1.5
|
||||
|
||||
local quad = nil -- nil = untried, false = unavailable
|
||||
|
||||
-- The unit card: x in -0.5..0.5, y in 0..1, z = 0, UV over the whole
|
||||
-- texture. Feet on the model origin, so the model matrix only has to say
|
||||
-- where the mon is standing and how big it is.
|
||||
local function unitQuad()
|
||||
if quad ~= nil then return quad or nil end
|
||||
local verts = {
|
||||
{ -0.5, 0, 0, 0, 1, 1 },
|
||||
{ 0.5, 0, 0, 1, 1, 1 },
|
||||
{ 0.5, 1, 0, 1, 0, 1 },
|
||||
{ -0.5, 1, 0, 0, 0, 1 },
|
||||
}
|
||||
local indices = {}
|
||||
Voxel3D.pushQuad(indices, 0)
|
||||
local mesh = Voxel3D.newMesh(verts, indices)
|
||||
quad = mesh or false
|
||||
return quad or nil
|
||||
end
|
||||
|
||||
BattleBillboard.mesh = unitQuad
|
||||
|
||||
-- The yaw that turns the card's face toward the eye. The quad's normal is
|
||||
-- +Z before rotation, so this is just the bearing from the mon to the
|
||||
-- camera -- flattened to the horizontal, because a card that also tipped to
|
||||
-- face a camera above it would lift its feet off the floor.
|
||||
function BattleBillboard.yawToward(x, z, eye)
|
||||
if not eye then return 0 end
|
||||
return math.atan2(eye[1] - x, eye[3] - z)
|
||||
end
|
||||
|
||||
-- Stand a `w` x `h` card with its feet centred on world (x, y, z).
|
||||
function BattleBillboard.matrix(x, y, z, w, h, yaw)
|
||||
return Mat4.mul(Mat4.mul(Mat4.translate(x, y, z), Mat4.rotateY(yaw)),
|
||||
Mat4.scale(w, h, 1))
|
||||
end
|
||||
|
||||
-- The world size a pic of `pw` x `ph` texture pixels stands at.
|
||||
function BattleBillboard.sizeFor(pw, ph)
|
||||
if not (pw and ph and pw > 0 and ph > 0) then return 0, 0 end
|
||||
local scale = BattleBillboard.FULL_W / BattleBillboard.FULL_PIC
|
||||
return pw * scale, ph * scale
|
||||
end
|
||||
|
||||
-- Draw one mon. `tex` is the pic already rendered to a texture (see
|
||||
-- OverworldBattle, which lets the engine's own battler draw produce it, so
|
||||
-- every faint slide, blink and squish comes along), `grow` the send-out
|
||||
-- animation's scale or nil.
|
||||
function BattleBillboard.draw(tex, x, y, z, grow)
|
||||
local mesh = unitQuad()
|
||||
if not (mesh and tex) then return false end
|
||||
local pw, ph = tex:getDimensions()
|
||||
local w, h = BattleBillboard.sizeFor(pw, ph)
|
||||
if grow then w, h = w * grow, h * grow end
|
||||
if w <= 0 or h <= 0 then return false end
|
||||
local yaw = BattleBillboard.yawToward(x, z, Voxel3D.eye)
|
||||
Voxel3D.draw(mesh, tex, BattleBillboard.matrix(x, y, z, w, h, yaw),
|
||||
BattleBillboard.PULL)
|
||||
return true
|
||||
end
|
||||
|
||||
-- The same card, as the SUN sees it: no camera-ward pull (that is a trick
|
||||
-- for the view's own depth buffer and would drag the shadow off its owner)
|
||||
-- and no draw call of its own, because the shadow pass has its own.
|
||||
function BattleBillboard.caster(shadowMap, tex, x, y, z, grow)
|
||||
local mesh = unitQuad()
|
||||
if not (mesh and tex) then return false end
|
||||
local pw, ph = tex:getDimensions()
|
||||
local w, h = BattleBillboard.sizeFor(pw, ph)
|
||||
if grow then w, h = w * grow, h * grow end
|
||||
if w <= 0 or h <= 0 then return false end
|
||||
local yaw = BattleBillboard.yawToward(x, z, Voxel3D.eye)
|
||||
shadowMap.draw(mesh, tex, BattleBillboard.matrix(x, y, z, w, h, yaw))
|
||||
return true
|
||||
end
|
||||
|
||||
function BattleBillboard.invalidate()
|
||||
quad = nil
|
||||
end
|
||||
|
||||
return BattleBillboard
|
||||
Reference in New Issue
Block a user