Multiple entity selection, translation and duplication

This commit is contained in:
2025-07-24 12:59:27 +02:00
parent 659c25dbe5
commit 0d51dc8236
2 changed files with 69 additions and 28 deletions

View File

@@ -114,6 +114,7 @@ Editor :: struct {
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;
@@ -171,10 +172,12 @@ init_transform_gizmo :: () {
}
update_transform_gizmo :: (ray: Ray, mouse_position: Vector2) -> bool {
if engine.editor.selected_entities.count != 1 return false;
selected_entity := engine.editor.selected_entities[0];
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;
@@ -183,6 +186,16 @@ update_transform_gizmo :: (ray: Ray, mouse_position: Vector2) -> bool {
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;
@@ -191,17 +204,28 @@ update_transform_gizmo :: (ray: Ray, mouse_position: Vector2) -> bool {
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 {
engine.editor.transform_gizmo.actual_entity_position = selected_entity.transform.position;
push_transform_undo(engine.editor.selected_entities[0]);
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
@@ -231,7 +255,7 @@ update_transform_gizmo :: (ray: Ray, mouse_position: Vector2) -> bool {
}
r1 : Ray;
r1.origin = selected_entity.transform.position;
r1.origin = position;
r1.direction = axis_vec;
r2 := normalized_screen_to_ray(*engine.editor.camera, mouse_position);
@@ -246,21 +270,28 @@ update_transform_gizmo :: (ray: Ray, mouse_position: Vector2) -> bool {
position_change := new_position - engine.editor.transform_gizmo.first_hit_position;
entity_position := engine.editor.transform_gizmo.actual_entity_position + position_change;
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 - selected_entity.snap_offset.x, snap_interval.x);// + selected_entity.snap_offset.x;
entity_position.y -= fmod_cycling(entity_position.y - selected_entity.snap_offset.y, snap_interval.y);// + selected_entity.snap_offset.y;
entity_position.z -= fmod_cycling(entity_position.z - selected_entity.snap_offset.z, snap_interval.z);// + selected_entity.snap_offset.z;
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 - selected_entity.snap_offset.x, selected_entity.snap_intervals.x);// + selected_entity.snap_offset.x;
entity_position.y -= fmod_cycling(entity_position.y - selected_entity.snap_offset.y, selected_entity.snap_intervals.y);// + selected_entity.snap_offset.y;
entity_position.z -= fmod_cycling(entity_position.z - selected_entity.snap_offset.z, selected_entity.snap_intervals.z);// + selected_entity.snap_offset.z;
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;
}
selected_entity.transform.position = entity_position;
selected_entity.transform.dirty = true;
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);
}
@@ -670,8 +701,16 @@ intersect_rotation_gizmo :: (ray: Ray) -> Transform_Axis, Vector3 {
}
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 = entity.transform.position;
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);
@@ -689,7 +728,7 @@ update_gizmo_buffers :: () {
}
render_transform_gizmo :: () {
if engine.editor.selected_entities.count == 1 {
if engine.editor.selected_entities.count > 0 {
update_gizmo_buffers();
renderer := engine.renderer;
push_cmd_set_draw_mode(renderer, .FILL);

View File

@@ -24,6 +24,8 @@ pick_scene_view_at :: (camera: Camera, coordinates: Vector2) {
engine.editor.selected_entities.count = 0;
}
array_add(*engine.editor.selected_entities, hit_entity);
} else {
array_unordered_remove_by_value(*engine.editor.selected_entities, hit_entity);
}
} else {
engine.editor.selected_entities.count = 0;
@@ -174,7 +176,7 @@ base_editor_update :: () {
if engine.editor.focused_widget == null && engine.mode == .EDITING {
engine.editor.should_check_entities = true;
if engine.editor.selected_entities.count == 1 {
if engine.editor.selected_entities.count > 0 {
entity := engine.editor.selected_entities[0];
gizmo_scale := distance(entity.transform.position, engine.editor.camera.position) * 0.1 * 0.5;
engine.editor.transform_gizmo.uniform_gizmo_scale = gizmo_scale;