diff --git a/assets/models/kenney_prototype-kit/crate.glb b/assets/models/kenney_prototype-kit/crate.glb new file mode 100644 index 0000000..c2d66b4 Binary files /dev/null and b/assets/models/kenney_prototype-kit/crate.glb differ diff --git a/examples/_main/examples.lua b/examples/_main/examples.lua index a571770..1287b38 100644 --- a/examples/_main/examples.lua +++ b/examples/_main/examples.lua @@ -18,7 +18,7 @@ examples["input"] = { "move", "text", "down_duration", "mouse_and_touch" } examples["material"] = { "vertexcolor", { name = "unlit", nobg = true }, "uvgradient", "noise", { name = "screenspace", nobg = true } } examples["particles"] = { "confetti", "particlefx", "modifiers", "fire_and_smoke" } examples["sound"] = { "music", "fade_in_out", "panning" } -examples["render"] = { "camera", "screen_to_world" } +examples["render"] = { "camera", { name = "orbit_camera", nobg = true }, "screen_to_world" } examples["debug"] = { "physics", "profile" } examples["collection"] = { "proxy", "splash", "timestep" } examples["sprite"] = { "size", "tint", "flip", "bunnymark" } diff --git a/examples/_main/loader.go b/examples/_main/loader.go index 5f4bf16..0c2f1b8 100644 --- a/examples/_main/loader.go +++ b/examples/_main/loader.go @@ -454,3 +454,9 @@ embedded_components { data: "collection: \"/examples/material/screenspace/screenspace.collection\"\n" "" } +embedded_components { + id: "render/orbit_camera" + type: "collectionproxy" + data: "collection: \"/examples/render/orbit_camera/orbit_camera.collection\"\n" + "" +} diff --git a/examples/material/screenspace/screenspace.collection b/examples/material/screenspace/screenspace.collection index 07a8074..2b1c39c 100644 --- a/examples/material/screenspace/screenspace.collection +++ b/examples/material/screenspace/screenspace.collection @@ -6,6 +6,15 @@ embedded_instances { " id: \"main\"\n" " component: \"/examples/material/screenspace/screenspace.script\"\n" "}\n" + "components {\n" + " id: \"orbit_camera\"\n" + " component: \"/examples/render/orbit_camera/orbit_camera.script\"\n" + " properties {\n" + " id: \"offset\"\n" + " value: \"0.0, 0.25, 0.0\"\n" + " type: PROPERTY_TYPE_VECTOR3\n" + " }\n" + "}\n" "embedded_components {\n" " id: \"camera\"\n" " type: \"camera\"\n" @@ -17,9 +26,6 @@ embedded_instances { "\"\n" "}\n" "" - position { - y: 0.25 - } rotation { x: -0.25881904 w: 0.9659258 diff --git a/examples/material/screenspace/screenspace.md b/examples/material/screenspace/screenspace.md index 7674f26..648745a 100644 --- a/examples/material/screenspace/screenspace.md +++ b/examples/material/screenspace/screenspace.md @@ -12,11 +12,13 @@ We added two game objects and two models to which we assigned our new `screenspa - fragment shader: we added sampling the color based on screenspace coordinates and blending into the final output color. - material properties: we added a new sampler to set a second texture to be used as a pattern, and user-defined uniforms to control the fragment shader. -Then the script setups a perspective camera, activates it with the `acquire_camera_focus` message. The last important thing is to pass the screen size to the shader to adjust the aspect ratio: +The last important thing is to pass the screen size to the shader to adjust the aspect ratio: ```lua local w, h = window.get_size() go.set("#model", "screen_size", vmath.vector4(w, h, 0, 0)) ``` +To activate a perspective camera and to have camera controls, we added the `orbit_camera.script` script from the [Orbit Camera (3D)](/examples/render/orbit_camera/orbit_camera/) example. + The shaders are written in GLSL 1.40, which is available from Defold 1.9.2. The model used in this example is from Kenney's [Prototype Pack](https://kenney.nl/assets/prototype-kit), licensed under CC0. diff --git a/examples/material/screenspace/screenspace.script b/examples/material/screenspace/screenspace.script index 94d334c..8288124 100644 --- a/examples/material/screenspace/screenspace.script +++ b/examples/material/screenspace/screenspace.script @@ -1,16 +1,5 @@ -local ZOOM_SPEED = 0.2 -local ROTATION_SPEED = 0.5 - function init(self) - msg.post("@render:", "use_camera_projection") - msg.post(".", "acquire_input_focus") - - self.yaw = go.get(".", "euler.y") -- for camera rotation - self.pitch = go.get(".", "euler.x") -- for camera rotation - self.offset = go.get_position() - self.zoom = 3 -- default zoom - self.zoom_offset = 0 -- modification from default zoom - self.time = 0 -- for pattern animation + self.time = 0 -- for pattern animation -- The model with the pattern - we enabled the effect, 0.5 is the intensity (alpha) go.set("/crate_selected#model", "pattern_opts.x", 0.5) @@ -22,14 +11,6 @@ function init(self) end function update(self, dt) - -- Camera controls - local camera_yaw = vmath.quat_rotation_y(math.rad(self.yaw)) - local camera_pitch = vmath.quat_rotation_x(math.rad(self.pitch)) - local camera_rot = camera_yaw * camera_pitch - local camera_position = self.offset + vmath.rotate(camera_rot, vmath.vector3(0, 0, self.zoom + self.zoom_offset)) - go.set_position(camera_position) - go.set_rotation(camera_rot) - -- Animate the pattern by changing the z value self.time = self.time - dt go.set("/crate_selected#model", "pattern_opts.z", self.time) @@ -39,14 +20,3 @@ function update(self, dt) local w, h = window.get_size() go.set("/crate_selected#model", "screen_size", vmath.vector4(w, h, 0, 0)) end - -function on_input(self, action_id, action) - if action_id == hash("touch") then - self.yaw = self.yaw - action.dx * ROTATION_SPEED - self.pitch = self.pitch + action.dy * ROTATION_SPEED - elseif action_id == hash("wheel_up") then - self.zoom_offset = self.zoom_offset - ZOOM_SPEED - elseif action_id == hash("wheel_down") then - self.zoom_offset = self.zoom_offset + ZOOM_SPEED - end -end diff --git a/examples/material/unlit/unlit.collection b/examples/material/unlit/unlit.collection index c5d146b..3bb2bfc 100644 --- a/examples/material/unlit/unlit.collection +++ b/examples/material/unlit/unlit.collection @@ -21,7 +21,21 @@ embedded_instances { } embedded_instances { id: "camera" - data: "embedded_components {\n" + data: "components {\n" + " id: \"orbit_camera\"\n" + " component: \"/examples/render/orbit_camera/orbit_camera.script\"\n" + " properties {\n" + " id: \"zoom\"\n" + " value: \"7.0\"\n" + " type: PROPERTY_TYPE_NUMBER\n" + " }\n" + " properties {\n" + " id: \"offset\"\n" + " value: \"0.0, 0.5, 0.0\"\n" + " type: PROPERTY_TYPE_VECTOR3\n" + " }\n" + "}\n" + "embedded_components {\n" " id: \"camera\"\n" " type: \"camera\"\n" " data: \"aspect_ratio: 1.0\\n" @@ -32,11 +46,6 @@ embedded_instances { "\"\n" "}\n" "" - position { - x: 3.543836 - y: 3.619002 - z: 3.104797 - } rotation { x: -0.26769933 y: 0.3956773 @@ -44,11 +53,3 @@ embedded_instances { w: 0.8700313 } } -embedded_instances { - id: "main" - data: "components {\n" - " id: \"unlit\"\n" - " component: \"/examples/material/unlit/unlit.script\"\n" - "}\n" - "" -} diff --git a/examples/material/unlit/unlit.md b/examples/material/unlit/unlit.md index a43dabd..2b12c3b 100644 --- a/examples/material/unlit/unlit.md +++ b/examples/material/unlit/unlit.md @@ -7,8 +7,8 @@ scripts: unlit.script, unlit.vp, unlit.fp In industry-established terms, a material that is not affected by lighting is called "unlit" or "non-lit". It is used to create retro-style graphics or for effects that should not depend on lighting (headlights, lamps). -This example contains a game object with a model that has an `unlit` material applied to it. The material is assigned custom vertex and fragment shaders. To set up a perspective camera, a camera is added to the collection, which is enabled in the script via the `acquire_camera_focus` message when the collection loads. +This example contains a game object with a model that has an `unlit` material applied to it. The material is assigned custom vertex and fragment shaders. The shader is very simple and just transfers the texture color to the model. This is an excellent starting point for creating new materials and for creating effects that do not depend on lighting. The shaders are written in GLSL 1.40, which is available from Defold 1.9.2. -The unlit shader is very simple and just transfers the texture color to the model. This is an excellent starting point for creating new materials and for creating effects that do not depend on lighting. The shaders are written in GLSL 1.40, which is available from Defold 1.9.2. +To activate a perspective camera and to have camera controls, we added the `orbit_camera.script` script from the [Orbit Camera (3D)](/examples/render/orbit_camera/orbit_camera/) example. The model used in this example is from Kenney's [Train Pack](https://kenney.nl/assets/train-kit), licensed under CC0. diff --git a/examples/material/unlit/unlit.script b/examples/material/unlit/unlit.script deleted file mode 100644 index 5083eae..0000000 --- a/examples/material/unlit/unlit.script +++ /dev/null @@ -1,4 +0,0 @@ -function init(self) - msg.post("/camera", "acquire_camera_focus") - msg.post("@render:", "use_camera_projection") -end diff --git a/examples/render/orbit_camera/orbit_camera.collection b/examples/render/orbit_camera/orbit_camera.collection new file mode 100644 index 0000000..01dc364 --- /dev/null +++ b/examples/render/orbit_camera/orbit_camera.collection @@ -0,0 +1,53 @@ +name: "orbit_camera" +scale_along_z: 1 +embedded_instances { + id: "crate" + data: "embedded_components {\n" + " id: \"model\"\n" + " type: \"model\"\n" + " data: \"mesh: \\\"/assets/models/kenney_prototype-kit/crate.glb\\\"\\n" + "name: \\\"{{NAME}}\\\"\\n" + "materials {\\n" + " name: \\\"colormap\\\"\\n" + " material: \\\"/examples/material/unlit/unlit.material\\\"\\n" + " textures {\\n" + " sampler: \\\"texture0\\\"\\n" + " texture: \\\"/assets/models/kenney_prototype-kit/Textures/colormap.png\\\"\\n" + " }\\n" + "}\\n" + "\"\n" + "}\n" + "" + position { + y: -0.25 + } +} +embedded_instances { + id: "camera" + data: "components {\n" + " id: \"main\"\n" + " component: \"/examples/render/orbit_camera/orbit_camera.script\"\n" + "}\n" + "embedded_components {\n" + " id: \"camera\"\n" + " type: \"camera\"\n" + " data: \"aspect_ratio: 1.0\\n" + "fov: 0.7854\\n" + "near_z: 0.1\\n" + "far_z: 1000.0\\n" + "auto_aspect_ratio: 1\\n" + "\"\n" + "}\n" + "" + position { + x: 2.0 + y: 2.0 + z: 2.0 + } + rotation { + x: -0.27781594 + y: 0.36497167 + z: 0.11507513 + w: 0.88111955 + } +} diff --git a/examples/render/orbit_camera/orbit_camera.md b/examples/render/orbit_camera/orbit_camera.md new file mode 100644 index 0000000..08d3b97 --- /dev/null +++ b/examples/render/orbit_camera/orbit_camera.md @@ -0,0 +1,22 @@ +--- +name: Orbit Camera (3D) +title: Orbit Camera +brief: This example demonstrates how to create script to control a 3D camera with the mouse. Scroll wheel is used to zoom in and out. +scripts: orbit_camera.script +--- + +In this example, we create a script to control a 3D camera using the mouse and mouse scroll wheel. + +We added two objects to the collection: a camera (`/camera`) and an object (`/crate`) that we will explore. In the `camera` object, we added the `orbit_camera.script` - the script that controls the camera. The properties defined in the script are: +- `zoom`: the initial zoom level. +- `zoom_speed`: the speed of the zoom. +- `rotation_speed`: the speed of the rotation. +- `offset`: the offset of the camera from the origin. Use it to move the camera away from the origin. + +During `init`, the script sets up the camera projection, acquires input focus, and establishes starting values for yaw, pitch, and zoom. + +In the `update` loop, the script smoothly interpolates camera rotation and zoom (note: `vmath.lerp` is used and it doesn't depend on the delta time, so the camera will move at different speed on different devices), calculates the camera's rotation and position based on current yaw, pitch, and zoom values, and then updates the camera's position and rotation accordingly. This creates a fluid, responsive camera movement! + +The function `on_input` handles user input to control the camera. As the user moves the mouse or touches the screen, the script adjusts the yaw and pitch values, allowing the camera to rotate around its focal point. Additionally, it responds to mouse wheel input, adjusting the zoom level to move the camera closer to or further from the center point. + +The model used in this example is from Kenney's [Prototype Pack](https://kenney.nl/assets/prototype-kit), licensed under CC0. diff --git a/examples/render/orbit_camera/orbit_camera.script b/examples/render/orbit_camera/orbit_camera.script new file mode 100644 index 0000000..7e53fe6 --- /dev/null +++ b/examples/render/orbit_camera/orbit_camera.script @@ -0,0 +1,51 @@ +-- The initial zoom level +go.property("zoom", 3) +-- The speed of the zoom +go.property("zoom_speed", 0.1) +-- The speed of the rotation +go.property("rotation_speed", 0.5) +-- The offset of the camera from the origin +go.property("offset", vmath.vector3(0, 0, 0)) + +function init(self) + -- Set the camera projection to be used + msg.post("@render:", "use_camera_projection") + -- Acquire input focus to receive input events + msg.post(".", "acquire_input_focus") + + -- Initialize start values + self.yaw = go.get(".", "euler.y") + self.pitch = go.get(".", "euler.x") + self.zoom_offset = 0 + self.current_yaw = self.yaw + self.current_pitch = self.pitch + self.current_zoom = self.zoom_offset +end + +function update(self, dt) + -- Animate camera rotation and zoom + self.current_yaw = vmath.lerp(0.15, self.current_yaw, self.yaw) + self.current_pitch = vmath.lerp(0.15, self.current_pitch, self.pitch) + self.current_zoom = vmath.lerp(0.15, self.current_zoom, self.zoom_offset) + + -- Calculate rotation and position + local camera_yaw = vmath.quat_rotation_y(math.rad(self.current_yaw)) + local camera_pitch = vmath.quat_rotation_x(math.rad(self.current_pitch)) + local camera_rotation = camera_yaw * camera_pitch + local camera_position = self.offset + vmath.rotate(camera_rotation, vmath.vector3(0, 0, self.zoom + self.current_zoom)) + + -- Set camera position and rotation + go.set_position(camera_position) + go.set_rotation(camera_rotation) +end + +function on_input(self, action_id, action) + if action_id == hash("touch") then + self.yaw = self.yaw - action.dx * self.rotation_speed + self.pitch = self.pitch + action.dy * self.rotation_speed + elseif action_id == hash("wheel_up") then + self.zoom_offset = self.zoom_offset - self.zoom * self.zoom_speed + elseif action_id == hash("wheel_down") then + self.zoom_offset = self.zoom_offset + self.zoom * self.zoom_speed + end +end