Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Changelog

**New Features and Improvements**

* Tools: added a new "flythrough" camera mode for navigating viewports.
* Tools: added textual tooltips to main object types and prominent interface elements.
* Tools: crown-launcher can now be executed from any directory.

Expand Down
72 changes: 69 additions & 3 deletions samples/core/editors/level_editor/camera.lua
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ local function camera_track(self, x, y)
pan_speed = 1 / pixels_per_unit
else
-- Speed is proportional to initial distance from target
pan_speed = self._drag_start_target_distance / 800 * self._movement_speed
pan_speed = self._drag_start_target_distance / 800
end

local delta = (drag_delta.y * pan_speed) * camera_up
Expand Down Expand Up @@ -89,18 +89,70 @@ local function camera_dolly(self, x, y)
end
end

local function camera_flythrough(self, dx, dy)
if self:is_orthographic() then
return
end

local camera_pose = self:local_pose()
local camera_position = Matrix4x4.translation(camera_pose)
local camera_rotation = Matrix4x4.rotation(camera_pose)
local camera_right = Matrix4x4.x(camera_pose)

local rotation_up = Quaternion.from_axis_angle(Vector3.up(), -dx * self._rotation_speed)
local rotation_right = Quaternion.from_axis_angle(camera_right, dy * self._rotation_speed)

local rotate_delta = Quaternion.multiply(rotation_up, rotation_right)
local new_rotation = Quaternion.multiply(rotate_delta, camera_rotation)
local pose = Matrix4x4.from_quaternion(new_rotation)
Matrix4x4.set_translation(pose, camera_position)

local tr = SceneGraph.instance(self._sg, self._unit)
SceneGraph.set_local_pose(self._sg, tr, pose)
end

local function camera_flythrough_action(self, dt, actions)
if self:is_orthographic() then
return
end

local speed = self._movement_speed * dt

local camera_pose = self:local_pose()
local camera_position = Matrix4x4.translation(camera_pose)
local camera_rotation = Matrix4x4.rotation(camera_pose)
local camera_right = Matrix4x4.x(camera_pose)
local camera_forward = Matrix4x4.y(camera_pose)
local camera_up = Matrix4x4.z(camera_pose)

local new_position = Vector3.zero()
if actions.forward then new_position = new_position + camera_forward * speed end
if actions.back then new_position = new_position - camera_forward * speed end
if actions.left then new_position = new_position - camera_right * speed end
if actions.right then new_position = new_position + camera_right * speed end
if actions.up then new_position = new_position + camera_up * speed end
if actions.down then new_position = new_position - camera_up * speed end

local tr = SceneGraph.instance(self._sg, self._unit)
Matrix4x4.set_translation(camera_pose, camera_position + new_position)
SceneGraph.set_local_pose(self._sg, tr, camera_pose)
end

Camera = class(Camera)

function Camera:init(world, unit)
self._world = world
self._unit = unit
self._sg = World.scene_graph(world)
self._movement_speed = 1
self._movement_speed = 30
self._rotation_speed = 0.002
self._orthographic_size = 10
self._target_distance = 10

self.actions = {}
self._move_callback = nil
self._move_callback_delta = nil
self._action_callback = nil

self._drag_start_cursor_xy = Vector3Box()
self._drag_start_camera_pose = Matrix4x4Box()
Expand Down Expand Up @@ -155,7 +207,7 @@ function Camera:is_orthographic()
end

function Camera:mouse_wheel(delta)
self._movement_speed = math.max(0.001, self._movement_speed + delta * 0.005)
self._movement_speed = math.max(0.001, self._movement_speed + delta * 0.1)
end

function Camera:camera_ray(x, y)
Expand Down Expand Up @@ -230,9 +282,15 @@ function Camera:update(dt, x, y, dx, dy)
if dx ~= 0 or dy ~= 0 then
if self._move_callback ~= nil then
self._move_callback(self, x, y)
elseif self._move_callback_delta then
self._move_callback_delta(self, dx, dy)
end
end

if self._action_callback then
self._action_callback(self, dt, self.actions)
end

if not Matrix4x4.equal(old_pose, self:local_pose())
or old_target_distance ~= self._target_distance
or old_orthographic_size ~= self._orthographic_size then
Expand Down Expand Up @@ -270,9 +328,17 @@ function Camera:set_mode(mode, x, y)
self._drag_start_orthographic_size = self._orthographic_size
self._drag_start_target_distance = self._target_distance

self._move_callback = nil
self._move_callback_delta = nil
self._action_callback = nil
self.actions = {}

if mode == "tumble" then self._move_callback = camera_tumble
elseif mode == "track" then self._move_callback = camera_track
elseif mode == "dolly" then self._move_callback = camera_dolly
elseif mode == "flythrough" then
self._move_callback_delta = camera_flythrough
self._action_callback = camera_flythrough_action
elseif mode == "idle" then self._move_callback = nil
end
end
Expand Down
82 changes: 77 additions & 5 deletions tools/widgets/editor_view.vala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public class EditorView : Gtk.EventBox

public Gtk.Allocation _allocation;
public uint _resize_timer_id;
public uint _enable_accels_id;
public uint _tick_callback_id;

public bool _mouse_left;
public bool _mouse_middle;
Expand Down Expand Up @@ -55,6 +57,8 @@ public class EditorView : Gtk.EventBox
case Gdk.Key.a: return "a";
case Gdk.Key.s: return "s";
case Gdk.Key.d: return "d";
case Gdk.Key.q: return "q";
case Gdk.Key.e: return "e";
case Gdk.Key.Control_L: return "ctrl_left";
case Gdk.Key.Shift_L: return "shift_left";
case Gdk.Key.Alt_L: return "alt_left";
Expand Down Expand Up @@ -82,6 +86,8 @@ public class EditorView : Gtk.EventBox

