Initial 3D blocks support

This commit is contained in:
nightmareci
2022-07-04 21:05:40 -07:00
parent e5892c0fae
commit dcde2a380d
24 changed files with 6202 additions and 44 deletions

View File

@@ -0,0 +1,300 @@
local mat4 = cpml.mat4
local vec3 = cpml.vec3
local cube = {
front = {},
top = {},
bottom = {},
left = {},
right = {}
}
for scale = 1,2 do
cube.front[scale] = love.graphics.newMesh(
{
{ "VertexPosition", "float", 3 },
{ "VertexTexCoord", "float", 2 },
{ "VertexColor", "byte", 4}
},
{
{
0, 0, 0,
0, 0,
1, 1, 1
},
{
16*scale, 0, 0,
1, 0,
1, 1, 1,
},
{
0, 16*scale, 0,
0, 1,
1, 1, 1,
},
{
16*scale, 16*scale, 0,
1, 1,
1, 1, 1,
},
},
"strip",
"static"
)
cube.top[scale] = love.graphics.newMesh(
{
{ "VertexPosition", "float", 3 },
{ "VertexTexCoord", "float", 2 },
{ "VertexColor", "byte", 4}
},
{
{
0, 0, 0,
0, 0,
1, 1, 1
},
{
16*scale, 0, 0,
1, 0,
1, 1, 1,
},
{
0, 0, 16*scale,
0, 1,
1, 1, 1,
},
{
16*scale, 0, 16*scale,
1, 1,
1, 1, 1,
},
},
"strip",
"static"
)
cube.bottom[scale] = love.graphics.newMesh(
{
{ "VertexPosition", "float", 3 },
{ "VertexTexCoord", "float", 2 },
{ "VertexColor", "byte", 4}
},
{
{
0, 16*scale, 16*scale,
0, 0,
1, 1, 1
},
{
16*scale, 16*scale, 16*scale,
1, 0,
1, 1, 1,
},
{
0, 16*scale, 0,
0, 1,
1, 1, 1,
},
{
16*scale, 16*scale, 0,
1, 1,
1, 1, 1,
},
},
"strip",
"static"
)
cube.left[scale] = love.graphics.newMesh(
{
{ "VertexPosition", "float", 3 },
{ "VertexTexCoord", "float", 2 },
{ "VertexColor", "byte", 4}
},
{
{
0, 0, 0,
0, 0,
1, 1, 1
},
{
0, 16*scale, 0,
1, 0,
1, 1, 1,
},
{
0, 0, 16*scale,
0, 1,
1, 1, 1,
},
{
0, 16*scale, 16*scale,
1, 1,
1, 1, 1,
},
},
"strip",
"static"
)
cube.right[scale] = love.graphics.newMesh(
{
{ "VertexPosition", "float", 3 },
{ "VertexTexCoord", "float", 2 },
{ "VertexColor", "byte", 4}
},
{
{
16*scale, 16*scale, 16*scale,
0, 0,
1, 1, 1
},
{
16*scale, 0, 16*scale,
1, 0,
1, 1, 1,
},
{
16*scale, 16*scale, 0,
0, 1,
1, 1, 1,
},
{
16*scale, 0, 0,
1, 1,
1, 1, 1,
},
},
"strip",
"static"
)
end
local shader_no_discard = {
shader = love.graphics.newShader(
[[
vec4 effect(vec4 color, Image tex, vec2 texture_coords, vec2 screen_coords) {
vec4 texcolor = Texel(tex, texture_coords);
return texcolor * color;
}
]],
[[
uniform mat4 transform;
vec4 position(mat4 transform_projection, vec4 vertex_position) {
return transform * TransformMatrix * (vertex_position - vec4(vec2(640.0, 480.0) / 2.0, 0.0, 0.0));
}
]]
),
width = -1,
height = -1,
depth_3d = -1
}
local shader_discard = {
shader = love.graphics.newShader(
[[
uniform vec4 rect;
vec4 effect(vec4 color, Image tex, vec2 texture_coords, vec2 screen_coords) {
if (screen_coords.x < rect.x || screen_coords.x > rect.y || screen_coords.y < rect.z || screen_coords.y > rect.w) discard;
vec4 texcolor = Texel(tex, texture_coords);
return texcolor * color;
}
]],
[[
uniform mat4 transform;
vec4 position(mat4 transform_projection, vec4 vertex_position) {
return transform * TransformMatrix * (vertex_position - vec4(vec2(640.0, 480.0) / 2.0, 0.0, 0.0));
}
]]
),
width = -1,
height = -1,
depth_3d = -1
}
local function set_shader_transform(shader, width, height)
local depth = (480 / 2) / math.tan(math.rad(config.depth_3d) / 2)
local look_at = mat4():look_at(vec3(0, 0, depth), vec3(0, 0, 0), vec3(0, 1, 0))
local perspective = mat4.from_perspective(config.depth_3d, width / height, -0.5, 0.5)
local tall = height / 480 > width / 640
if tall then
local scale_factor = (480 * width) / (640 * height)
shader:send("transform", mat4.scale(mat4.new(), perspective * look_at, vec3(scale_factor)))
else
shader:send("transform", perspective * look_at)
end
end
return function(R, G, B, A, image, x, y, left, right, top, bottom, big)
local scale = big and 2 or 1
if config.depth_3d > 0 then
love.graphics.setCanvas{GLOBAL_CANVAS, depth = true}
love.graphics.push()
love.graphics.origin()
love.graphics.setDepthMode("lequal", true)
local shader
local current_width = love.graphics.getWidth()
local current_height = love.graphics.getHeight()
if left and right and top and bottom then
love.graphics.setShader(shader_discard.shader)
local scale_factor = math.min(current_width / 640, current_height / 480)
local rect_left = (current_width - scale_factor * 640) / 2 + left * scale_factor
local rect_right = (current_width - scale_factor * 640) / 2 + right * scale_factor
local rect_top = (current_height - scale_factor * 480) / 2 + top * scale_factor
local rect_bottom = (current_height - scale_factor * 480) / 2 + bottom * scale_factor
shader_discard.shader:send("rect", {rect_left, rect_right, rect_top, rect_bottom})
shader = shader_discard
else
love.graphics.setShader(shader_no_discard.shader)
shader = shader_no_discard
end
if current_width ~= shader.width or current_height ~= shader.height or config.depth_3d ~= shader.depth_3d then
set_shader_transform(shader.shader, current_width, current_height)
shader.width = current_width
shader.height = current_height
shader.depth_3d = config.depth_3d
end
if A ~= 1 or y > 480 / 2 then
love.graphics.setColor(R, G, B, A)
cube.top[scale]:setTexture(image)
love.graphics.draw(cube.top[scale], x, y)
end
love.graphics.setColor(R / 2, G / 2, B / 2, A)
if A ~= 1 or y < 480 / 2 then
cube.bottom[scale]:setTexture(image)
love.graphics.draw(cube.bottom[scale], x, y)
end
if A ~= 1 or x > 640 / 2 then
cube.left[scale]:setTexture(image)
love.graphics.draw(cube.left[scale], x, y)
end
if A ~= 1 or x < 640 / 2 then
cube.right[scale]:setTexture(image)
love.graphics.draw(cube.right[scale], x, y)
end
love.graphics.setColor(R, G, B, A)
cube.front[scale]:setTexture(image)
love.graphics.draw(cube.front[scale], x, y)
love.graphics.setShader()
love.graphics.setDepthMode()
love.graphics.pop()
love.graphics.setCanvas(GLOBAL_CANVAS)
else
love.graphics.setColor(R, G, B, A)
love.graphics.draw(image, x, y, 0, scale, scale)
end
end

