diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index f60c092..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,69 +0,0 @@ -## Orthographic Camera API 2.2.5 [britzl released 2018-04-16] -FIX: Assign default `display.width` and `display.height` (960x640) if none is provided from game.project - -## Orthographic Camera API 2.2.4 [britzl released 2018-04-08] -FIX: An enabled camera is now immediately updated on creation. This will make sure that the render script gets the correct view and projection without any delay. - -## Orthographic Camera API 2.2.3 [britzl released 2018-04-01] -FIX: Added missing message constant that made the camera.set_projection() function fail - -## Orthographic Camera API 2.2.2 [britzl released 2018-03-31] -FIX: camera.set_zoom() crash - -## Orthographic Camera API 2.2.1 [britzl released 2018-03-19] -FIX: Added check on script shared state in the provided render script. Also added a note on this in the readme - -## Orthographic Camera API 2.2 [britzl released 2018-02-17] -NEW: Camera script properties to control bounds, deadzone, follow -CHANGE: Camera script properties can now be manipulated using go.animate(), go.set() and go.get() - -## Orthographic Camera API 2.1 [britzl released 2018-02-17] -NEW: camera.recoil() - -## Orthographic Camera API 2.0 [britzl released 2018-02-17] -NEW: Camera zoom property. This makes it a whole lot easier to at run-time configure the zoom level of a camera -NEW: camera.get_zoom() and camera.zoom_to() -CHANGE: The available projections have been simplified. There's now only FIXED_AUTO and FIXED_ZOOM. - -Refer to the example project to see the new changes in action. - -## Orthographic Camera API 1.5 [britzl released 2018-02-04] -NEW: Added camera.stop_shaking() - -## Orthographic Camera API 1.4.2 [britzl released 2018-01-11] -FIX: Added argument asserts to all public facing functions on camera.lua - -## Orthographic Camera API 1.4.1 [britzl released 2017-12-15] -FIX: Set an initial view and projection on the camera when it is initialized - -## Orthographic Camera API 1.4 [britzl released 2017-12-15] -NEW: screen_to_world_bounds() - -## Orthographic Camera API 1.3 [britzl released 2017-12-14] -NEW: camera.use_projector() to change projector at runtime -NEW: camera.set_window_size() to feed current window size from render script to camera -NEW: camera.get_window_size() to get current window size -NEW: camera.get_display_size() to get display size from game.project -NEW: camera.PROJECTORS.* constants for the provided projectors -CHANGE: Moved projector functions from render script to camera.lua - - -## Orthographic Camera API 1.2.2 [britzl released 2017-08-26] -* CHANGE: Moved the render script from the example project into the library folder. - -## Orthographic Camera API 1.2.1 [britzl released 2017-08-03] -FIX: Bounds still didn't work as expected - -## Orthographic Camera API 1.2 [britzl released 2017-08-01] -Delayed camera update to after all game objects have been update. This is to ensure that camera bounds are respected properly. - -## Orthographic Camera API 1.1 [britzl released 2017-07-26] -* Fixed issues with the new bounds functionality -* Added more projections (FIXED_NOZOOM, FIXED_ZOOM_2, FIXED_ZOOM_4, FIXED_ZOOM_6, FIXED_ZOOM_8 and FIXED_ZOOM_10) - -## Orthographic Camera API 1.0 [britzl released 2017-07-24] -First official release of the Orthographic Camera API - -## Orthographic Camera API 1.0 beta [britzl released 2017-06-30] -First public beta version of the API - diff --git a/README.md b/README.md index 8618d1a..b5315d7 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,9 @@ Note that when using `go.animate()`, `go.get()` and `go.set()` you need to make * `go.set("mycamera#camerascript", "zoom")` * `go.get("mycamera#camerascript", "zoom")` +#### order (number) +The order in which multiple cameras should be drawn, lower is drawn first. + #### projection (hash) The camera can be configured to support different kinds of orthographic projections. The default projection (aptly named `DEFAULT`) uses the same orthographic projection matrix as in the default render script (ie aspect ratio isn't maintained and content is stretched). Other projections are available out-of-the box: @@ -43,9 +46,6 @@ Additional custom projections can be added, see `camera.add_projector()` below. #### enabled (boolean) This controls if the camera is enabled by default or not. Send `enable` and `disable` messages to the script or use `go.set(id, "enable", true|false)` to toggle this value. -#### offset_gui (boolean) -This controls if the gui should be offset during a screen shake or a camera recoil. This will send the camera offset to the render script using the `send_camera_offset` message. - #### follow (boolean) This controls if the camera should follow a target or not. See `camera.follow()` for details. @@ -73,6 +73,9 @@ The camera bounds. See `camera.bounds()` for details. #### deadzone_left (number), deadzone_right (number), deadzone_top (number), deadzone_bottom (number) The camera deadzone. See `camera.deadzone()` for details. +#### viewport_left (number), viewport_right (number), viewport_top (number), viewport_bottom (number) +The camera viewport. + ## Render script integration In order for the Orthographic camera to function properly you need to integrate it in your render script. You can do this in a number of different ways: @@ -81,81 +84,27 @@ In order for the Orthographic camera to function properly you need to integrate The Orthographic API comes with a ready to use render script in `orthographic/render/orthograpic.render_script`. Open `game.project` and make sure to reference `orthographic/render/orthograpic.render` in the `Render` field in the `Bootstrap` section. ### 2. Integrating in an existing render script -While the camera is enabled it will send `set_view_projection` messages once per frame to the render script. The message is the same as that of the camera component, meaning that it contains `id`, `view` and `projection` values. Make sure that these values are handled and used properly in the render script. - -#### 2.1. Simplified integration -The Orthographic API provides a helper module to easily update the camera and set screen and world view and projection. Integrate it in your own render script like this: - - local render_helper = require "orthographic.render.helper" - - function init(self) - ... - render_helper.init() - ... - end - - function update(self) - render_helper.set_world_view_projection() - -- draw world - ... - - render_helper.set_screen_view_projection() - -- draw screen - ... - end - - function on_message(self, message_id, message) - render_helper.on_message(self, message_id, message) - ... - end - -NOTE: In order for this to work you need to make sure that the `Shared State` setting in the `Script` section of `game.project` is checked (defaults to checked) - -#### 2.2. Manual integration -If you prefer to manually setup the integration you need to make sure to handle the `set_view_projection` message: - - function update(self) - ... - render.set_view(self.view) - render.set_projection(self.projection) - -- draw using the view and projection - ... - end - - function on_message(self, message_id, message, sender) - if message_id == hash("set_view_projection") then - self.camera_id = message.id - self.view = message.view - self.projection = message.projection - end - end - -An alternative approach is to ignore the `set_view_projection` message and directly read the view and projection from the camera in the render script: +Get a list of active cameras and apply the camera viewport, view and projection before drawing: +``` local camera = require "orthographic.camera" function update(self) ... - local camera_id = id of your camera - render.set_view(camera.get_view(camera_id)) - render.set_projection(camera.get_projection(camera_id)) - -- draw using the view and projection - ... - end + for _,camera_id in ipairs(camera.get_cameras()) do + local viewport = camera.get_viewport(camera_id) + local view = camera.get_view(camera_id) + local projection = camera.get_projection(camera_id) -It is recommended to send the window width and height from the render script to the camera module. This is required if any of the projectors provided in `camera.lua` is used. It also allows custom projectors to get the current window size by calling `camera.get_window_size()`. Set the window size like this: + render.set_viewport(viewport.x, viewport.y, viewport.z, viewport.w) + render.set_view(view) + render.set_projection(projection) - local camera = require "orthographic.camera" - - function update(self) - ... - local window_width = render.get_window_width() - local window_height = render.get_window_height() - camera.set_window_size(window_width, window_height) - ... + -- draw using the viewport, view and projection + ... + end end - -NOTE: In order for this to work you need to make sure that the `Shared State` setting in the `Script` section of `game.project` is checked (defaults to checked) +``` ### Example render script The `orthographic/render` folder contains a render script that does the above mentioned integration of the Orthographic Camera API. Use it as it is or copy it into your project and make whatever modifications that you need. diff --git a/example/allfeatures/allfeatures.collection b/example/allfeatures/allfeatures.collection index e60a581..2c329ba 100644 --- a/example/allfeatures/allfeatures.collection +++ b/example/allfeatures/allfeatures.collection @@ -25,11 +25,6 @@ instances { value: "FIXED_ZOOM" type: PROPERTY_TYPE_HASH } - properties { - id: "offset_gui" - value: "true" - type: PROPERTY_TYPE_BOOLEAN - } properties { id: "follow" value: "true" diff --git a/example/multicamera/scene1.collection b/example/multicamera/scene1.collection index cbde808..ca1686f 100644 --- a/example/multicamera/scene1.collection +++ b/example/multicamera/scene1.collection @@ -72,6 +72,54 @@ instances { z: 1.0 } } +instances { + id: "camera2" + prototype: "/orthographic/camera.go" + position { + x: 0.0 + y: 0.0 + z: 0.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + component_properties { + id: "script" + properties { + id: "order" + value: "2.0" + type: PROPERTY_TYPE_NUMBER + } + properties { + id: "viewport_left" + value: "20.0" + type: PROPERTY_TYPE_NUMBER + } + properties { + id: "viewport_bottom" + value: "20.0" + type: PROPERTY_TYPE_NUMBER + } + properties { + id: "viewport_right" + value: "340.0" + type: PROPERTY_TYPE_NUMBER + } + properties { + id: "viewport_top" + value: "200.0" + type: PROPERTY_TYPE_NUMBER + } + } + scale3 { + x: 1.0 + y: 1.0 + z: 1.0 + } +} scale_along_z: 0 embedded_instances { id: "map" diff --git a/game.project b/game.project index 5eb2537..7f5e448 100644 --- a/game.project +++ b/game.project @@ -1,7 +1,8 @@ [project] title = Orthographic version = 0.9 -dependencies = https://github.com/subsoap/defos/archive/v2.3.1.zip,https://github.com/britzl/deftest/archive/2.7.0.zip +dependencies#0 = https://github.com/subsoap/defos/archive/v2.3.1.zip +dependencies#1 = https://github.com/britzl/deftest/archive/2.7.0.zip [bootstrap] main_collection = /example/allfeatures/allfeatures.collectionc diff --git a/orthographic/camera.lua b/orthographic/camera.lua index 692b9a2..e0771e5 100644 --- a/orthographic/camera.lua +++ b/orthographic/camera.lua @@ -18,6 +18,7 @@ M.MSG_BOUNDS = hash("bounds") M.MSG_UPDATE_CAMERA = hash("update_camera") M.MSG_ZOOM_TO = hash("zoom_to") M.MSG_USE_PROJECTION = hash("use_projection") +M.MSG_VIEWPORT = hash("viewport") local HIGH_DPI = (sys.get_config("display.high_dpi", "0") == "1") @@ -55,11 +56,15 @@ local VECTOR3_MINUS1_Z = vmath.vector3(0, 0, -1.0) local VECTOR3_UP = vmath.vector3(0, 1.0, 0) local MATRIX4 = vmath.matrix4() +local VECTOR4 = vmath.vector4() local v4_tmp = vmath.vector4() local v3_tmp = vmath.vector3() local cameras = {} +local camera_ids = {} +-- track if the cameras list has changed or not +local cameras_dirty = true --- projection providers (projectors) -- a mapping of id to function to calculate and return a projection matrix @@ -205,7 +210,7 @@ local function calculate_projection(camera) local projection_id = camera.projection_id assert(projectors[projection_id], "Unknown projection id") local projector_fn = projectors[projection_id] or projectors[M.PROJECTOR.DEFAULT] - return projector_fn(camera_id, camera.near_z, camera.far_z, camera.zoom) + return projector_fn(camera.id, camera.near_z, camera.far_z, camera.zoom) end @@ -230,6 +235,7 @@ function M.init(camera_id, camera_script_url, settings) assert(camera_id, "You must provide a camera id") assert(camera_script_url, "You must provide a camera script url") cameras[camera_id] = settings + cameras_dirty = true local camera = cameras[camera_id] camera.id = camera_id camera.url = camera_script_url @@ -238,6 +244,7 @@ function M.init(camera_id, camera_script_url, settings) camera.far_z = go.get(camera_script_url, "far_z") camera.view = calculate_view(camera, go.get_world_position(camera_id)) camera.projection = calculate_projection(camera) + camera.viewport = vmath.vector4(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT) if not sys.get_engine_info().is_debug then log = function() end @@ -256,8 +263,9 @@ function M.final(camera_id) -- check that a new camera with the same id but from a different go hasn't been -- replacing the camera that is being unregistered -- if this is the case we simply ignore the call to final() - if cameras[camera_id].url ~= msg.url() then + if cameras[camera_id].url == msg.url() then cameras[camera_id] = nil + cameras_dirty = true end end @@ -278,8 +286,18 @@ function M.update(camera_id, dt) return end - update_window_size() + local enabled = go.get(camera.url, "enabled") + local order = go.get(camera.url, "order") + cameras_dirty = cameras_dirty or (camera.enabled ~= enabled) + cameras_dirty = cameras_dirty or (camera.order ~= order) + camera.enabled = enabled + camera.order = order + if not enabled then + return + end + update_window_size() + local camera_world_pos = go.get_world_position(camera_id) local camera_world_to_local_diff = camera_world_pos - go.get_position(camera_id) local follow_enabled = go.get(camera.url, "follow") @@ -360,6 +378,22 @@ function M.update(camera_id, dt) camera_world_pos = M.screen_to_world(camera_id, cp) end + local viewport_top = go.get(camera.url, "viewport_top") + local viewport_left = go.get(camera.url, "viewport_left") + local viewport_bottom = go.get(camera.url, "viewport_bottom") + local viewport_right = go.get(camera.url, "viewport_right") + if viewport_top ~= 0 or viewport_left ~= 0 or viewport_bottom ~= 0 or viewport_right ~= 0 then + camera.viewport.x = viewport_left + camera.viewport.y = viewport_bottom + camera.viewport.z = viewport_right - viewport_left + camera.viewport.w = viewport_top - viewport_bottom + else + camera.viewport.x = 0 + camera.viewport.y = 0 + camera.viewport.z = WINDOW_WIDTH + camera.viewport.w = WINDOW_HEIGHT + end + go.set_position(camera_world_pos + camera_world_to_local_diff, camera_id) @@ -399,6 +433,7 @@ function M.update(camera_id, dt) end end camera.offset = offset + camera.projection_id = go.get(camera.url, "projection") camera.near_z = go.get(camera.url, "near_z") camera.far_z = go.get(camera.url, "far_z") @@ -407,6 +442,27 @@ function M.update(camera_id, dt) camera.projection = calculate_projection(camera) end +--- Get list of camera ids +-- @return List of camera ids +function M.get_cameras() + if cameras_dirty then + cameras_dirty = false + local enabled_cameras = {} + for camera_id,camera in pairs(cameras) do + if camera.enabled then + enabled_cameras[#enabled_cameras + 1] = camera + end + end + table.sort(enabled_cameras, function(a, b) + return b.order > a.order + end) + camera_ids = {} + for i=1,#enabled_cameras do + camera_ids[i] = enabled_cameras[i].id + end + end + return camera_ids +end --- Follow a game object -- @param camera_id @@ -556,6 +612,15 @@ function M.get_projection(camera_id) end +--- Get the projection id for a camera +-- @param camera_id +-- @return Projection id +function M.get_projection_id(camera_id) + assert(camera_id, "You must provide a camera id") + return cameras[camera_id].projection_id +end + + --- Get the view matrix for a specific camera, based on the camera position -- and rotation -- @param camera_id @@ -566,23 +631,32 @@ function M.get_view(camera_id) end ---- Send the view and projection matrix for a camera to the render script +--- Get the viewport for a specific camera -- @param camera_id -function M.send_view_projection(camera_id) +-- @return Viewport (vector4) +function M.get_viewport(camera_id) assert(camera_id, "You must provide a camera id") - local camera = cameras[camera_id] - local view = camera.view or MATRIX4 - local projection = camera.projection or MATRIX4 - msg.post("@render:", "set_view_projection", { id = camera_id, view = view, projection = projection }) + return cameras[camera_id].viewport end ---- Send the camera offset to the render script +--- Get the offset for a specific camera -- @param camera_id -function M.send_camera_offset(camera_id) +-- @return Offset (vector3) +function M.get_offset(camera_id) + assert(camera_id, "You must provide a camera id") + return cameras[camera_id].offset +end + + +--- Send the view and projection matrix for a camera to the render script +-- @param camera_id +function M.send_view_projection(camera_id) assert(camera_id, "You must provide a camera id") local camera = cameras[camera_id] - msg.post("@render:", "set_camera_offset", { id = camera_id, offset = camera.offset }) + local view = camera.view or MATRIX4 + local projection = camera.projection or MATRIX4 + msg.post("@render:", "set_view_projection", { id = camera_id, view = view, projection = projection }) end @@ -597,9 +671,20 @@ end function M.screen_to_world(camera_id, screen) assert(camera_id, "You must provide a camera id") assert(screen, "You must provide screen coordinates to convert") - local view = cameras[camera_id].view or MATRIX4 - local projection = cameras[camera_id].projection or MATRIX4 - return M.unproject(view, projection, vmath.vector3(screen)) + local camera = cameras[camera_id] + local view = camera.view or MATRIX4 + local projection = camera.projection or MATRIX4 + local viewport = camera.viewport or VECTOR4 + local viewport_width = viewport.z * DISPLAY_WIDTH / WINDOW_WIDTH + local viewport_height = viewport.w * DISPLAY_HEIGHT / WINDOW_HEIGHT + local viewport_left = viewport.x * DISPLAY_WIDTH / WINDOW_WIDTH + local viewport_bottom = viewport.y * DISPLAY_HEIGHT / WINDOW_HEIGHT + + local s = vmath.vector3(screen) + s.x = (s.x - viewport_left) * (DISPLAY_WIDTH / viewport_width) + s.y = (s.y - viewport_bottom) * (DISPLAY_HEIGHT / viewport_height) + + return M.unproject(view, projection, s) end @@ -613,11 +698,16 @@ end function M.window_to_world(camera_id, window) assert(camera_id, "You must provide a camera id") assert(window, "You must provide window coordinates to convert") - local view = cameras[camera_id].view or MATRIX4 - local projection = cameras[camera_id].projection or MATRIX4 + local camera = cameras[camera_id] + local view = camera.view or MATRIX4 + local projection = camera.projection or MATRIX4 + local viewport = camera.viewport or VECTOR4 local scale_x = window.x * dpi_ratio * DISPLAY_WIDTH / WINDOW_WIDTH local scale_y = window.y * dpi_ratio * DISPLAY_HEIGHT / WINDOW_HEIGHT + local screen = vmath.vector3(scale_x, scale_y, 0) + screen.x = (screen.x - viewport_left) * (DISPLAY_WIDTH / viewport_width) + screen.y = (screen.y - viewport_bottom) * (DISPLAY_HEIGHT / viewport_height) return M.unproject(view, projection, screen) end @@ -630,9 +720,18 @@ end function M.world_to_screen(camera_id, world, adjust_mode) assert(camera_id, "You must provide a camera id") assert(world, "You must provide world coordinates to convert") - local view = cameras[camera_id].view or MATRIX4 - local projection = cameras[camera_id].projection or MATRIX4 + local camera = cameras[camera_id] + local view = camera.view or MATRIX4 + local projection = camera.projection or MATRIX4 + local viewport = camera.viewport or VECTOR4 + local viewport_width = viewport.z * DISPLAY_WIDTH / WINDOW_WIDTH + local viewport_height = viewport.w * DISPLAY_HEIGHT / WINDOW_HEIGHT + local viewport_left = viewport.x * DISPLAY_WIDTH / WINDOW_WIDTH + local viewport_bottom = viewport.y * DISPLAY_HEIGHT / WINDOW_HEIGHT + local screen = M.project(view, projection, vmath.vector3(world)) + screen.x = viewport_left + screen.x * (viewport_width / DISPLAY_WIDTH) + screen.y = viewport_bottom + screen.y * (viewport_height / DISPLAY_HEIGHT) if adjust_mode then screen.x = (screen.x / GUI_ADJUST[adjust_mode].sx) - GUI_ADJUST[adjust_mode].ox screen.y = (screen.y / GUI_ADJUST[adjust_mode].sy) - GUI_ADJUST[adjust_mode].oy @@ -694,8 +793,10 @@ end -- @return bounds Vector4 where x is left, y is top, z is right and w is bottom function M.screen_to_world_bounds(camera_id) assert(camera_id, "You must provide a camera id") - local view = cameras[camera_id].view or MATRIX4 - local projection = cameras[camera_id].projection or MATRIX4 + local camera = cameras[camera_id] + local view = camera.view or MATRIX4 + local projection = camera.projection or MATRIX4 + local viewport = camera.viewport or VECTOR4 local inv = vmath.inv(projection * view) local bl_x, bl_y = unproject_xyz(inv, 0, 0, 0) local br_x, br_y = unproject_xyz(inv, DISPLAY_WIDTH, 0, 0) diff --git a/orthographic/camera.script b/orthographic/camera.script index f6d4c25..03cf41d 100644 --- a/orthographic/camera.script +++ b/orthographic/camera.script @@ -3,8 +3,7 @@ go.property("far_z", 1) go.property("zoom", 1) go.property("projection", hash("DEFAULT")) go.property("enabled", true) - -go.property("offset_gui", false) +go.property("order", 1) go.property("follow", false) go.property("follow_horizontal", true) @@ -18,11 +17,17 @@ go.property("bounds_left", 0) go.property("bounds_bottom", 0) go.property("bounds_right", 0) go.property("bounds_top", 0) + go.property("deadzone_left", 0) go.property("deadzone_bottom", 0) go.property("deadzone_right", 0) go.property("deadzone_top", 0) +go.property("viewport_left", 0) +go.property("viewport_bottom", 0) +go.property("viewport_right", 0) +go.property("viewport_top", 0) + local camera = require "orthographic.camera" local function has_active_follow_target(follow_id) @@ -41,8 +46,8 @@ end function init(self) camera.init(go.get_id(), msg.url(), { zoom = self.zoom }) + camera.update(go.get_id(), 0) if self.enabled then - camera.update(go.get_id(), 0) camera.send_view_projection(go.get_id()) if self.follow and self.follow_immediately then position_on_follow(self) @@ -57,20 +62,17 @@ end function update(self, dt) - if self.enabled then - -- update camera and view projection after all game objects have been updated - -- will jitter otherwise - msg.post("#", camera.MSG_UPDATE_CAMERA, { dt = dt }) - end + -- update camera and view projection after all game objects have been updated + -- will jitter otherwise + msg.post("#", camera.MSG_UPDATE_CAMERA, { dt = dt }) end function on_message(self, message_id, message, sender) if message_id == camera.MSG_UPDATE_CAMERA then camera.update(go.get_id(), message.dt) - camera.send_view_projection(go.get_id()) - if self.offset_gui then - camera.send_camera_offset(go.get_id()) + if self.enabled then + camera.send_view_projection(go.get_id()) end elseif message_id == camera.MSG_ENABLE then self.enabled = true @@ -108,6 +110,11 @@ function on_message(self, message_id, message, sender) self.bounds_top = message.top or 0 self.bounds_left = message.left or 0 self.bounds_bottom = message.bottom or 0 + elseif message_id == camera.MSG_VIEWPORT then + self.viewport_right = message.right or 0 + self.viewport_top = message.top or 0 + self.viewport_left = message.left or 0 + self.viewport_bottom = message.bottom or 0 elseif message_id == camera.MSG_SHAKE then camera.shake(go.get_id(), message.intensity, message.duration, message.direction, function() msg.post(sender, camera.MSG_SHAKE_COMPLETED) diff --git a/orthographic/render/helper.lua b/orthographic/render/helper.lua deleted file mode 100644 index f906a68..0000000 --- a/orthographic/render/helper.lua +++ /dev/null @@ -1,78 +0,0 @@ -local camera = require "orthographic.camera" - -if sys.get_config("script.shared_state") ~= "1" then - error("ERROR - camera - 'shared_state' setting in game.project must be enabled for camera to work.") -end - -local M = {} - -local IDENTITY = vmath.matrix4() - -local SET_VIEW_PROJECTION = hash("set_view_projection") -local SET_CAMERA_OFFSET = hash("set_camera_offset") - -local world_view = vmath.matrix4() -local world_projection = vmath.matrix4() -local screen_view = vmath.matrix4() -local screen_projection = vmath.matrix4() -local camera_offset = nil - - -function M.init() -end - -function M.world_projection() - return world_projection -end - -function M.world_view() - return world_view -end - -function M.set_world_view_projection() - render.set_view(M.world_view()) - render.set_projection(M.world_projection()) -end - - -function M.screen_view() - return IDENTITY -end - -function M.screen_projection() - local current_window_width = render.get_window_width() - local current_window_height = render.get_window_height() - if current_window_width ~= 0 and current_window_height ~= 0 then - local left, right, bottom, top - if camera_offset then - left = camera_offset.x - right = left + current_window_width - bottom = camera_offset.y - top = bottom + current_window_height - else - left = 0 - right = current_window_width - bottom = 0 - top = current_window_height - end - screen_projection = vmath.matrix4_orthographic(left, right, bottom, top, -1, 1) - end - return screen_projection -end - -function M.set_screen_view_projection() - render.set_view(M.screen_view()) - render.set_projection(M.screen_projection()) -end - - -function M.on_message(_, message_id, message) - if message_id == SET_VIEW_PROJECTION then - world_view = message.view - world_projection = message.projection - elseif message_id == SET_CAMERA_OFFSET then - camera_offset = message.offset - end -end - -return M diff --git a/orthographic/render/orthographic.render_script b/orthographic/render/orthographic.render_script index 2661930..186e1fb 100644 --- a/orthographic/render/orthographic.render_script +++ b/orthographic/render/orthographic.render_script @@ -1,10 +1,11 @@ -local render_helper = require "orthographic.render.helper" local camera = require "orthographic.camera" camera.ORTHOGRAPHIC_RENDER_SCRIPT_USED = true local CLEAR_COLOR = hash("clear_color") +local IDENTITY = vmath.matrix4() + function init(self) self.tile_pred = render.predicate({"tile"}) self.gui_pred = render.predicate({"gui"}) @@ -16,48 +17,47 @@ function init(self) 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) - - render_helper.init() end function update(self) + -- clear color render.set_depth_mask(true) render.clear({[render.BUFFER_COLOR_BIT] = self.clear_color, [render.BUFFER_DEPTH_BIT] = 1, [render.BUFFER_STENCIL_BIT] = 0}) - - -- setup the viewport - render.set_viewport(0, 0, render.get_window_width(), render.get_window_height()) - - - -- draw world space using projection received from the camera in on_message - render_helper.set_world_view_projection(self) - - 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.draw(self.tile_pred) - render.draw(self.particle_pred) - render.draw_debug3d() - - - -- draw gui in screen space using an orthographic projection - render_helper.set_screen_view_projection(self) - - render.enable_state(render.STATE_STENCIL_TEST) - render.draw(self.gui_pred) - render.draw(self.text_pred) - render.disable_state(render.STATE_STENCIL_TEST) - - render.set_depth_mask(false) + for _,camera_id in ipairs(camera.get_cameras()) do + local viewport = camera.get_viewport(camera_id) + render.set_viewport(viewport.x, viewport.y, viewport.z, viewport.w) + render.set_view(camera.get_view(camera_id)) + render.set_projection(camera.get_projection(camera_id)) + + 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.draw(self.tile_pred) + render.draw(self.particle_pred) + render.draw_debug3d() + + + -- draw gui in screen space using an orthographic projection + 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.gui_pred) + render.draw(self.text_pred) + render.disable_state(render.STATE_STENCIL_TEST) + + render.set_depth_mask(false) + end end function on_message(self, message_id, message) - render_helper.on_message(nil, message_id, message) if message_id == CLEAR_COLOR then self.clear_color = message.color end