From aa396a76ed6815425202e445f1e49b385e0956d8 Mon Sep 17 00:00:00 2001 From: valmme Date: Sat, 9 May 2026 16:57:06 +0400 Subject: [PATCH 1/5] move compile scripts into scripts folder --- build_engine.bat => scripts/build_engine.bat | 0 build_engine.sh => scripts/build_engine.sh | 0 build_plugin.bat => scripts/build_plugin.bat | 0 build_plugin.sh => scripts/build_plugin.sh | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename build_engine.bat => scripts/build_engine.bat (100%) rename build_engine.sh => scripts/build_engine.sh (100%) rename build_plugin.bat => scripts/build_plugin.bat (100%) rename build_plugin.sh => scripts/build_plugin.sh (100%) diff --git a/build_engine.bat b/scripts/build_engine.bat similarity index 100% rename from build_engine.bat rename to scripts/build_engine.bat diff --git a/build_engine.sh b/scripts/build_engine.sh similarity index 100% rename from build_engine.sh rename to scripts/build_engine.sh diff --git a/build_plugin.bat b/scripts/build_plugin.bat similarity index 100% rename from build_plugin.bat rename to scripts/build_plugin.bat diff --git a/build_plugin.sh b/scripts/build_plugin.sh similarity index 100% rename from build_plugin.sh rename to scripts/build_plugin.sh From ef7097f6a18b47b41181e067a64e2108cff7116a Mon Sep 17 00:00:00 2001 From: valmme Date: Sat, 9 May 2026 17:48:43 +0400 Subject: [PATCH 2/5] CreateNew popup fix --- src/editor/editor_assets.cpp | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/editor/editor_assets.cpp b/src/editor/editor_assets.cpp index c65f06a..92aba13 100644 --- a/src/editor/editor_assets.cpp +++ b/src/editor/editor_assets.cpp @@ -335,6 +335,8 @@ void draw_assets_ui(Editor& editor) { if (icon_folder_tex.id == 0) icon_folder_tex = LoadTexture("assets/folder.png"); if (icon_full_folder_tex.id == 0) icon_full_folder_tex = LoadTexture("assets/full_folder.png"); + std::string popup_name = std::string(lang.word("create_new")) + "##CreateNew"; + static ImVec2 selection_start = {}; static ImVec2 selection_end = {}; static bool selecting = false; @@ -343,10 +345,10 @@ void draw_assets_ui(Editor& editor) { static std::string dragged_file_name; static ImVec2 drag_start_pos; + static bool show_create_popup = false; static bool show_duplicate_popup = false; static std::string duplicate_name; - static bool show_create_new_item = false; static bool creating_folder = false; static char new_item_name[128] = ""; @@ -474,13 +476,13 @@ void draw_assets_ui(Editor& editor) { if (ImGui::MenuItem(lang.word("new_file"))) { creating_folder = false; new_item_name[0] = '\0'; - show_create_new_item = true; + show_create_popup = true; } if (ImGui::MenuItem(lang.word("new_folder"))) { creating_folder = true; new_item_name[0] = '\0'; - show_create_new_item = true; + show_create_popup = true; } ImGui::EndPopup(); @@ -506,6 +508,7 @@ void draw_assets_ui(Editor& editor) { editor_internal::dragged_target_folder_index = -1; + bool navigated = false; for (int i = 0; i < static_cast(entries.size()); i++) { auto& entry = entries[i]; ImGui::PushID(i); @@ -529,6 +532,7 @@ void draw_assets_ui(Editor& editor) { model_render_cache.clear(); ImGui::EndGroup(); ImGui::PopID(); + navigated = true; break; } @@ -578,13 +582,13 @@ void draw_assets_ui(Editor& editor) { if (ImGui::MenuItem(lang.word("new_file"))) { creating_folder = false; new_item_name[0] = '\0'; - show_create_new_item = true; + show_create_popup = true; } if (ImGui::MenuItem(lang.word("new_folder"))) { creating_folder = true; new_item_name[0] = '\0'; - show_create_new_item = true; + show_create_popup = true; } ImGui::EndPopup(); @@ -826,13 +830,13 @@ void draw_assets_ui(Editor& editor) { if (ImGui::MenuItem(lang.word("new_file"))) { creating_folder = false; new_item_name[0] = '\0'; - show_create_new_item = true; + show_create_popup = true; } if (ImGui::MenuItem(lang.word("new_folder"))) { creating_folder = true; new_item_name[0] = '\0'; - show_create_new_item = true; + show_create_popup = true; } ImGui::EndPopup(); @@ -848,11 +852,6 @@ void draw_assets_ui(Editor& editor) { show_duplicate_popup = false; } - if (show_create_new_item) { - ImGui::OpenPopup(("%s##CreateNew", lang.word("create_new"))); - show_create_new_item = false; - } - if (ImGui::BeginPopupModal(("%s##DuplicateName", lang.word("move_error")), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text(lang.word("unable_to_move")); ImGui::TextColored(ImVec4(1, 0.4f, 0.4f, 1), "%s", duplicate_name.c_str()); @@ -868,7 +867,14 @@ void draw_assets_ui(Editor& editor) { ImGui::EndPopup(); } - if (ImGui::BeginPopupModal(("%s##CreateNew", lang.word("create_new")), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::EndChild(); + + if (show_create_popup && !navigated) { + ImGui::OpenPopup(popup_name.c_str()); + show_create_popup = false; + } + + if (ImGui::BeginPopupModal(popup_name.c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::TextUnformatted(creating_folder ? lang.word("new_folder") : lang.word("new_file")); ImGui::InputText(lang.word("name"), new_item_name, IM_ARRAYSIZE(new_item_name)); ImGui::Spacing(); @@ -919,7 +925,6 @@ void draw_assets_ui(Editor& editor) { ImGui::EndPopup(); } - ImGui::EndChild(); ImGui::End(); } From 0d11ef6ab72eb8cedca09cc4fb3a7647321d270b Mon Sep 17 00:00:00 2001 From: valmme Date: Sat, 9 May 2026 17:54:23 +0400 Subject: [PATCH 3/5] drag & drop models to the scene fix --- src/editor/editor_assets.cpp | 3 --- src/editor/editor_ui.cpp | 12 +++++++++--- src/editor/editor_ui.h | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/editor/editor_assets.cpp b/src/editor/editor_assets.cpp index 92aba13..17cb6a4 100644 --- a/src/editor/editor_assets.cpp +++ b/src/editor/editor_assets.cpp @@ -776,9 +776,6 @@ void draw_assets_ui(Editor& editor) { editor_internal::dragged_target_folder_index = -1; dragged_file_name.clear(); drag_start_pos = ImVec2(0, 0); - - editor_internal::scene_asset_dragging = false; - editor_internal::dragged_scene_asset_name.clear(); } if (rename_target >= 0) { diff --git a/src/editor/editor_ui.cpp b/src/editor/editor_ui.cpp index 7cb3b65..b13667b 100644 --- a/src/editor/editor_ui.cpp +++ b/src/editor/editor_ui.cpp @@ -625,7 +625,7 @@ void draw_mesh_vertex_overlay(Editor& editor, Camera3D camera) { } } -void handle_scene_asset_drop(Editor& editor, Camera3D camera, bool is_hovered) { +void handle_scene_asset_drop(Editor& editor, Camera3D camera) { if (!editor_internal::scene_asset_dragging) return; if (!IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) return; @@ -634,7 +634,13 @@ void handle_scene_asset_drop(Editor& editor, Camera3D camera, bool is_hovered) { editor_internal::dragged_scene_asset_name.clear(); if (ImGuizmo::IsUsing()) return; - if (!is_hovered) return; + + const ImVec2 mouse = { (float)GetMouseX(), (float)GetMouseY() }; + const bool mouse_over_scene = + mouse.x >= g_scene_window_pos.x && mouse.x <= g_scene_window_pos.x + g_scene_window_size.x && + mouse.y >= g_scene_window_pos.y && mouse.y <= g_scene_window_pos.y + g_scene_window_size.y; + + if (!mouse_over_scene) return; ModelAsset* asset = find_asset_by_name(asset_name); if (!asset) return; @@ -1299,7 +1305,7 @@ void draw_ui(Editor& editor, Shader shader, FlyCamera camera) { draw_gizmo(editor, camera); draw_polygon_editor(editor, camera.get_camera()); - handle_scene_asset_drop(editor, camera.get_camera(), g_is_scene_hovered); + handle_scene_asset_drop(editor, camera.get_camera()); } } ImGui::End(); diff --git a/src/editor/editor_ui.h b/src/editor/editor_ui.h index 636ceff..8c5aaf6 100644 --- a/src/editor/editor_ui.h +++ b/src/editor/editor_ui.h @@ -29,7 +29,7 @@ extern std::vector g_selected_vertices; void draw_ui(Editor& editor, Shader shader, FlyCamera camera); void draw_gizmo(Editor& editor, FlyCamera camera); -void handle_scene_asset_drop(Editor& editor, Camera3D camera, bool is_hovered); +void handle_scene_asset_drop(Editor& editor, Camera3D camera); void draw_mesh_vertex_overlay(Editor& editor, Camera3D camera); void reset_mesh_edit_model(Entity& entity); From 0602e52014b8d7680ad5a16ea1d58616ff0213d0 Mon Sep 17 00:00:00 2001 From: valmme Date: Sat, 9 May 2026 19:42:46 +0400 Subject: [PATCH 4/5] Ctrl+C, Ctrl+V, Ctrl+D, Del keys to copy, paste, dublicate or delete entity --- src/editor/editor.cpp | 73 +++++++++++++++++- src/editor/editor_ui.cpp | 163 ++++++++++++++++++++++----------------- src/editor/editor_ui.h | 5 ++ 3 files changed, 169 insertions(+), 72 deletions(-) diff --git a/src/editor/editor.cpp b/src/editor/editor.cpp index 4c880cc..b4e0806 100644 --- a/src/editor/editor.cpp +++ b/src/editor/editor.cpp @@ -141,18 +141,33 @@ void Editor::handle_input() { } ImGuiIO& io = ImGui::GetIO(); - const bool ctrl = IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL); + const bool ctrl = (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)) && !io.WantCaptureKeyboard; - if (!io.WantCaptureKeyboard && ctrl && IsKeyPressed(KEY_S)) { + if (ctrl && IsKeyPressed(KEY_S)) { project_save(project_path, scene); } static float last_undo_time = 0.0f; static float last_redo_time = 0.0f; + static float last_copy_time = 0.0f; + static float last_paste_time = 0.0f; + static float last_dublicate_time = 0.0f; + static float last_delete_time = 0.0f; + static bool undo_key_was_pressed = false; static bool redo_key_was_pressed = false; + static bool copy_key_was_pressed = false; + static bool paste_key_was_pressed = false; + static bool dubl_key_was_pressed = false; + static bool del_key_was_pressed = false; + static float undo_hold_start = 0.0f; static float redo_hold_start = 0.0f; + static float copy_hold_start = 0.0f; + static float paste_hold_start = 0.0f; + static float dubl_hold_start = 0.0f; + static float del_hold_start = 0.0f; + const float now = static_cast(GetTime()); if (ctrl && IsKeyDown(KEY_Z)) { @@ -185,6 +200,60 @@ void Editor::handle_input() { redo_hold_start = 0.0f; } + Entity* entity = scene.get_selected(); + + if (ctrl && IsKeyDown(KEY_C)) { + if (!copy_key_was_pressed) { + copy_entity(entity); + + copy_key_was_pressed = true; + copy_hold_start = now; + last_copy_time = now; + } else if (now - copy_hold_start > 0.5f && now - last_copy_time > 0.15f) { + copy_entity(entity); + last_copy_time = now; + } + } + + if (ctrl && IsKeyDown(KEY_V)) { + if (!paste_key_was_pressed){ + paste_entity(*this); + + paste_key_was_pressed = true; + paste_hold_start = now; + last_paste_time = now; + } else if (now - paste_hold_start > 0.5f && now - last_paste_time > 0.15f) { + paste_entity(*this); + last_paste_time = now; + } + } + + if (ctrl && IsKeyDown(KEY_D)) { + if (!dubl_key_was_pressed) { + dublicate_entity(*this, entity); + + dubl_key_was_pressed = true; + dubl_hold_start = now; + last_dublicate_time = now; + } else if (now - dubl_hold_start > 0.5f && now - last_dublicate_time > 0.15f) { + dublicate_entity(*this, entity); + last_dublicate_time = now; + } + } + + if (IsKeyDown(KEY_DELETE)) { + if (!del_key_was_pressed) { + delete_entity(*this, entity, shadowmap_shader); + + del_key_was_pressed = true; + del_hold_start = now; + last_delete_time = now; + } else if (now - del_hold_start > 0.5f && now - last_delete_time > 0.15f) { + delete_entity(*this, entity, shadowmap_shader); + last_delete_time = now; + } + } + static double last_asset_poll = 0.0; const double current_time = GetTime(); if (current_time - last_asset_poll <= 2.0) return; diff --git a/src/editor/editor_ui.cpp b/src/editor/editor_ui.cpp index b13667b..354c6b4 100644 --- a/src/editor/editor_ui.cpp +++ b/src/editor/editor_ui.cpp @@ -868,6 +868,95 @@ void draw_polygon_editor(Editor& editor, Camera3D camera) { } } +void copy_entity(Entity* entity) { + using namespace editor_internal; + + clipboard_data = *entity; + has_clipboard = true; +} + +void paste_entity(Editor& editor) { + using namespace editor_internal; + + const MeshComponent* clipboard_mesh = clipboard_data.get_mesh_component(); + const TransformComponent* clipboard_transform = clipboard_data.get_transform_component(); + const LightComponent* clipboard_light = clipboard_data.get_light_component(); + const MaterialComponent* clipboard_mat = clipboard_data.get_material_component(); + ModelAsset* asset = (clipboard_mesh && !clipboard_mesh->asset_name.empty()) + ? find_asset_by_name(clipboard_mesh->asset_name) + : nullptr; + + if (asset) { + editor.save_state(); + Entity pasted = make_entity_from_asset(editor.scene, *asset); + if (auto pasted_transform = pasted.get_transform_component(); pasted_transform && clipboard_transform) { + pasted_transform->position = clipboard_transform->position; + pasted_transform->rotation = clipboard_transform->rotation; + pasted_transform->scale = clipboard_transform->scale; + } + + auto pasted_mesh = pasted.get_mesh_component(); + auto pasted_mat = pasted.get_material_component(); + + if (pasted_mesh && clipboard_mesh) { + pasted_mat->color = clipboard_mat->color; + pasted_mat->outline_color = clipboard_mat->outline_color; + pasted_mat->texture_source = clipboard_mat->texture_source; + pasted_mat->texture_name = clipboard_mat->texture_name; + pasted_mat->texture = clipboard_mat->texture; + pasted_mat->texture_stretch = clipboard_mat->texture_stretch; + pasted_mat->auto_uv = clipboard_mat->auto_uv; + pasted_mat->texture_repeat_u = clipboard_mat->texture_repeat_u; + pasted_mat->texture_repeat_v = clipboard_mat->texture_repeat_v; + pasted_mat->uv_scale = clipboard_mat->uv_scale; + pasted_mesh->mesh_triangles_detached = clipboard_mesh->mesh_triangles_detached; + pasted_mesh->mesh_vertex_overrides = clipboard_mesh->mesh_vertex_overrides; + apply_mesh_overrides(pasted); + } + if (clipboard_light) { + auto light_copy = std::make_shared(*clipboard_light); + const int light_type = light_copy->light.light.type; + light_copy->created = false; + light_copy->light.id = -1; + light_copy->light.light = {0}; + light_copy->light.light.type = light_type; + pasted.get_components()->add_component(light_copy); + } + editor.scene.entities.push_back(pasted); + editor.scene.selected = static_cast(editor.scene.entities.size()) - 1; + } +} + +void dublicate_entity(Editor& editor, Entity* entity) { + editor.save_state(); + Entity copy = *entity; + + copy.id = static_cast(editor.scene.entities.size()); + copy.name = editor.scene.make_default_name_for(copy); + if (auto light = copy.get_light_component()) { + const int light_type = light->light.light.type; + light->created = false; + light->light.id = -1; + light->light.light = {0}; + light->light.light.type = light_type; + } + editor.scene.entities.push_back(copy); + editor.scene.selected = static_cast(editor.scene.entities.size()) - 1; +} + +void delete_entity(Editor& editor, Entity* entity, Shader shader) { + editor.save_state(); + const int index = editor.scene.selected; + if (auto light = entity->get_light_component(); light && light->created) { + light->light.enabled = false; + if (light->light.id != -1) update_lighting(shader, light->light); + free_light_id(light->light.id); + } + + editor.scene.entities.erase(editor.scene.entities.begin() + index); + editor.scene.selected = -1; +} + void draw_ui(Editor& editor, Shader shader, FlyCamera camera) { using namespace editor_internal; @@ -898,87 +987,21 @@ void draw_ui(Editor& editor, Shader shader, FlyCamera camera) { Entity* entity = editor.scene.get_selected(); if (ImGui::MenuItem(lang.word("copy"), "Ctrl+C", false, entity != nullptr)) { - clipboard_data = *entity; - has_clipboard = true; + copy_entity(entity); } if (ImGui::MenuItem(lang.word("paste"), "Ctrl+V", false, has_clipboard)) { - const MeshComponent* clipboard_mesh = clipboard_data.get_mesh_component(); - const TransformComponent* clipboard_transform = clipboard_data.get_transform_component(); - const LightComponent* clipboard_light = clipboard_data.get_light_component(); - const MaterialComponent* clipboard_mat = clipboard_data.get_material_component(); - ModelAsset* asset = (clipboard_mesh && !clipboard_mesh->asset_name.empty()) - ? find_asset_by_name(clipboard_mesh->asset_name) - : nullptr; - if (asset) { - editor.save_state(); - Entity pasted = make_entity_from_asset(editor.scene, *asset); - if (auto pasted_transform = pasted.get_transform_component(); pasted_transform && clipboard_transform) { - pasted_transform->position = clipboard_transform->position; - pasted_transform->rotation = clipboard_transform->rotation; - pasted_transform->scale = clipboard_transform->scale; - } - - auto pasted_mesh = pasted.get_mesh_component(); - auto pasted_mat = pasted.get_material_component(); - - if (pasted_mesh && clipboard_mesh) { - pasted_mat->color = clipboard_mat->color; - pasted_mat->outline_color = clipboard_mat->outline_color; - pasted_mat->texture_source = clipboard_mat->texture_source; - pasted_mat->texture_name = clipboard_mat->texture_name; - pasted_mat->texture = clipboard_mat->texture; - pasted_mat->texture_stretch = clipboard_mat->texture_stretch; - pasted_mat->auto_uv = clipboard_mat->auto_uv; - pasted_mat->texture_repeat_u = clipboard_mat->texture_repeat_u; - pasted_mat->texture_repeat_v = clipboard_mat->texture_repeat_v; - pasted_mat->uv_scale = clipboard_mat->uv_scale; - pasted_mesh->mesh_triangles_detached = clipboard_mesh->mesh_triangles_detached; - pasted_mesh->mesh_vertex_overrides = clipboard_mesh->mesh_vertex_overrides; - apply_mesh_overrides(pasted); - } - if (clipboard_light) { - auto light_copy = std::make_shared(*clipboard_light); - const int light_type = light_copy->light.light.type; - light_copy->created = false; - light_copy->light.id = -1; - light_copy->light.light = {0}; - light_copy->light.light.type = light_type; - pasted.get_components()->add_component(light_copy); - } - editor.scene.entities.push_back(pasted); - editor.scene.selected = static_cast(editor.scene.entities.size()) - 1; - } + paste_entity(editor); } if (ImGui::MenuItem(lang.word("dublicate"), "Ctrl+D", false, entity != nullptr)) { - editor.save_state(); - Entity copy = *entity; - copy.id = static_cast(editor.scene.entities.size()); - copy.name = editor.scene.make_default_name_for(copy); - if (auto light = copy.get_light_component()) { - const int light_type = light->light.light.type; - light->created = false; - light->light.id = -1; - light->light.light = {0}; - light->light.light.type = light_type; - } - editor.scene.entities.push_back(copy); - editor.scene.selected = static_cast(editor.scene.entities.size()) - 1; + dublicate_entity(editor, entity); } ImGui::Separator(); if (ImGui::MenuItem(lang.word("delete"), "Del", false, entity != nullptr)) { - editor.save_state(); - const int index = editor.scene.selected; - if (auto light = entity->get_light_component(); light && light->created) { - light->light.enabled = false; - if (light->light.id != -1) update_lighting(shader, light->light); - free_light_id(light->light.id); - } - editor.scene.entities.erase(editor.scene.entities.begin() + index); - editor.scene.selected = -1; + delete_entity(editor, entity, shader); } ImGui::Separator(); diff --git a/src/editor/editor_ui.h b/src/editor/editor_ui.h index 8c5aaf6..33708d9 100644 --- a/src/editor/editor_ui.h +++ b/src/editor/editor_ui.h @@ -26,6 +26,11 @@ extern MeshEditState g_mesh_edit_state; extern PolygonEditMode g_poly_mode; extern std::vector g_selected_vertices; +void copy_entity(Entity* entity); +void paste_entity(Editor& editor); +void dublicate_entity(Editor& editor, Entity* entity); +void delete_entity(Editor& editor, Entity* entity, Shader shader); + void draw_ui(Editor& editor, Shader shader, FlyCamera camera); void draw_gizmo(Editor& editor, FlyCamera camera); From cae00fcf923974eeef81d694063d264e58661251 Mon Sep 17 00:00:00 2001 From: valmme Date: Sat, 9 May 2026 22:40:53 +0400 Subject: [PATCH 5/5] material drag n drop to the scene --- assets/lang/english.json | 1 + src/editor/editor_assets.cpp | 12 ++- src/editor/editor_ui.cpp | 169 ++++++++++++++++++++++++++++++++++- 3 files changed, 177 insertions(+), 5 deletions(-) diff --git a/assets/lang/english.json b/assets/lang/english.json index e3d6604..a748f15 100644 --- a/assets/lang/english.json +++ b/assets/lang/english.json @@ -62,6 +62,7 @@ "rename_asset": "Rename Item", "spawn": "Spawn %s", + "drag_n_drop_material": "Drag the material to an entity", "enabled": "Enabled", diff --git a/src/editor/editor_assets.cpp b/src/editor/editor_assets.cpp index 17cb6a4..049a1b4 100644 --- a/src/editor/editor_assets.cpp +++ b/src/editor/editor_assets.cpp @@ -528,7 +528,9 @@ void draw_assets_ui(Editor& editor) { editor.selected_asset_index = -1; editor_internal::tex_cache.clear(); model_preview_cache.clear(); + for (auto& pair : model_render_cache) UnloadRenderTexture(pair.second); + model_render_cache.clear(); ImGui::EndGroup(); ImGui::PopID(); @@ -694,6 +696,11 @@ void draw_assets_ui(Editor& editor) { if (fabsf(delta.x) > 5.0f || fabsf(delta.y) > 5.0f) { editor_internal::file_dragging = true; editor_internal::dragged_file_index = i; + + if (entry.is_material) { + editor_internal::scene_asset_dragging = true; + editor_internal::dragged_scene_asset_name = (editor.current_asset_path / entry.filename).string(); + } if (is_model_file(fs::path(entry.filename))) { const std::string asset_name = get_asset_name_for_path(fs::path(editor.project_path), editor.current_asset_path / entry.filename); @@ -714,10 +721,11 @@ void draw_assets_ui(Editor& editor) { } } - if (!entry.is_directory && is_model_file(fs::path(entry.filename))) { + if (!entry.is_directory && (is_model_file(fs::path(entry.filename)) || entry.is_material)) { const std::string asset_name = get_asset_name_for_path(fs::path(editor.project_path), editor.current_asset_path / entry.filename); + if (editor_internal::scene_asset_dragging && editor_internal::dragged_scene_asset_name == asset_name) { - ImGui::SetTooltip(lang.word("spawn"), editor_internal::dragged_scene_asset_name.c_str()); + if (is_model_file(fs::path(entry.filename))) ImGui::SetTooltip(lang.word("spawn"), editor_internal::dragged_scene_asset_name.c_str()); } } diff --git a/src/editor/editor_ui.cpp b/src/editor/editor_ui.cpp index 354c6b4..51006ef 100644 --- a/src/editor/editor_ui.cpp +++ b/src/editor/editor_ui.cpp @@ -309,6 +309,46 @@ bool pick_mesh_triangle( return true; } +bool raycast_entity(const Entity& entity, Ray ray, float& out_distance) { + const MeshComponent* mesh = entity.get_mesh_component(); + if (!mesh || !has_valid_model_data(mesh->model)) return false; + + const Matrix transform = compose_entity_transform_matrix(entity); + + bool hit_any = false; + float best_distance = FLT_MAX; + + for (int i = 0; i < mesh->model.meshCount; i++) { + const Mesh& m = mesh->model.meshes[i]; + + for (int j = 0; j < m.triangleCount; j++) { + int indices[3] = {}; + if (!get_mesh_triangle_vertex_indices(m, j, indices)) continue; + + Vector3 verts[3]; + + for (int k = 0; k < 3; k++) { + verts[k] = Vector3Transform({ + m.vertices[indices[k] * 3 + 0], + m.vertices[indices[k] * 3 + 1], + m.vertices[indices[k] * 3 + 2], + }, transform); + } + + RayCollision hit = GetRayCollisionTriangle(ray, verts[0], verts[1], verts[2]); + + if (hit.hit && hit.distance < best_distance) { + best_distance = hit.distance; + hit_any = true; + } + } + } + + out_distance = best_distance; + return hit_any; + +} + void reset_mesh_edit_model(Entity& entity) { MeshComponent* mesh = entity.get_mesh_component(); if (!mesh) return; @@ -625,7 +665,8 @@ void draw_mesh_vertex_overlay(Editor& editor, Camera3D camera) { } } -void handle_scene_asset_drop(Editor& editor, Camera3D camera) { +void handle_scene_asset_drop(Editor& editor, Camera3D camera) +{ if (!editor_internal::scene_asset_dragging) return; if (!IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) return; @@ -636,25 +677,147 @@ void handle_scene_asset_drop(Editor& editor, Camera3D camera) { if (ImGuizmo::IsUsing()) return; const ImVec2 mouse = { (float)GetMouseX(), (float)GetMouseY() }; - const bool mouse_over_scene = + + const bool mouse_over_scene = mouse.x >= g_scene_window_pos.x && mouse.x <= g_scene_window_pos.x + g_scene_window_size.x && mouse.y >= g_scene_window_pos.y && mouse.y <= g_scene_window_pos.y + g_scene_window_size.y; if (!mouse_over_scene) return; + const bool is_material = + asset_name.size() >= 4 && + asset_name.substr(asset_name.size() - 4) == ".mtl"; + + if (is_material) + { + Ray ray = GetScreenToWorldRay({ mouse.x, mouse.y }, camera); + + Entity* hit_entity = nullptr; + float best_distance = FLT_MAX; + + for (Entity& entity : editor.scene.entities) + { + const MeshComponent* mesh_comp = entity.get_mesh_component(); + if (!mesh_comp || !has_valid_model_data(mesh_comp->model)) continue; + + Matrix transform_mat = compose_entity_transform_matrix(entity); + + for (int i = 0; i < mesh_comp->model.meshCount; i++) + { + const Mesh& mesh = mesh_comp->model.meshes[i]; + + BoundingBox box = GetMeshBoundingBox(mesh); + + Vector3 corners[8] = { + { box.min.x, box.min.y, box.min.z }, + { box.max.x, box.min.y, box.min.z }, + { box.min.x, box.max.y, box.min.z }, + { box.max.x, box.max.y, box.min.z }, + { box.min.x, box.min.y, box.max.z }, + { box.max.x, box.min.y, box.max.z }, + { box.min.x, box.max.y, box.max.z }, + { box.max.x, box.max.y, box.max.z } + }; + + BoundingBox world_box = { + { FLT_MAX, FLT_MAX, FLT_MAX }, + { -FLT_MAX, -FLT_MAX, -FLT_MAX } + }; + + for (int c = 0; c < 8; c++) + { + Vector3 p = Vector3Transform(corners[c], transform_mat); + + world_box.min.x = std::min(world_box.min.x, p.x); + world_box.min.y = std::min(world_box.min.y, p.y); + world_box.min.z = std::min(world_box.min.z, p.z); + + world_box.max.x = std::max(world_box.max.x, p.x); + world_box.max.y = std::max(world_box.max.y, p.y); + world_box.max.z = std::max(world_box.max.z, p.z); + } + + if (!GetRayCollisionBox(ray, world_box).hit) + continue; + + // 💥 ПРАВИЛЬНЫЙ RAYCAST ПО ТРЕУГОЛЬНИКАМ + float mesh_best = FLT_MAX; + bool mesh_hit = false; + + for (int t = 0; t < mesh.triangleCount; t++) + { + int indices[3]; + if (!get_mesh_triangle_vertex_indices(mesh, t, indices)) + continue; + + Vector3 v0 = Vector3Transform( + { + mesh.vertices[indices[0] * 3 + 0], + mesh.vertices[indices[0] * 3 + 1], + mesh.vertices[indices[0] * 3 + 2] + }, + transform_mat + ); + + Vector3 v1 = Vector3Transform( + { + mesh.vertices[indices[1] * 3 + 0], + mesh.vertices[indices[1] * 3 + 1], + mesh.vertices[indices[1] * 3 + 2] + }, + transform_mat + ); + + Vector3 v2 = Vector3Transform( + { + mesh.vertices[indices[2] * 3 + 0], + mesh.vertices[indices[2] * 3 + 1], + mesh.vertices[indices[2] * 3 + 2] + }, + transform_mat + ); + + RayCollision hit = GetRayCollisionTriangle(ray, v0, v1, v2); + + if (hit.hit && hit.distance < mesh_best) + { + mesh_best = hit.distance; + mesh_hit = true; + } + } + + if (mesh_hit && mesh_best < best_distance) + { + best_distance = mesh_best; + hit_entity = &entity; + } + } + } + + if (hit_entity && hit_entity->get_material_component()) + { + editor.save_state(); + load_material_to_entity(hit_entity, asset_name); + mark_entity_uv_dirty(hit_entity); + } + + return; + } + ModelAsset* asset = find_asset_by_name(asset_name); if (!asset) return; Entity entity = make_entity_from_asset(editor.scene, *asset); MeshComponent* mesh = entity.get_mesh_component(); TransformComponent* transform = entity.get_transform_component(); + if (!mesh || !transform || !has_valid_model_data(mesh->model)) return; editor.save_state(); transform->position = get_scene_drop_position(camera); editor.scene.entities.push_back(entity); - editor.scene.selected = static_cast(editor.scene.entities.size()) - 1; + editor.scene.selected = (int)editor.scene.entities.size() - 1; } bool polygon_create_vertex(Entity& entity, const Vector3& world_position) {