britzl / template-lowres

Defold template project for lowres/pixel-art games.
26 stars 3 forks source link

Turn into general purpose pixel art template #2

Closed subsoap closed 1 year ago

subsoap commented 6 years ago

Only real changes needed I think would be to include a SIZE_HEIGHT and SIZE_WIDTH in the render script.

Another thing could add would be to produce non-square pixels. Here is an example

local SIZE = 64
local SIZE_WIDTH = 64 * 1.143

local IDENTITY = vmath.matrix4()
local LOWREZ_PROJECTION = vmath.matrix4_orthographic(0, SIZE, 0, SIZE, -1, 1)

function init(self)
    self.tile_pred = render.predicate({"tile"})
    self.gui_pred = render.predicate({"gui"})
    self.text_pred = render.predicate({"text"})
    self.particle_pred = render.predicate({"particle"})
    self.lowrez_pred = render.predicate({"lowrez"})
    self.controls_pred = render.predicate({"controls"})

    self.view = IDENTITY

    -- render target buffer parameters
    local color_params = {
        format = render.FORMAT_RGBA,
        width = SIZE,
        height = SIZE,
        min_filter = render.FILTER_NEAREST,
        mag_filter = render.FILTER_NEAREST,
        u_wrap = render.WRAP_CLAMP_TO_EDGE,
        v_wrap = render.WRAP_CLAMP_TO_EDGE
    }
    local depth_params = {
        format = render.FORMAT_DEPTH,
        width = SIZE,
        height = SIZE,
        u_wrap = render.WRAP_CLAMP_TO_EDGE,
        v_wrap = render.WRAP_CLAMP_TO_EDGE
    }
    self.rt = render.render_target("lowrez", { [render.BUFFER_COLOR_BIT] = color_params, [render.BUFFER_DEPTH_BIT] = depth_params })

    local clear_color = vmath.vector4(0, 0, 0, 0)
    clear_color.x = sys.get_config("render.clear_color_red", 0)
    clear_color.y = sys.get_config("render.clear_color_green", 0)
    clear_color.z = sys.get_config("render.clear_color_blue", 0)
    clear_color.w = sys.get_config("render.clear_color_alpha", 0)
    self.clear_buffers = {
        [render.BUFFER_COLOR_BIT] = clear_color,
        [render.BUFFER_DEPTH_BIT] = 1,
        [render.BUFFER_STENCIL_BIT] = 0
    }
end

local function clear(self, w, h)
    -- clear
    render.set_view(IDENTITY)
    render.set_projection(vmath.matrix4_orthographic(0, w, 0, h, -1, 1))
    render.set_depth_mask(true)
    render.set_stencil_mask(0xff)
    render.clear(self.clear_buffers)
end

local function draw_game(self)
    clear(self, render.get_window_width(), render.get_window_height())

    render.set_viewport(0, 0, SIZE, SIZE)

    -- draw world (sprites, tiles, pfx etc)
    render.set_depth_mask(false)
    render.disable_state(render.STATE_DEPTH_TEST)
    render.disable_state(render.STATE_STENCIL_TEST)
    render.disable_state(render.STATE_CULL_FACE)
    render.enable_state(render.STATE_BLEND)
    render.set_blend_func(render.BLEND_SRC_ALPHA, render.BLEND_ONE_MINUS_SRC_ALPHA)
    render.set_view(self.view)
    render.set_projection(LOWREZ_PROJECTION)
    render.draw(self.tile_pred)
    render.draw(self.particle_pred)
    render.draw_debug3d()

    -- draw screen space gui
    render.set_view(vmath.matrix4())
    render.set_projection(LOWREZ_PROJECTION)
    render.enable_state(render.STATE_STENCIL_TEST)
    render.draw(self.gui_pred)
    render.draw(self.text_pred)
    render.disable_state(render.STATE_STENCIL_TEST)
end

local function draw_upscaled(self)
    -- calculate zoom
    local window_width = render.get_window_width()
    local window_height = render.get_window_height()
    local zoom = math.min(window_width / SIZE_WIDTH, window_height / SIZE)
    zoom = math.max(1, math.floor(zoom))

    -- positioning
    local width = SIZE_WIDTH * zoom
    local height = SIZE * zoom
    local width_halved = width / 2
    local height_halved = height / 2
    local offsetx = (window_width - width) / 2
    local offsety = (window_height - height) / 2

    -- draw!
    render.set_viewport(offsetx, offsety, width, height)
    render.set_view(IDENTITY)
    render.set_projection(IDENTITY)
    render.enable_texture(0, self.rt, render.BUFFER_COLOR_BIT)
    render.draw(self.lowrez_pred)
    render.disable_texture(0, self.rt)
end

local function draw_controls(self)
    render.set_viewport(0, 0, render.get_window_width(), render.get_window_height())
    render.set_view(IDENTITY)
    render.set_projection(vmath.matrix4_orthographic(0, render.get_window_width(), 0, render.get_window_height(), -1, 1))

    render.enable_state(render.STATE_STENCIL_TEST)
    render.draw(self.controls_pred)
    --render.draw(self.text_pred)
    render.disable_state(render.STATE_STENCIL_TEST)
end

function update(self)
    clear(self, render.get_window_width(), render.get_window_height())
    render.enable_render_target(self.rt)
    draw_game(self)
    render.disable_render_target(self.rt)
    draw_upscaled(self)
    draw_controls(self)
end

function on_message(self, message_id, message)
    if message_id == hash("clear_color") then
        self.clear_buffers[render.BUFFER_COLOR_BIT] = message.color
    elseif message_id == hash("set_view_projection") then
        self.view = message.view
    end
end

Which produces thicc pixels useful in NES projects although may not be best way to achieve the effect that NES emulators can produce to give proper NES look, other game systems use non-square pixels too

2018-08-11 14_03_23-lowrezjam

Could maybe merge this with Orthographic or make it an extension of that somehow.

subsoap commented 6 years ago

Hmmm

https://amigalove.com/viewtopic.php?t=38

subsoap commented 6 years ago

https://wiki.nesdev.com/w/index.php/PAL_video

subsoap commented 6 years ago

Should also add an upscaling shader, one of the pixel art to "vector" graphics shaders common with emulators. http://filthypants.blogspot.com/search/label/xBR

subsoap commented 6 years ago

https://github.com/LIJI32/SameBoy/blob/master/Shaders/OmniScale.fsh

britzl commented 6 years ago

I think maybe the shader stuff, crt and scanline filters are better suited for a separate repo. You started something similar recently? I've many tims thought of trying to find a way to modularize the render script and make it really easy for people to create full screen effects using shaders.

As for setting WIDTH and HEIGHT individually: Good idea and a simple change. I'll keep the default values to 64x64.

subsoap commented 6 years ago

Yes, it could be an a stand alone example (I could make some too, if they don't exist at some point I will). It's something people who want to make pixel art games will eventually ask for always.

I made a rendertarget helper which makes using full screen render targets for post processing and other full screen effects really easy. Will release it sometime soon. It somewhat makes full screen effects easy although you still have to manually include and setup unitquads and materials, require the module, and then set whatever you want rendered to the rendertarget listed there instead of being rendered normally. At least it's all easier now than it was before!

subsoap commented 6 years ago

Mind if I make a pixel art template as a fork of this project with post processing enabled or do you want to do it?

britzl commented 6 years ago

Please go ahead! I haven't had time to do it yet.

britzl commented 1 year ago

Closing in favor of https://github.com/britzl/lowrezjam-template/issues/6