Orthographic Camera screen_to_world() not working with Fixed Projection

I am using a cursor to indicate the location of the mouse in my game. If I have the camera projection set to “DEFAULT”, the screen_to_world function works great. No matter the size I change the window to, the cursor always is where it’s supposed to be. If I change the projection to “FIXED_ZOOM” or “FIXED_AUTO” it works great. However, if the camera is set to one of the fixed projections and I change the window size while the game is running, the cursor becomes offset from the mouse. I don’t really want to leave the projection at default, so I’m not quite sure what to do.

This is the code for the cursor:

local camera = require "orthographic.camera"
function init(self)
	msg.post(".", "acquire_input_focus")
	self.pos = vmath.vector3()
end

function update(self, dt)
	go.set_position(self.pos)
end

function on_input(self, action_id, action)
	local pos = vmath.vector3(action.x, action.y, 0)
	local camera_id = hash("/camera")
	self.pos = camera.screen_to_world(camera_id, pos)
end

Is this expected behavior?
If so, how can I account for it in my code?

EDIT:
I forgot to mention that I am using the orthographic.render.helper in my current render script.

local render_helper = require "orthographic.render.helper"
--
-- projection that centers content with maintained aspect ratio and optional zoom
--
local function fixed_projection(near, far, zoom)
    local projected_width = render.get_window_width() / (zoom or 1)
    local projected_height = render.get_window_height() / (zoom or 1)
    local xoffset = -(projected_width - render.get_width()) / 2
    local yoffset = -(projected_height - render.get_height()) / 2
    return vmath.matrix4_orthographic(xoffset, xoffset + projected_width, yoffset, yoffset + projected_height, near, far)
end
--
-- projection that centers and fits content with maintained aspect ratio
--
local function fixed_fit_projection(near, far)
    local width = render.get_width()
    local height = render.get_height()
    local window_width = render.get_window_width()
    local window_height = render.get_window_height()
    local zoom = math.min(window_width / width, window_height / height)
    return fixed_projection(near, far, zoom)
end
--
-- projection that stretches content
--
local function stretch_projection(near, far)
    return vmath.matrix4_orthographic(0, render.get_width(), 0, render.get_height(), near, far)
end

local function get_projection(self)
    return self.projection_fn(self.near, self.far, self.zoom)
end

function init(self)
    render_helper.init()
    
    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.clear_color = vmath.vector4(0, 0, 0, 0)
    self.clear_color.x = sys.get_config("render.clear_color_red", 0)
    self.clear_color.y = sys.get_config("render.clear_color_green", 0)
    self.clear_color.z = sys.get_config("render.clear_color_blue", 0)
    self.clear_color.w = sys.get_config("render.clear_color_alpha", 0)

    self.view = vmath.matrix4()

    -- default is stretch projection. copy from builtins and change for different projection
    -- or send a message to the render script to change projection:
    -- msg.post("@render:", "use_stretch_projection", { near = -1, far = 1 })
    -- msg.post("@render:", "use_fixed_projection", { near = -1, far = 1, zoom = 2 })
    -- msg.post("@render:", "use_fixed_fit_projection", { near = -1, far = 1 })
    self.near = -1
    self.far = 1
    self.projection_fn = stretch_projection
end

function update(self)
    render.set_depth_mask(true)
    render.set_stencil_mask(0xff)
    render.clear({[render.BUFFER_COLOR_BIT] = self.clear_color, [render.BUFFER_DEPTH_BIT] = 1, [render.BUFFER_STENCIL_BIT] = 0})

    render.set_viewport(0, 0, render.get_window_width(), render.get_window_height())
    render.set_view(self.view)

    render.set_depth_mask(false)
    render.disable_state(render.STATE_DEPTH_TEST)
    render.disable_state(render.STATE_STENCIL_TEST)
    render.enable_state(render.STATE_BLEND)
    render.set_blend_func(render.BLEND_SRC_ALPHA, render.BLEND_ONE_MINUS_SRC_ALPHA)
    render.disable_state(render.STATE_CULL_FACE)

    render_helper.set_world_view_projection()

    render.draw(self.tile_pred)
    render.draw(self.particle_pred)
    render.draw_debug3d()

    -- render GUI
    --
    render_helper.set_screen_view_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

function on_message(self, message_id, message)
    render_helper.on_message(self, message_id, message)
    if message_id == hash("clear_color") then
        self.clear_color = message.color
    elseif message_id == hash("set_view_projection") then
        self.view = message.view
        self.projection = message.projection
    elseif message_id == hash("use_camera_projection") then
        self.projection_fn = function() return self.projection or vmath.matrix4() end
    elseif message_id == hash("use_stretch_projection") then
        self.near = message.near or -1
        self.far = message.far or 1
        self.projection_fn = stretch_projection
    elseif message_id == hash("use_fixed_projection") then
        self.near = message.near or -1
        self.far = message.far or 1
        self.zoom = message.zoom or 1
        self.projection_fn = fixed_projection
    elseif message_id == hash("use_fixed_fit_projection") then
        self.near = message.near or -1
        self.far = message.far or 1
        self.projection_fn = fixed_fit_projection
    end
end

Does it work if you use the render script that comes with Orthographic?

Yes that works.
I figured out what the problem was… I just realized that my render file was still using the default render script. This is embarrassing! XD Sorry for bothering you.

1 Like