View File

@@ -2,6 +2,7 @@ local Object = require 'libs.classic'
local Grid = Object:extend()
local drawBlock = require 'tetris.components.draw_block'
local empty = { skin = "", colour = "" }
local oob = { skin = "", colour = "" }
local block = { skin = "2tie", colour = "A" }
@@ -405,23 +406,47 @@ function Grid:update()
end
function Grid:draw()
is_3d = is_3d == nil and false or is_3d
for y = 5, self.height do
for x = 1, self.width do
if blocks[self.grid[y][x].skin] and
blocks[self.grid[y][x].skin][self.grid[y][x].colour] then
if self.grid_age[y][x] < 2 then
love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(blocks[self.grid[y][x].skin]["F"], 48+x*16, y*16)
drawBlock(1, 1, 1, 1, blocks[self.grid[y][x].skin]["F"], 48+x*16, y*16,
48, 48+(self.width+1)*16,
5*16, (self.height+1)*16
)
else
local R, G, B, A
if self.grid[y][x].colour == "X" then
love.graphics.setColor(0, 0, 0, 0)
R = 0
G = 0
B = 0
A = 0
elseif self.grid[y][x].skin == "bone" then
love.graphics.setColor(1, 1, 1, 1)
r = 1
G = 1
B = 1
A = 1
else
love.graphics.setColor(0.5, 0.5, 0.5, 1)
R = 0.5
G = 0.5
B = 0.5
A = 1
end
love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16)
drawBlock(R, G, B, A, blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16,
48, 48+(self.width+1)*16,
5*16, (self.height+1)*16
)
end
end
end
end
-- everything that needs to be drawn over the blocks *must* be drawn after all blocks have been drawn, due to how 3D works
for y = 5, self.height do
for x = 1, self.width do
if blocks[self.grid[y][x].skin] and
blocks[self.grid[y][x].skin][self.grid[y][x].colour] then
if self.grid[y][x].skin ~= "bone" and self.grid[y][x].colour ~= "X" then
love.graphics.setColor(0.8, 0.8, 0.8, 1)
love.graphics.setLineWidth(1)
@@ -468,9 +493,10 @@ function Grid:drawOutline()
end
end
function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_flash, brightness)
function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_flash, brightness, is_3d)
lock_flash = lock_flash == nil and true or lock_flash
brightness = brightness == nil and 0.5 or brightness
is_3d = is_3d == nil and false or is_3d
for y = 5, self.height do
for x = 1, self.width do
if self.grid[y][x] ~= empty then
@@ -481,8 +507,17 @@ function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_fla
else
opacity = opacity_function(self.grid_age[y][x])
end
love.graphics.setColor(brightness, brightness, brightness, opacity)
love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16)
drawBlock(brightness, brightness, brightness, opacity, blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16,
48, 48+(self.width+1)*16,
5*16, (self.height+1)*16
)
end
end
end
-- everything that needs to be drawn over the blocks *must* be drawn after all blocks have been drawn, due to how 3D works
for y = 5, self.height do
for x = 1, self.width do
if self.grid[y][x] ~= empty then
if lock_flash then
if opacity > 0 and self.grid[y][x].colour ~= "X" then
love.graphics.setColor(0.64, 0.64, 0.64)
@@ -507,29 +542,40 @@ function Grid:drawInvisible(opacity_function, garbage_opacity_function, lock_fla
end
end
function Grid:drawCustom(colour_function, gamestate)
--[[
colour_function: (game, block, x, y, age) -> (R, G, B, A, outlineA)
When called, calls the supplied function on every block passing the block itself as argument
as well as coordinates and the grid_age value of the same cell.
Should return a RGBA colour for the block, as well as the opacity of the stack outline (0 for no outline).
gamestate: the gamemode instance itself to pass in colour_function
]]
function Grid:drawCustom(colour_function, gamestate, is_3d)
--[[
colour_function: (game, block, x, y, age) -> (R, G, B, A, outlineA)
When called, calls the supplied function on every block passing the block itself as argument
as well as coordinates and the grid_age value of the same cell.
Should return a RGBA colour for the block, as well as the opacity of the stack outline (0 for no outline).
gamestate: the gamemode instance itself to pass in colour_function
]]
is_3d = is_3d == nil and false or is_3d
for y = 5, self.height do
for x = 1, self.width do
local block = self.grid[y][x]
local block = self.grid[y][x]
if block ~= empty then
local R, G, B, A, outline = colour_function(gamestate, block, x, y, self.grid_age[y][x])
local R, G, B, A, outline = colour_function(gamestate, block, x, y, self.grid_age[y][x])
if self.grid[y][x].colour == "X" then
A = 0
end
love.graphics.setColor(R, G, B, A)
love.graphics.draw(blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16)
if outline > 0 and self.grid[y][x].colour ~= "X" then
love.graphics.setColor(0.64, 0.64, 0.64, outline)
love.graphics.setLineWidth(1)
if y > 5 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then
drawBlock(R, G, B, A, blocks[self.grid[y][x].skin][self.grid[y][x].colour], 48+x*16, y*16,
48, 48+(self.width+1)*16,
5*16, (self.height+1)*16
)
end
end
end
-- everything that needs to be drawn over the blocks *must* be drawn after all blocks have been drawn, due to how 3D works
for y = 5, self.height do
for x = 1, self.width do
local block = self.grid[y][x]
if block ~= empty then
if outline > 0 and self.grid[y][x].colour ~= "X" then
love.graphics.setColor(0.64, 0.64, 0.64, outline)
love.graphics.setLineWidth(1)
if y > 5 and self.grid[y-1][x] == empty or self.grid[y-1][x].colour == "X" then
love.graphics.line(48.0+x*16, -0.5+y*16, 64.0+x*16, -0.5+y*16)
end
if y < self.height and self.grid[y+1][x] == empty or
@@ -542,7 +588,7 @@ function Grid:drawCustom(colour_function, gamestate)
if x < self.width and self.grid[y][x+1] == empty then
love.graphics.line(64.5+x*16, -0.0+y*16, 64.5+x*16, 16.0+y*16)
end
end
end
end
end
end

View File

@@ -2,6 +2,8 @@ local Object = require 'libs.classic'
local Piece = Object:extend()
local drawBlock = require 'tetris.components.draw_block'
function Piece:new(shape, rotation, position, block_offsets, gravity, lock_delay, skin, colour, big)
self.shape = shape
self.rotation = rotation
@@ -157,7 +159,6 @@ end
function Piece:draw(opacity, brightness, grid, partial_das)
if opacity == nil then opacity = 1 end
if brightness == nil then brightness = 1 end
love.graphics.setColor(brightness, brightness, brightness, opacity)
local offsets = self:getBlockOffsets()
local gravity_offset = 0
if config.gamesettings.smooth_movement == 1 and
@@ -168,16 +169,24 @@ function Piece:draw(opacity, brightness, grid, partial_das)
for index, offset in pairs(offsets) do
local x = self.position.x + offset.x
local y = self.position.y + offset.y
if self.big then
love.graphics.draw(
local scale = self.big and 2 or 1
if grid ~= nil then
drawBlock(
brightness, brightness, brightness, opacity,
blocks[self.skin][self.colour],
64+x*32+partial_das*2, 16+y*32+gravity_offset*2,
0, 2, 2
64 + (x*16+partial_das)*scale, 16 + (y*16+gravity_offset)*scale,
48, 48+(grid.width+1)*16,
0, (grid.height+1)*16,
self.big
)
else
love.graphics.draw(
drawBlock(
brightness, brightness, brightness, opacity,
blocks[self.skin][self.colour],
64+x*16+partial_das, 16+y*16+gravity_offset
64 + (x*16+partial_das)*scale, 16 + (y*16+gravity_offset)*scale,
false, false,
false, false,
self.big
)
end
end

View File

@@ -5,6 +5,7 @@ local playedReadySE = false
local playedGoSE = false
local Grid = require 'tetris.components.grid'
local drawBlock = require 'tetris.components.draw_block'
local Randomizer = require 'tetris.randomizers.randomizer'
local BagRandomizer = require 'tetris.randomizers.bag'
local binser = require 'libs.binser'
@@ -787,13 +788,12 @@ function GameMode:drawLineClearAnimation()
for y, row in pairs(self.cleared_block_table) do
for x, block in pairs(row) do
local animation_table = self:animation(x, y, block.skin, block.colour)
love.graphics.setColor(
animation_table[1], animation_table[2],
animation_table[3], animation_table[4]
)
love.graphics.draw(
drawBlock(
animation_table[1], animation_table[2], animation_table[3], animation_table[4],
blocks[animation_table[5]][animation_table[6]],
animation_table[7], animation_table[8]
animation_table[7], animation_table[8],
48, 48+(self.grid.width+1)*16,
5*16, (self.grid.height+1)*16
)
end
end
@@ -820,7 +820,7 @@ function GameMode:drawGhostPiece(ruleset)
local ghost_piece = self.piece:withOffset({x=0, y=0})
ghost_piece.ghost = true
ghost_piece:dropToBottom(self.grid)
ghost_piece:draw(0.5)
ghost_piece:draw(0.5, 1, self.grid)
end
function GameMode:drawNextQueue(ruleset)
@@ -837,7 +837,11 @@ function GameMode:drawNextQueue(ruleset)
for index, offset in pairs(offsets) do
local x = offset.x + ruleset:getDrawOffset(piece, rotation).x + ruleset.spawn_positions[piece].x
local y = offset.y + ruleset:getDrawOffset(piece, rotation).y + 4.7
love.graphics.draw(blocks[skin][colourscheme[piece]], pos_x+x*16, pos_y+y*16)
drawBlock(
1, 1, 1, 1,
blocks[skin][colourscheme[piece]],
pos_x+x*16, pos_y+y*16
)
end
end
for i = 1, self.next_queue_length do