797 lines
32 KiB
Plaintext
797 lines
32 KiB
Plaintext
Gizmo_Space :: enum {
|
|
LOCAL;
|
|
WORLD;
|
|
}
|
|
|
|
Transform_Axis :: enum {
|
|
NONE;
|
|
UP;
|
|
FORWARD;
|
|
RIGHT;
|
|
CENTER;
|
|
}
|
|
|
|
Transform_Type :: enum {
|
|
TRANSLATION;
|
|
ROTATION;
|
|
SCALE;
|
|
}
|
|
|
|
Editor_Undo_Type :: enum {
|
|
TRANSFORM_CHANGE;
|
|
}
|
|
|
|
Editor_Undo :: struct {
|
|
type: Editor_Undo_Type;
|
|
|
|
entity: *Entity;
|
|
|
|
union {
|
|
transform : struct {
|
|
position: Vector3;
|
|
rotation: Quaternion;
|
|
scale: Vector3;
|
|
};
|
|
}
|
|
}
|
|
|
|
push_transform_undo :: (e: *Entity) {
|
|
undo: Editor_Undo;
|
|
undo.type = .TRANSFORM_CHANGE;
|
|
undo.entity = e;
|
|
undo.transform.position = e.transform.position;
|
|
undo.transform.scale = e.transform.scale;
|
|
undo.transform.rotation = e.transform.orientation;
|
|
array_add(*engine.editor.undo_stack, undo);
|
|
}
|
|
|
|
undo :: () {
|
|
if engine.editor.undo_stack.count == 0 return;
|
|
|
|
print("UNDO!\n");
|
|
undo := engine.editor.undo_stack[engine.editor.undo_stack.count-1];
|
|
|
|
if undo.type == {
|
|
case .TRANSFORM_CHANGE; {
|
|
set_position_rotation_scale(undo.entity, undo.transform.position, undo.transform.rotation, undo.transform.scale);
|
|
}
|
|
}
|
|
|
|
engine.editor.undo_stack.count -= 1;
|
|
}
|
|
|
|
Buffer_Info :: struct {
|
|
buffer: Buffer_Handle;
|
|
vertex_count: u32;
|
|
}
|
|
|
|
Transform_Gizmo :: struct {
|
|
active: bool = false;
|
|
transform_type: Transform_Type = .TRANSLATION;
|
|
space: Gizmo_Space;
|
|
|
|
selected_axis: Transform_Axis;
|
|
|
|
first_hit_position: Vector3;
|
|
actual_entity_position: Vector3; // The actual position of the selected entity. Used for snapping
|
|
actual_entity_scale: Vector3; // The actual position of the selected entity. Used for snapping
|
|
last_circle_dir: Vector3;
|
|
|
|
// Settings
|
|
snap_to_grid: bool;
|
|
|
|
clicked: bool;
|
|
|
|
transform: Transform;
|
|
|
|
can_use: bool = true;
|
|
|
|
center_model_buffer: Buffer_Handle;
|
|
up_model_buffer: Buffer_Handle;
|
|
forward_model_buffer: Buffer_Handle;
|
|
right_model_buffer: Buffer_Handle;
|
|
|
|
color_center: Color;
|
|
color_up: Color;
|
|
color_forward: Color;
|
|
color_right: Color;
|
|
|
|
color_center_buffer: Buffer_Handle;
|
|
color_up_buffer: Buffer_Handle;
|
|
color_forward_buffer: Buffer_Handle;
|
|
color_right_buffer: Buffer_Handle;
|
|
|
|
pipeline: Pipeline_State_Handle;
|
|
|
|
uniform_gizmo_scale: float;
|
|
}
|
|
|
|
Editor :: struct {
|
|
focused_widget: *UI_Box;
|
|
|
|
show_menu: bool;
|
|
should_check_entities: bool;
|
|
camera: Camera;
|
|
transform_gizmo: Transform_Gizmo;
|
|
selected_entities: [..] *Entity;
|
|
selected_entity_transforms : Table(*Entity, Transform);
|
|
|
|
mouse_viewport_state: Interaction_State;
|
|
last_right_mouse_click_time: float;
|
|
|
|
menu_position: Vector2;
|
|
hide_ui: bool;
|
|
|
|
icons : struct {
|
|
play: Texture_Handle;
|
|
stop: Texture_Handle;
|
|
}
|
|
|
|
undo_stack: [..] Editor_Undo;
|
|
}
|
|
|
|
init_editor :: () {
|
|
aspect_ratio := cast(float)engine.renderer.render_target_width / cast(float)engine.renderer.render_target_height;
|
|
engine.editor.camera = create_perspective_camera(.{0, 10, -10}, yaw=0, pitch=-40, roll=0.0, fov=40, z_far=100.0, aspect=aspect_ratio);
|
|
|
|
init_transform_gizmo();
|
|
|
|
engine.editor.icons.play = create_texture(engine.renderer, "../modules/Coven/assets/textures/ui_icons/play.png", false);
|
|
engine.editor.icons.stop = create_texture(engine.renderer, "../modules/Coven/assets/textures/ui_icons/stop.png", false);
|
|
}
|
|
|
|
init_transform_gizmo :: () {
|
|
gizmo : Transform_Gizmo;
|
|
gizmo.selected_axis = .NONE;
|
|
gizmo.transform = create_identity_transform();
|
|
|
|
gizmo.center_model_buffer = create_constant_buffer(engine.renderer, null, size_of(Matrix4), mappable=true);
|
|
gizmo.up_model_buffer = create_constant_buffer(engine.renderer, null, size_of(Matrix4), mappable=true);
|
|
gizmo.forward_model_buffer = create_constant_buffer(engine.renderer, null, size_of(Matrix4), mappable=true);
|
|
gizmo.right_model_buffer = create_constant_buffer(engine.renderer, null, size_of(Matrix4), mappable=true);
|
|
|
|
gizmo.color_center = Color.{1,1,1,1};
|
|
gizmo.color_up = Color.{0,1,0,1};
|
|
gizmo.color_forward = Color.{0,0,1,1};
|
|
gizmo.color_right = Color.{1,0,0,1};
|
|
|
|
gizmo.color_center_buffer = create_constant_buffer(engine.renderer, *gizmo.color_center, size_of(Vector4), mappable=true);
|
|
gizmo.color_up_buffer = create_constant_buffer(engine.renderer, *gizmo.color_up, size_of(Vector4), mappable=true);
|
|
gizmo.color_forward_buffer = create_constant_buffer(engine.renderer, *gizmo.color_forward, size_of(Vector4), mappable=true);
|
|
gizmo.color_right_buffer = create_constant_buffer(engine.renderer, *gizmo.color_right, size_of(Vector4), mappable=true);
|
|
|
|
engine.editor.transform_gizmo = gizmo;
|
|
|
|
// Transform gizmo shader
|
|
{
|
|
vs := create_vertex_shader_from_source(engine.renderer, "Transform Gizmo", TRANSFORM_GIZMO_SHADER, "VS", mesh_data_types = .[.POSITION]);
|
|
ps := create_pixel_shader_from_source(engine.renderer, "Transform Gizmo", TRANSFORM_GIZMO_SHADER, "PS");
|
|
|
|
engine.editor.transform_gizmo.pipeline = create_pipeline_state(engine.renderer, vs, ps, blend_type=.OPAQUE);
|
|
}
|
|
}
|
|
|
|
update_transform_gizmo :: (ray: Ray, mouse_position: Vector2) -> bool {
|
|
if engine.editor.selected_entities.count != 1 {
|
|
// Hardcode to world + translation when selecting multiple entities
|
|
// We currently don't need to do anything but translate in world space, when having multiple entities selected
|
|
engine.editor.transform_gizmo.space = .WORLD;
|
|
engine.editor.transform_gizmo.transform_type = .TRANSLATION;
|
|
} else {
|
|
if key_down(.TAB) {
|
|
if engine.editor.transform_gizmo.space == {
|
|
case .WORLD;
|
|
engine.editor.transform_gizmo.space = .LOCAL;
|
|
case .LOCAL;
|
|
engine.editor.transform_gizmo.space = .WORLD;
|
|
}
|
|
}
|
|
}
|
|
|
|
position : Vector3;
|
|
for e: engine.editor.selected_entities {
|
|
position += e.transform.position;
|
|
}
|
|
|
|
position /= cast(float)engine.editor.selected_entities.count;
|
|
|
|
selected_entity := engine.editor.selected_entities[0];
|
|
|
|
if engine.editor.transform_gizmo.space == {
|
|
case .WORLD;
|
|
set_rotation(*engine.editor.transform_gizmo.transform, .{0,0,0,1});
|
|
case .LOCAL;
|
|
set_rotation(*engine.editor.transform_gizmo.transform, selected_entity.transform.orientation);
|
|
}
|
|
|
|
engine.editor.transform_gizmo.actual_entity_position = position;
|
|
|
|
if engine.editor.transform_gizmo.transform_type == {
|
|
case .TRANSLATION;
|
|
if !key_pressed(.MOUSE_LEFT) {
|
|
if key_pressed(.CTRL) {
|
|
engine.editor.transform_gizmo.selected_axis = .NONE;
|
|
table_reset(*engine.editor.selected_entity_transforms);
|
|
} else {
|
|
selected_axis, t := intersect_translation_gizmo(ray);
|
|
engine.editor.transform_gizmo.selected_axis = selected_axis;
|
|
table_reset(*engine.editor.selected_entity_transforms);
|
|
}
|
|
} else if engine.editor.transform_gizmo.can_use && engine.editor.transform_gizmo.selected_axis != .NONE {
|
|
first_update := key_down(.MOUSE_LEFT);
|
|
|
|
if first_update {
|
|
|
|
for engine.editor.selected_entities {
|
|
push_transform_undo(it);
|
|
table_add(*engine.editor.selected_entity_transforms, it, it.transform);
|
|
}
|
|
}
|
|
|
|
// Move the currently selected entity along the selected axis
|
|
axis_vec : Vector3;
|
|
|
|
if engine.editor.transform_gizmo.space == {
|
|
case .WORLD; {
|
|
if engine.editor.transform_gizmo.selected_axis == {
|
|
case .UP;
|
|
axis_vec.y = 1;
|
|
case .FORWARD;
|
|
axis_vec.z = 1;
|
|
case .RIGHT;
|
|
axis_vec.x = 1;
|
|
}
|
|
}
|
|
case .LOCAL; {
|
|
if engine.editor.transform_gizmo.selected_axis == {
|
|
case .UP;
|
|
axis_vec = get_up(selected_entity.transform);
|
|
case .FORWARD;
|
|
axis_vec = get_forward(selected_entity.transform);
|
|
case .RIGHT;
|
|
axis_vec = get_right(selected_entity.transform);
|
|
}
|
|
}
|
|
}
|
|
|
|
r1 : Ray;
|
|
r1.origin = position;
|
|
r1.direction = axis_vec;
|
|
|
|
r2 := normalized_screen_to_ray(*engine.editor.camera, mouse_position);
|
|
|
|
d, t1, t2 := closest_distance_between_rays(r1, r2);
|
|
|
|
new_position := r1.origin + r1.direction * t1;
|
|
|
|
if first_update {
|
|
engine.editor.transform_gizmo.first_hit_position = new_position;
|
|
}
|
|
|
|
position_change := new_position - engine.editor.transform_gizmo.first_hit_position;
|
|
|
|
for e: engine.editor.selected_entities {
|
|
found, transform := table_find_new(*engine.editor.selected_entity_transforms, e);
|
|
assert(found);
|
|
|
|
entity_position := transform.position + position_change;
|
|
if engine.editor.transform_gizmo.snap_to_grid {
|
|
snap_interval := Vector3.{1,1,1};
|
|
entity_position.x -= fmod_cycling(entity_position.x - e.snap_offset.x, snap_interval.x);// + selected_entity.snap_offset.x;
|
|
entity_position.y -= fmod_cycling(entity_position.y - e.snap_offset.y, snap_interval.y);// + selected_entity.snap_offset.y;
|
|
entity_position.z -= fmod_cycling(entity_position.z - e.snap_offset.z, snap_interval.z);// + selected_entity.snap_offset.z;
|
|
} else if selected_entity.flags & Entity_Flags.SNAP_TO_GRID {
|
|
entity_position.x -= fmod_cycling(entity_position.x - e.snap_offset.x, e.snap_intervals.x);// + selected_entity.snap_offset.x;
|
|
entity_position.y -= fmod_cycling(entity_position.y - e.snap_offset.y, e.snap_intervals.y);// + selected_entity.snap_offset.y;
|
|
entity_position.z -= fmod_cycling(entity_position.z - e.snap_offset.z, e.snap_intervals.z);// + selected_entity.snap_offset.z;
|
|
}
|
|
|
|
e.transform.position = entity_position;
|
|
e.transform.dirty = true;
|
|
}
|
|
|
|
entity_position := engine.editor.transform_gizmo.actual_entity_position + position_change;
|
|
engine.editor.transform_gizmo.actual_entity_position = entity_position;
|
|
set_position(*engine.editor.transform_gizmo.transform, entity_position);
|
|
}
|
|
|
|
color_up := engine.editor.transform_gizmo.color_up;
|
|
color_forward := engine.editor.transform_gizmo.color_forward;
|
|
color_right := engine.editor.transform_gizmo.color_right;
|
|
|
|
if engine.editor.transform_gizmo.selected_axis == {
|
|
case .NONE;
|
|
case .UP;
|
|
color_up = Color.{1,1,0,1};
|
|
case .FORWARD;
|
|
color_forward = Color.{1,1,0,1};
|
|
case .RIGHT;
|
|
color_right = Color.{1,1,0,1};
|
|
}
|
|
|
|
upload_data_to_buffer(engine.renderer, engine.editor.transform_gizmo.color_up_buffer, *color_up, size_of(Color));
|
|
upload_data_to_buffer(engine.renderer, engine.editor.transform_gizmo.color_forward_buffer, *color_forward, size_of(Color));
|
|
upload_data_to_buffer(engine.renderer, engine.editor.transform_gizmo.color_right_buffer, *color_right, size_of(Color));
|
|
case .ROTATION;
|
|
selected_entity := engine.editor.selected_entities[0];
|
|
|
|
if !engine.editor.transform_gizmo.clicked {
|
|
selected_axis, point := intersect_rotation_gizmo(ray);
|
|
engine.editor.transform_gizmo.selected_axis = selected_axis;
|
|
|
|
if engine.editor.transform_gizmo.selected_axis != .NONE && key_down(.MOUSE_LEFT) {
|
|
engine.editor.transform_gizmo.clicked = true;
|
|
engine.editor.transform_gizmo.last_circle_dir = normalize(point - selected_entity.transform.position);
|
|
}
|
|
} else if !key_pressed(.MOUSE_LEFT) {
|
|
engine.editor.transform_gizmo.clicked = false;
|
|
} else {
|
|
if key_down(.MOUSE_LEFT) {
|
|
push_transform_undo(engine.editor.selected_entities[0]);
|
|
}
|
|
|
|
direction : Vector3;
|
|
if engine.editor.transform_gizmo.selected_axis == {
|
|
case .UP;
|
|
direction = .{0,1,0};
|
|
case .FORWARD;
|
|
direction = .{0,0,1};
|
|
case .RIGHT;
|
|
direction = .{1,0,0};
|
|
}
|
|
direction = rotate(direction, engine.editor.transform_gizmo.transform.orientation);
|
|
|
|
// Find the rotation
|
|
circle : Circle;
|
|
circle.radius = engine.editor.transform_gizmo.uniform_gizmo_scale;
|
|
circle.center = selected_entity.transform.position;
|
|
circle.orientation = direction;
|
|
|
|
distance, point := closest_distance_ray_circle(ray, circle);
|
|
new_dir := normalize(point - selected_entity.transform.position);
|
|
|
|
dotp := dot(engine.editor.transform_gizmo.last_circle_dir, new_dir);
|
|
angle := acos(clamp(dotp, -1.0, 1.0));
|
|
cp := cross(engine.editor.transform_gizmo.last_circle_dir, new_dir);
|
|
|
|
if dot(direction, cp) < 0 {
|
|
angle *= -1.0;
|
|
}
|
|
|
|
q : Quaternion;
|
|
set_from_axis_and_angle(*q, direction, angle);
|
|
|
|
selected_entity.transform.orientation = q * selected_entity.transform.orientation;
|
|
update_matrix(*selected_entity.transform);
|
|
|
|
set_rotation(*engine.editor.transform_gizmo.transform, selected_entity.transform.orientation);
|
|
|
|
engine.editor.transform_gizmo.last_circle_dir = new_dir;
|
|
}
|
|
|
|
color_up := engine.editor.transform_gizmo.color_up;
|
|
color_forward := engine.editor.transform_gizmo.color_forward;
|
|
color_right := engine.editor.transform_gizmo.color_right;
|
|
|
|
if engine.editor.transform_gizmo.selected_axis == {
|
|
case .NONE;
|
|
case .UP;
|
|
color_up = Color.{1,1,0,1};
|
|
case .FORWARD;
|
|
color_forward = Color.{1,1,0,1};
|
|
case .RIGHT;
|
|
color_right = Color.{1,1,0,1};
|
|
}
|
|
|
|
upload_data_to_buffer(engine.renderer, engine.editor.transform_gizmo.color_up_buffer, *color_up, size_of(Color));
|
|
upload_data_to_buffer(engine.renderer, engine.editor.transform_gizmo.color_forward_buffer, *color_forward, size_of(Color));
|
|
upload_data_to_buffer(engine.renderer, engine.editor.transform_gizmo.color_right_buffer, *color_right, size_of(Color));
|
|
case .SCALE;
|
|
if !key_pressed(.MOUSE_LEFT) {
|
|
selected_axis, t := intersect_scale_gizmo(ray);
|
|
engine.editor.transform_gizmo.selected_axis = selected_axis;
|
|
} else if engine.editor.transform_gizmo.selected_axis != .NONE {
|
|
selected_entity := engine.editor.selected_entities[0];
|
|
first_update := key_down(.MOUSE_LEFT);
|
|
|
|
if first_update {
|
|
push_transform_undo(engine.editor.selected_entities[0]);
|
|
engine.editor.transform_gizmo.actual_entity_position = selected_entity.transform.position;
|
|
engine.editor.transform_gizmo.actual_entity_scale = selected_entity.transform.scale;
|
|
}
|
|
|
|
// Move the currently selected entity along the selected axis
|
|
axis_vec : Vector3;
|
|
|
|
if engine.editor.transform_gizmo.selected_axis == {
|
|
case .UP;
|
|
axis_vec.y = 1;
|
|
case .FORWARD;
|
|
axis_vec.z = -1;
|
|
case .RIGHT;
|
|
axis_vec.x = 1;
|
|
case .CENTER; axis_vec = .{1,1,1};
|
|
}
|
|
|
|
r1 : Ray;
|
|
r1.origin = selected_entity.transform.position;
|
|
r1.direction = rotate(axis_vec, engine.editor.transform_gizmo.transform.orientation);
|
|
// Shoot a ray from screen to world
|
|
mouse_position : Vector2;
|
|
mouse_position.x = xx engine.input.mouse.x;
|
|
mouse_position.y = xx engine.input.mouse.y;
|
|
|
|
r2 := screen_to_ray(*engine.editor.camera, mouse_position);
|
|
|
|
d, t1, t2 := closest_distance_between_rays(r1, r2);
|
|
new_position := r1.origin + r1.direction * t1;
|
|
|
|
if first_update {
|
|
engine.editor.transform_gizmo.first_hit_position = new_position;
|
|
}
|
|
|
|
position_change := new_position - engine.editor.transform_gizmo.first_hit_position;
|
|
|
|
// @Robustness: Why though?
|
|
position_change.z *= -1.0;
|
|
position_change.y *= -1.0;
|
|
|
|
scale_speed := ifx key_pressed(.SHIFT) then 4.0 else 1.0;
|
|
|
|
if selected_entity.flags & .UNIFORM_SCALE {
|
|
current_scale := selected_entity.transform.scale;
|
|
|
|
if engine.editor.transform_gizmo.selected_axis == {
|
|
case .UP; {
|
|
current_scale.y = engine.editor.transform_gizmo.actual_entity_scale.y + position_change.y * scale_speed;
|
|
position_change.x = 0;
|
|
position_change.z = 0;
|
|
}
|
|
case .FORWARD; {
|
|
current_scale.z = engine.editor.transform_gizmo.actual_entity_scale.z + position_change.z * scale_speed;
|
|
position_change.x = 0;
|
|
position_change.y = 0;
|
|
}
|
|
case .RIGHT; {
|
|
current_scale.x = engine.editor.transform_gizmo.actual_entity_scale.x + position_change.x * scale_speed;
|
|
position_change.y = 0;
|
|
position_change.z = 0;
|
|
}
|
|
case .CENTER; {
|
|
current_scale.x = engine.editor.transform_gizmo.actual_entity_scale.x + position_change.x * scale_speed;
|
|
current_scale.y = current_scale.x; // @Incomplete: This is most definitely wrong!
|
|
current_scale.z = current_scale.x;
|
|
|
|
position_change.y = 0;
|
|
position_change.z = 0;
|
|
}
|
|
}
|
|
|
|
set_scale(*selected_entity.transform, current_scale);
|
|
|
|
} else {
|
|
if engine.editor.transform_gizmo.selected_axis == {
|
|
case .UP;
|
|
position_change.x = 0;
|
|
position_change.z = 0;
|
|
case .FORWARD;
|
|
position_change.x = 0;
|
|
position_change.y = 0;
|
|
case .RIGHT;
|
|
position_change.y = 0;
|
|
position_change.z = 0;
|
|
}
|
|
|
|
entity_scale := engine.editor.transform_gizmo.actual_entity_scale + position_change * scale_speed;
|
|
set_scale(*selected_entity.transform, entity_scale);
|
|
}
|
|
}
|
|
|
|
color_up := engine.editor.transform_gizmo.color_up;
|
|
color_forward := engine.editor.transform_gizmo.color_forward;
|
|
color_right := engine.editor.transform_gizmo.color_right;
|
|
color_center := Color.{1,1,1,1};
|
|
|
|
if engine.editor.transform_gizmo.selected_axis == {
|
|
case .NONE;
|
|
case .UP;
|
|
color_up = Color.{1,1,0,1};
|
|
case .FORWARD;
|
|
color_forward = Color.{1,1,0,1};
|
|
case .RIGHT;
|
|
color_right = Color.{1,1,0,1};
|
|
case .CENTER;
|
|
color_center = Color.{1,1,0,1};
|
|
}
|
|
|
|
upload_data_to_buffer(engine.renderer, engine.editor.transform_gizmo.color_center_buffer, *color_center, size_of(Color));
|
|
upload_data_to_buffer(engine.renderer, engine.editor.transform_gizmo.color_up_buffer, *color_up, size_of(Color));
|
|
upload_data_to_buffer(engine.renderer, engine.editor.transform_gizmo.color_forward_buffer, *color_forward, size_of(Color));
|
|
upload_data_to_buffer(engine.renderer, engine.editor.transform_gizmo.color_right_buffer, *color_right, size_of(Color));
|
|
}
|
|
|
|
return engine.editor.transform_gizmo.selected_axis != .NONE;
|
|
}
|
|
|
|
intersect_translation_gizmo :: (ray: Ray) -> Transform_Axis, float {
|
|
transform := engine.editor.transform_gizmo.transform;
|
|
origin := transform.position;
|
|
orientation := transform.orientation;
|
|
|
|
up_ray: Ray;
|
|
up_ray.origin = origin;
|
|
up_ray.direction = rotate(.{0,1,0}, orientation);
|
|
right_ray: Ray;
|
|
right_ray.origin = origin;
|
|
right_ray.direction = rotate(.{1,0,0}, orientation);
|
|
forward_ray: Ray;
|
|
forward_ray.origin = origin;
|
|
forward_ray.direction = rotate(.{0,0,1}, orientation);
|
|
|
|
max_dist :: 0.5;
|
|
axis : Transform_Axis = .NONE;
|
|
closest := 100000000.0;
|
|
t := 0.0;
|
|
|
|
GIZMO_LENGTH := 1.3 * engine.editor.transform_gizmo.uniform_gizmo_scale;
|
|
|
|
du, tu1, tu2 := closest_distance_between_rays(ray, up_ray);
|
|
if du <= max_dist && du < closest && tu1 >= 0.0 && tu2 < GIZMO_LENGTH {
|
|
// Make sure that the point is not behind the gizmo
|
|
if dot(up_ray.direction, normalize((ray.origin + ray.direction * tu1) - origin)) >= 0.0 {
|
|
closest = du;
|
|
t = tu1;
|
|
axis = .UP;
|
|
}
|
|
}
|
|
|
|
dr, tr1, tr2 := closest_distance_between_rays(ray, right_ray);
|
|
if dr <= max_dist && dr < closest && tr1 >= 0.0 && tr2 < GIZMO_LENGTH {
|
|
// Make sure that the point is not behind the gizmo
|
|
if dot(right_ray.direction, normalize((ray.origin + ray.direction * tr1) - origin)) >= 0.0 {
|
|
closest = dr;
|
|
t = tr1;
|
|
axis = .RIGHT;
|
|
}
|
|
}
|
|
|
|
df, tf1, tf2 := closest_distance_between_rays(ray, forward_ray);
|
|
if df <= max_dist && df < closest && tf1 >= 0.0 && tf2 < GIZMO_LENGTH {
|
|
// Make sure that the point is not behind the gizmo
|
|
if dot(forward_ray.direction, normalize((ray.origin + ray.direction * tf1) - origin)) >= 0.0 {
|
|
closest = df;
|
|
t = tf1;
|
|
axis = .FORWARD;
|
|
}
|
|
}
|
|
|
|
return axis, t;
|
|
}
|
|
|
|
intersect_scale_gizmo :: (ray: Ray) -> Transform_Axis, float {
|
|
transform := engine.editor.transform_gizmo.transform;
|
|
origin := transform.position;
|
|
orientation := transform.orientation;
|
|
|
|
radius := engine.editor.transform_gizmo.uniform_gizmo_scale * 0.15;
|
|
if ray_sphere_intersect(ray, origin, radius) {
|
|
return .CENTER, 0.0;
|
|
}
|
|
|
|
up_ray: Ray;
|
|
up_ray.origin = origin;
|
|
up_ray.direction = rotate(.{0,1,0}, orientation);
|
|
right_ray: Ray;
|
|
right_ray.origin = origin;
|
|
right_ray.direction = rotate(.{1,0,0}, orientation);
|
|
forward_ray: Ray;
|
|
forward_ray.origin = origin;
|
|
forward_ray.direction = rotate(.{0,0,1}, orientation);
|
|
|
|
max_dist :: 0.5;
|
|
axis : Transform_Axis = .NONE;
|
|
closest := 100000000.0;
|
|
t := 0.0;
|
|
|
|
du, tu1, tu2 := closest_distance_between_rays(ray, up_ray);
|
|
if du <= max_dist && du < closest && tu1 >= 0.0 {
|
|
// Make sure that the point is not behind the gizmo
|
|
if dot(up_ray.direction, normalize((ray.origin + ray.direction * tu1) - origin)) >= 0.0 {
|
|
closest = du;
|
|
t = tu1;
|
|
axis = .UP;
|
|
}
|
|
}
|
|
|
|
dr, tr1, tr2 := closest_distance_between_rays(ray, right_ray);
|
|
if dr <= max_dist && dr < closest && tr1 >= 0.0 {
|
|
// Make sure that the point is not behind the gizmo
|
|
if dot(right_ray.direction, normalize((ray.origin + ray.direction * tr1) - origin)) >= 0.0 {
|
|
closest = dr;
|
|
t = tr1;
|
|
axis = .RIGHT;
|
|
}
|
|
}
|
|
|
|
df, tf1, tf2 := closest_distance_between_rays(ray, forward_ray);
|
|
if df <= max_dist && df < closest && tf1 >= 0.0 {
|
|
// Make sure that the point is not behind the gizmo
|
|
if dot(forward_ray.direction, normalize((ray.origin + ray.direction * tf1) - origin)) >= 0.0 {
|
|
closest = df;
|
|
t = tf1;
|
|
axis = .FORWARD;
|
|
}
|
|
}
|
|
|
|
return axis, t;
|
|
}
|
|
|
|
Circle :: struct {
|
|
center: Vector3;
|
|
radius: float;
|
|
orientation: Vector3;
|
|
}
|
|
|
|
closest_distance_ray_circle :: (r: Ray, c: Circle) -> float, Vector3 {
|
|
plane_p := c.center;
|
|
plane_orientation := c.orientation;
|
|
|
|
success, t := ray_plane_intersection(r, plane_p, plane_orientation);
|
|
|
|
if success {
|
|
// get the ray's intersection point on the plane which
|
|
// contains the circle
|
|
on_plane := r.origin + t * r.direction;
|
|
// project that point on to the circle's circumference
|
|
point := c.center + c.radius * normalize(on_plane - c.center);
|
|
return length(on_plane - point), point;
|
|
} else {
|
|
// the required point on the circle is the one closest to the camera origin
|
|
point := c.radius * normalize(reject(r.origin - c.center, c.orientation));
|
|
|
|
return distance_from_ray_to_point(r, point), point;
|
|
}
|
|
}
|
|
|
|
intersect_rotation_gizmo :: (ray: Ray) -> Transform_Axis, Vector3 {
|
|
selected_entity := engine.editor.selected_entities[0];
|
|
orientation := selected_entity.transform.orientation;
|
|
|
|
radius := engine.editor.transform_gizmo.uniform_gizmo_scale;
|
|
up: Circle;
|
|
up.radius = radius;
|
|
up.center = engine.editor.transform_gizmo.transform.position;
|
|
up.orientation = rotate(.{0,1,0}, orientation);
|
|
right: Circle;
|
|
right.radius = radius;
|
|
right.center = engine.editor.transform_gizmo.transform.position;
|
|
right.orientation = rotate(.{1,0,0}, orientation);
|
|
forward: Circle;
|
|
forward.radius = radius;
|
|
forward.center = engine.editor.transform_gizmo.transform.position;
|
|
forward.orientation = rotate(.{0,0,1}, orientation);
|
|
|
|
min_dist :: 0.4;
|
|
axis : Transform_Axis = .NONE;
|
|
closest := 100000000.0;
|
|
p : Vector3;
|
|
|
|
du, pu := closest_distance_ray_circle(ray, up);
|
|
if du <= min_dist && du < closest {
|
|
closest = du;
|
|
axis = .UP;
|
|
p = pu;
|
|
}
|
|
|
|
dr, pr := closest_distance_ray_circle(ray, right);
|
|
if dr <= min_dist && dr < closest {
|
|
closest = dr;
|
|
axis = .RIGHT;
|
|
p = pr;
|
|
}
|
|
|
|
df, pf := closest_distance_ray_circle(ray, forward);
|
|
if df <= min_dist && df < closest {
|
|
closest = df;
|
|
axis = .FORWARD;
|
|
p = pf;
|
|
}
|
|
|
|
return axis, p;
|
|
}
|
|
|
|
update_gizmo_buffers :: () {
|
|
position : Vector3;
|
|
for e: engine.editor.selected_entities {
|
|
position += e.transform.position;
|
|
}
|
|
|
|
position /= cast(float)engine.editor.selected_entities.count;
|
|
|
|
entity := engine.editor.selected_entities[0];
|
|
|
|
engine.editor.transform_gizmo.transform.position = position;
|
|
engine.editor.transform_gizmo.transform.orientation = ifx engine.editor.transform_gizmo.space == .LOCAL then entity.transform.orientation else .{0,0,0,1};
|
|
update_matrix(*engine.editor.transform_gizmo.transform);
|
|
|
|
up_rotation := rotation_matrix(Matrix3, euler_to_quaternion(degrees_to_radians(90), degrees_to_radians(90), degrees_to_radians(0)));
|
|
right_rotation := rotation_matrix(Matrix3, euler_to_quaternion(degrees_to_radians(90), degrees_to_radians(0), degrees_to_radians(90)));
|
|
up_model := engine.editor.transform_gizmo.transform.model_matrix * up_rotation;
|
|
right_model := engine.editor.transform_gizmo.transform.model_matrix * right_rotation;
|
|
center_scale := make_scale_matrix4(.{0.15, 0.15, 0.15});
|
|
center_model := engine.editor.transform_gizmo.transform.model_matrix * center_scale;
|
|
|
|
upload_data_to_buffer(engine.renderer, engine.editor.transform_gizmo.center_model_buffer, *center_model, size_of(Matrix4));
|
|
upload_data_to_buffer(engine.renderer, engine.editor.transform_gizmo.up_model_buffer, *up_model, size_of(Matrix4));
|
|
upload_data_to_buffer(engine.renderer, engine.editor.transform_gizmo.forward_model_buffer, *engine.editor.transform_gizmo.transform.model_matrix, size_of(Matrix4));
|
|
upload_data_to_buffer(engine.renderer, engine.editor.transform_gizmo.right_model_buffer, *right_model, size_of(Matrix4));
|
|
}
|
|
|
|
render_transform_gizmo :: () {
|
|
if engine.editor.selected_entities.count > 0 {
|
|
update_gizmo_buffers();
|
|
renderer := engine.renderer;
|
|
push_cmd_set_draw_mode(renderer, .FILL);
|
|
push_cmd_set_depth_write(renderer, false);
|
|
push_cmd_set_pipeline_state(renderer, engine.editor.transform_gizmo.pipeline);
|
|
push_cmd_set_constant_buffer(engine.renderer, 0, engine.camera_buffer,.VERTEX);
|
|
|
|
render_gizmo :: (mesh: *Mesh) {
|
|
renderer := engine.renderer;
|
|
vb := get_mesh_vb(mesh, engine.editor.transform_gizmo.pipeline);
|
|
|
|
// RIGHT
|
|
push_cmd_set_constant_buffer(engine.renderer, 1, engine.editor.transform_gizmo.right_model_buffer, .VERTEX);
|
|
push_cmd_set_constant_buffer(engine.renderer, 2, engine.editor.transform_gizmo.color_right_buffer, .PIXEL);
|
|
|
|
push_cmd_set_vertex_buffer(renderer, vb);
|
|
push_cmd_set_index_buffer(renderer, mesh.ib);
|
|
push_cmd_draw_indexed(renderer, mesh.indices.count);
|
|
|
|
// FORWARD
|
|
push_cmd_set_constant_buffer(engine.renderer, 1, engine.editor.transform_gizmo.forward_model_buffer, .VERTEX);
|
|
push_cmd_set_constant_buffer(engine.renderer, 2, engine.editor.transform_gizmo.color_forward_buffer, .PIXEL);
|
|
|
|
push_cmd_set_vertex_buffer(renderer, vb);
|
|
|
|
push_cmd_set_index_buffer(renderer, mesh.ib);
|
|
push_cmd_draw_indexed(renderer, mesh.indices.count);
|
|
|
|
// UP
|
|
push_cmd_set_constant_buffer(engine.renderer, 1, engine.editor.transform_gizmo.up_model_buffer, .VERTEX);
|
|
push_cmd_set_constant_buffer(engine.renderer, 2, engine.editor.transform_gizmo.color_up_buffer, .PIXEL);
|
|
|
|
push_cmd_set_vertex_buffer(renderer, vb);
|
|
push_cmd_set_index_buffer(renderer, mesh.ib);
|
|
push_cmd_draw_indexed(renderer, mesh.indices.count);
|
|
}
|
|
|
|
if engine.editor.transform_gizmo.transform_type == {
|
|
case .TRANSLATION; {
|
|
mesh := parray_get(*engine.renderer.meshes, engine.renderer.default_meshes.translation_gizmo);
|
|
render_gizmo(mesh);
|
|
}
|
|
case .SCALE; {
|
|
mesh := parray_get(*engine.renderer.meshes, engine.renderer.default_meshes.scale_gizmo);
|
|
render_gizmo(mesh);
|
|
|
|
center_mesh := parray_get(*engine.renderer.meshes, engine.renderer.default_meshes.cube);
|
|
push_cmd_set_constant_buffer(engine.renderer, 1, engine.editor.transform_gizmo.center_model_buffer, .VERTEX);
|
|
push_cmd_set_constant_buffer(engine.renderer, 2, engine.editor.transform_gizmo.color_center_buffer, .PIXEL);
|
|
|
|
vb := get_mesh_vb(mesh, engine.editor.transform_gizmo.pipeline);
|
|
push_cmd_set_vertex_buffer(renderer, vb);
|
|
push_cmd_set_index_buffer(renderer, center_mesh.ib);
|
|
push_cmd_draw_indexed(renderer, center_mesh.indices.count);
|
|
}
|
|
case .ROTATION; {
|
|
mesh := parray_get(*engine.renderer.meshes, engine.renderer.default_meshes.rotation_gizmo);
|
|
render_gizmo(mesh);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#load "editor_ui.jai";
|
|
#load "../renderer/shaders.jai";
|