_allocation = { 0, 0, 0, 0 };
_resize_timer_id = 0;
_enable_accels_id = 0;
_tick_callback_id = 0;

_mouse_left = false;
_mouse_middle = false;
Expand All @@ -95,6 +101,8 @@ public class EditorView : Gtk.EventBox
_keys[Gdk.Key.a] = false;
_keys[Gdk.Key.s] = false;
_keys[Gdk.Key.d] = false;
_keys[Gdk.Key.q] = false;
_keys[Gdk.Key.e] = false;
_keys[Gdk.Key.Control_L] = false;
_keys[Gdk.Key.Shift_L] = false;
_keys[Gdk.Key.Alt_L] = false;
Expand Down Expand Up @@ -242,8 +250,21 @@ public class EditorView : Gtk.EventBox
if (camera_modifier_pressed()) {
if (!_mouse_left || !_mouse_middle || !_mouse_right)
_buffer.append("LevelEditor:camera_drag_start('idle')");
} else if (!_mouse_middle) {
} else if (!_mouse_middle || !_mouse_right) {
_buffer.append("LevelEditor:camera_drag_start('idle')");

bool is_flying = _tick_callback_id > 0;

if (!_mouse_right && is_flying) {
// Wait a little to prevent camera movement keys
// from activating unwanted accelerators.
_enable_accels_id = GLib.Timeout.add_full(GLib.Priority.DEFAULT, 300, on_enable_accels);

if (_tick_callback_id != 0) {
remove_tick_callback(_tick_callback_id);
_tick_callback_id = 0;
}
}
}

if (_buffer.len != 0) {
Expand Down Expand Up @@ -280,6 +301,16 @@ public class EditorView : Gtk.EventBox
_buffer.append("LevelEditor:camera_drag_start('dolly')");
} else if (_mouse_middle) {
_buffer.append("LevelEditor:camera_drag_start('tumble')");
} else if (_mouse_right) {
_buffer.append("LevelEditor:camera_drag_start('flythrough')");

if (_tick_callback_id == 0)
_tick_callback_id = add_tick_callback(on_tick);

if (_enable_accels_id > 0)
GLib.Source.remove(_enable_accels_id);

((LevelEditorApplication)GLib.Application.get_default()).set_conflicting_accels(false);
}

if (button == Gdk.BUTTON_PRIMARY)
Expand Down Expand Up @@ -307,9 +338,23 @@ public class EditorView : Gtk.EventBox
_buffer.append("LevelEditor:key_down(\"move_left\")");

if (_keys.has_key(keyval)) {
if (!_keys[keyval])
if (!_keys[keyval]) {
_buffer.append(LevelEditorApi.key_down(key_to_string(keyval)));

if (keyval == Gdk.Key.w)
_buffer.append("LevelEditor._camera.actions.forward = true;");
if (keyval == Gdk.Key.s)
_buffer.append("LevelEditor._camera.actions.back = true;");
if (keyval == Gdk.Key.a)
_buffer.append("LevelEditor._camera.actions.left = true;");
if (keyval == Gdk.Key.d)
_buffer.append("LevelEditor._camera.actions.right = true;");
if (keyval == Gdk.Key.q)
_buffer.append("LevelEditor._camera.actions.up = true;");
if (keyval == Gdk.Key.e)
_buffer.append("LevelEditor._camera.actions.down = true;");
}

_keys[keyval] = true;
}

Expand All @@ -324,9 +369,23 @@ public class EditorView : Gtk.EventBox
public void on_key_released(uint keyval, uint keycode, Gdk.ModifierType state)
{
if (_keys.has_key(keyval)) {
if (_keys[keyval])
if (_keys[keyval]) {
_buffer.append(LevelEditorApi.key_up(key_to_string(keyval)));

if (keyval == Gdk.Key.w)
_buffer.append("LevelEditor._camera.actions.forward = false");
if (keyval == Gdk.Key.s)
_buffer.append("LevelEditor._camera.actions.back = false");
if (keyval == Gdk.Key.a)
_buffer.append("LevelEditor._camera.actions.left = false");
if (keyval == Gdk.Key.d)
_buffer.append("LevelEditor._camera.actions.right = false");
if (keyval == Gdk.Key.q)
_buffer.append("LevelEditor._camera.actions.up = false");
if (keyval == Gdk.Key.e)
_buffer.append("LevelEditor._camera.actions.down = false");
}

_keys[keyval] = false;
}

Expand Down Expand Up @@ -356,8 +415,8 @@ public class EditorView : Gtk.EventBox

public void on_scroll(double dx, double dy)
{
if (camera_modifier_pressed()) {
_runtime.send_script(LevelEditorApi.mouse_wheel(dy));
if (_keys[Gdk.Key.Shift_L]) {
_runtime.send_script(LevelEditorApi.mouse_wheel(-dy));
} else {
_runtime.send_script("LevelEditor:camera_drag_start_relative('dolly')");
_runtime.send_script("LevelEditor._camera:update(1,0,%.17f,1,1)".printf(-dy * 32.0));
Expand Down Expand Up @@ -422,6 +481,19 @@ public class EditorView : Gtk.EventBox
{
this.grab_focus();
}

public bool on_tick(Gtk.Widget widget, Gdk.FrameClock frame_clock)
{
_runtime.send(DeviceApi.frame());
return GLib.Source.CONTINUE;
}

public bool on_enable_accels()
{
((LevelEditorApplication)GLib.Application.get_default()).set_conflicting_accels(true);
_enable_accels_id = 0;
return GLib.Source.REMOVE;
}
}

} /* namespace Crown */