mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-03-22 15:05:03 +01:00
Merge pull request #56 from DaniuPl/master
Undo/Redo system, hierarchy for models in scene, uuid for basic_nodes, menu bar 'windows' options
This commit is contained in:
@@ -25,12 +25,12 @@ void
|
||||
editorkeyboard_input::default_bindings() {
|
||||
|
||||
m_bindingsetups = {
|
||||
{ user_command::moveleft, {GLFW_KEY_LEFT, "Move left"} },
|
||||
{ user_command::moveright, {GLFW_KEY_RIGHT, "Move right"} },
|
||||
{ user_command::moveforward, {GLFW_KEY_UP, "Move forwards"} },
|
||||
{ user_command::moveback, {GLFW_KEY_DOWN, "Move backwards"} },
|
||||
{ user_command::moveup, {GLFW_KEY_PAGE_UP, "Move up"} },
|
||||
{ user_command::movedown, {GLFW_KEY_PAGE_DOWN, "Move down"} },
|
||||
{ user_command::moveleft, {GLFW_KEY_A, "Move left"} },
|
||||
{ user_command::moveright, {GLFW_KEY_D, "Move right"} },
|
||||
{ user_command::moveforward, {GLFW_KEY_W, "Move forwards"} },
|
||||
{ user_command::moveback, {GLFW_KEY_S, "Move backwards"} },
|
||||
{ user_command::moveup, {GLFW_KEY_E, "Move up"} },
|
||||
{ user_command::movedown, {GLFW_KEY_Q, "Move down"} },
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
894
editormode.cpp
894
editormode.cpp
File diff suppressed because it is too large
Load Diff
65
editormode.h
65
editormode.h
@@ -45,7 +45,12 @@ class editor_mode : public application_mode
|
||||
}
|
||||
void on_event_poll() override;
|
||||
bool is_command_processor() const override;
|
||||
|
||||
void undo_last();
|
||||
static bool focus_active();
|
||||
static void set_focus_active(bool isActive);
|
||||
static TCamera& get_camera() { return Camera; }
|
||||
static bool change_history() { return m_change_history; }
|
||||
static void set_change_history(bool enabled) { m_change_history = enabled; }
|
||||
private:
|
||||
// types
|
||||
struct editormode_input
|
||||
@@ -65,6 +70,25 @@ class editor_mode : public application_mode
|
||||
bool freefly;
|
||||
bool picking;
|
||||
};
|
||||
|
||||
struct EditorSnapshot
|
||||
{
|
||||
enum class Action { Move, Rotate, Add, Delete, Other };
|
||||
|
||||
Action action{Action::Other};
|
||||
std::string node_name; // node identifier (basic_node::name())
|
||||
// direct pointer to node when available; used for in-memory undo/redo lookup
|
||||
scene::basic_node *node_ptr{nullptr};
|
||||
std::string serialized; // full text for recreate (used for Add/Delete)
|
||||
glm::dvec3 position{0.0, 0.0, 0.0};
|
||||
glm::vec3 rotation{0.0f, 0.0f, 0.0f};
|
||||
UID uuid; // node UUID for reference, used as fallback lookup for deleted/recreated nodes
|
||||
|
||||
};
|
||||
void push_snapshot(scene::basic_node *node, EditorSnapshot::Action Action = EditorSnapshot::Action::Move, std::string const &Serialized = std::string());
|
||||
|
||||
std::vector<EditorSnapshot> m_history; // history of changes to nodes, used for undo functionality
|
||||
std::vector<EditorSnapshot> g_redo;
|
||||
// methods
|
||||
void update_camera(double const Deltatime);
|
||||
bool mode_translation() const;
|
||||
@@ -73,15 +97,50 @@ class editor_mode : public application_mode
|
||||
bool mode_rotationX() const;
|
||||
bool mode_rotationZ() const;
|
||||
bool mode_snap() const;
|
||||
|
||||
editor_ui *ui() const;
|
||||
void redo_last();
|
||||
void handle_brush_mouse_hold(int Action, int Button);
|
||||
void apply_rotation_for_new_node(scene::basic_node *node, int rotation_mode, float fixed_rotation_value);
|
||||
// members
|
||||
state_backup m_statebackup; // helper, cached variables to be restored on mode exit
|
||||
editormode_input m_input;
|
||||
TCamera Camera;
|
||||
static TCamera Camera;
|
||||
|
||||
// focus (smooth camera fly-to) state
|
||||
static bool m_focus_active;
|
||||
glm::dvec3 m_focus_start_pos{0.0,0.0,0.0};
|
||||
glm::dvec3 m_focus_target_pos{0.0,0.0,0.0};
|
||||
glm::dvec3 m_focus_start_lookat{0.0,0.0,0.0};
|
||||
glm::dvec3 m_focus_target_lookat{0.0,0.0,0.0};
|
||||
double m_focus_time{0.0};
|
||||
double m_focus_duration{0.6};
|
||||
|
||||
double fTime50Hz{0.0}; // bufor czasu dla komunikacji z PoKeys
|
||||
scene::basic_editor m_editor;
|
||||
scene::basic_node *m_node; // currently selected scene node
|
||||
bool m_takesnapshot{true}; // helper, hints whether snapshot of selected node(s) should be taken before modification
|
||||
bool m_dragging = false;
|
||||
glm::vec3 oldPos;
|
||||
glm::dvec3 oldPos;
|
||||
bool mouseHold{false};
|
||||
float kMaxPlacementDistance = 200.0f;
|
||||
static bool m_change_history;
|
||||
|
||||
// UI/history settings
|
||||
int m_max_history_size{200};
|
||||
int m_selected_history_idx{-1};
|
||||
glm::dvec3 clamp_mouse_offset_to_max(const glm::dvec3 &offset);
|
||||
|
||||
// focus camera smoothly on specified node
|
||||
void start_focus(scene::basic_node *node, double duration = 0.6);
|
||||
|
||||
// hierarchy management
|
||||
void add_to_hierarchy(scene::basic_node *node);
|
||||
void remove_from_hierarchy(scene::basic_node *node);
|
||||
scene::basic_node* find_in_hierarchy(const std::string &uuid_str);
|
||||
scene::basic_node* find_node_by_any(scene::basic_node *node_ptr, const std::string &uuid_str, const std::string &name);
|
||||
|
||||
// clear history/redo pointers that reference the given node (prevent dangling pointers)
|
||||
void nullify_history_pointers(scene::basic_node *node);
|
||||
void render_change_history();
|
||||
};
|
||||
|
||||
@@ -61,9 +61,9 @@ void itemproperties_panel::update(scene::basic_node const *Node)
|
||||
}
|
||||
}
|
||||
*/
|
||||
textline = "name: " + (node->name().empty() ? "(none)" : Bezogonkow(node->name())) + "\nlocation: [" + to_string(node->location().x, 2) + ", " + to_string(node->location().y, 2) + ", " +
|
||||
textline = "name: " + (node->name().empty() ? "(none)" : Bezogonkow(node->name())) + "\ntype: " + node->node_type + "\nlocation: [" + to_string(node->location().x, 2) + ", " + to_string(node->location().y, 2) + ", " +
|
||||
to_string(node->location().z, 2) + "]" +
|
||||
" (distance: " + to_string(glm::length(glm::dvec3{node->location().x, 0.0, node->location().z} - glm::dvec3{camera.Pos.x, 0.0, camera.Pos.z}), 1) + " m)";
|
||||
" (distance: " + to_string(glm::length(glm::dvec3{node->location().x, 0.0, node->location().z} - glm::dvec3{camera.Pos.x, 0.0, camera.Pos.z}), 1) + " m)" + "\nUUID: " + node->uuid.to_string();
|
||||
text_lines.emplace_back(textline, Global.UITextColor);
|
||||
|
||||
// subclass-specific data
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace scene {
|
||||
std::string const EU07_FILEEXTENSION_REGION { ".sbt" };
|
||||
std::uint32_t const EU07_FILEHEADER { MAKE_ID4( 'E','U','0','7' ) };
|
||||
std::uint32_t const EU07_FILEVERSION_REGION { MAKE_ID4( 'S', 'B', 'T', '2' ) };
|
||||
std::map<std::string, basic_node *> Hierarchy;
|
||||
|
||||
// potentially activates event handler with the same name as provided node, and within handler activation range
|
||||
void
|
||||
@@ -678,9 +679,6 @@ basic_cell::enclose_area( scene::basic_node *Node ) {
|
||||
m_area.radius,
|
||||
static_cast<float>( glm::length( m_area.center - Node->location() ) + Node->radius() ) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
// potentially activates event handler with the same name as provided node, and within handler activation range
|
||||
void
|
||||
basic_section::on_click( TAnimModel const *Instance ) {
|
||||
|
||||
3
scene.h
3
scene.h
@@ -14,6 +14,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include <array>
|
||||
#include <stack>
|
||||
#include <unordered_set>
|
||||
#include <map>
|
||||
|
||||
#include "parser.h"
|
||||
#include "geometrybank.h"
|
||||
@@ -455,6 +456,8 @@ public:
|
||||
|
||||
};
|
||||
|
||||
// global hierarchy map for scene nodes
|
||||
extern std::map<std::string, basic_node *> Hierarchy;
|
||||
} // scene
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@@ -725,6 +725,8 @@ memory_node::deserialize( cParser &Input, node_data const &Nodedata ) {
|
||||
basic_node::basic_node( scene::node_data const &Nodedata ) :
|
||||
m_name( Nodedata.name )
|
||||
{
|
||||
uuid = UID::random();
|
||||
node_type = Nodedata.type;
|
||||
m_rangesquaredmin = Nodedata.range_min * Nodedata.range_min;
|
||||
m_rangesquaredmax = (
|
||||
Nodedata.range_max >= 0.0 ?
|
||||
|
||||
@@ -15,6 +15,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "material.h"
|
||||
#include "vertex.h"
|
||||
#include "geometrybank.h"
|
||||
#include "utils/uuid.hpp"
|
||||
|
||||
struct lighting_data {
|
||||
|
||||
@@ -352,6 +353,8 @@ public:
|
||||
bool
|
||||
dirty() const { return m_dirty; }
|
||||
|
||||
std::string node_type;
|
||||
|
||||
public:
|
||||
// members
|
||||
scene::group_handle m_group { null_handle }; // group this node belongs to, if any
|
||||
@@ -361,6 +364,7 @@ public:
|
||||
bool m_visible { true }; // visibility flag
|
||||
std::string m_name;
|
||||
bool m_dirty { false };
|
||||
UID uuid;
|
||||
|
||||
private:
|
||||
// methods
|
||||
@@ -386,7 +390,6 @@ std::string basic_node::tooltip() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
basic_node::location( glm::dvec3 const Location ) {
|
||||
|
||||
@@ -550,6 +550,10 @@ state_serializer::deserialize_node( cParser &Input, scene::scratch_data &Scratch
|
||||
}
|
||||
scene::Groups.insert( scene::Groups.handle(), instance );
|
||||
simulation::Region->insert( instance );
|
||||
scene::basic_node *hierarchy_node = instance;
|
||||
if (hierarchy_node)
|
||||
{ scene::Hierarchy[hierarchy_node->uuid.to_string()] = hierarchy_node;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( ( nodedata.type == "triangles" )
|
||||
|
||||
58
uilayer.cpp
58
uilayer.cpp
@@ -18,6 +18,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "simulation.h"
|
||||
#include "translation.h"
|
||||
#include "application.h"
|
||||
#include "editormode.h"
|
||||
|
||||
#include "imgui/imgui_impl_glfw.h"
|
||||
|
||||
@@ -381,7 +382,7 @@ void ui_layer::render()
|
||||
render_tooltip();
|
||||
render_menu();
|
||||
render_quit_widget();
|
||||
|
||||
render_hierarchy();
|
||||
// template method implementation
|
||||
render_();
|
||||
|
||||
@@ -423,6 +424,53 @@ void ui_layer::render_quit_widget()
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void ui_layer::render_hierarchy(){
|
||||
if(!m_editor_hierarchy)
|
||||
return;
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(0, 0));
|
||||
ImGui::Begin(STR_C("Scene Hierarchy"), &m_editor_hierarchy, ImGuiWindowFlags_AlwaysAutoResize);
|
||||
ImGui::Text("Registered nodes: %zu", scene::Hierarchy.size());
|
||||
ImGui::BeginChild("hierarchy_list", ImVec2(500, 300), true);
|
||||
|
||||
for (auto &entry : scene::Hierarchy)
|
||||
{
|
||||
const std::string &uuid = entry.first;
|
||||
scene::basic_node *node = entry.second;
|
||||
|
||||
if (node)
|
||||
{
|
||||
char buf[512];
|
||||
std::snprintf(buf, sizeof(buf), "%s | %s (%.1f, %.1f, %.1f)",
|
||||
node->name().c_str(),
|
||||
uuid.c_str(),
|
||||
node->location().x,
|
||||
node->location().y,
|
||||
node->location().z);
|
||||
|
||||
if (ImGui::Selectable(buf, false))
|
||||
{
|
||||
// Focus camera on selected node
|
||||
auto const node_pos = node->location();
|
||||
auto const camera_offset = glm::dvec3(0.0, 10.0, -20.0);
|
||||
editor_mode::set_focus_active(false);
|
||||
TCamera &camera = editor_mode::get_camera();
|
||||
camera.Pos = node_pos + camera_offset;
|
||||
|
||||
}
|
||||
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
ImGui::SetTooltip("UUID: %s\nNode type: %s", uuid.c_str(), typeid(*node).name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::End();
|
||||
|
||||
}
|
||||
|
||||
void ui_layer::set_cursor(int const Mode)
|
||||
{
|
||||
glfwSetInputMode(m_window, GLFW_CURSOR, Mode);
|
||||
@@ -522,6 +570,14 @@ void ui_layer::render_menu_contents()
|
||||
|
||||
GfxRenderer->Debug_Ui_State(ret);
|
||||
}
|
||||
if(EditorModeFlag){
|
||||
ImGui::MenuItem("Hierarchy", nullptr, &m_editor_hierarchy);
|
||||
bool change_history_enabled = editor_mode::change_history();
|
||||
if (ImGui::MenuItem("Change History", nullptr, &change_history_enabled))
|
||||
{
|
||||
editor_mode::set_change_history(change_history_enabled);
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,6 +147,7 @@ protected:
|
||||
void render_panels();
|
||||
void render_menu();
|
||||
void render_quit_widget();
|
||||
void render_hierarchy();
|
||||
// draws a quad between coordinates x,y and z,w with uv-coordinates spanning 0-1
|
||||
void quad( glm::vec4 const &Coordinates, glm::vec4 const &Color );
|
||||
// members
|
||||
@@ -158,4 +159,6 @@ protected:
|
||||
std::string m_tooltip;
|
||||
bool m_quit_active = false;
|
||||
bool m_imgui_demo = false;
|
||||
bool m_editor_hierarchy = false;
|
||||
bool m_editor_change_history = false;
|
||||
};
|
||||
|
||||
69
utils/uuid.hpp
Normal file
69
utils/uuid.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
|
||||
class UID {
|
||||
public:
|
||||
std::array<uint8_t,16> bytes;
|
||||
|
||||
|
||||
static UID random() {
|
||||
static thread_local std::mt19937_64 gen(std::random_device{}());
|
||||
UID u;
|
||||
uint64_t a = gen();
|
||||
uint64_t b = gen();
|
||||
for (int i = 0; i < 8; ++i) u.bytes[i] = uint8_t((a >> (i*8)) & 0xFF);
|
||||
for (int i = 0; i < 8; ++i) u.bytes[8 + i] = uint8_t((b >> (i*8)) & 0xFF);
|
||||
|
||||
u.bytes[6] = (u.bytes[6] & 0x0F) | 0x40;
|
||||
u.bytes[8] = (u.bytes[8] & 0x3F) | 0x80;
|
||||
|
||||
return u;
|
||||
}
|
||||
|
||||
std::string to_string() const {
|
||||
std::ostringstream os;
|
||||
os << std::hex << std::setfill('0');
|
||||
auto put = [&](int i){
|
||||
os << std::setw(2) << static_cast<int>(bytes[i]);
|
||||
};
|
||||
// format 8-4-4-4-12
|
||||
for (int i = 0; i < 4; ++i) put(i);
|
||||
for (int i = 4; i < 6; ++i) put(i);
|
||||
os << '-';
|
||||
for (int i = 6; i < 8; ++i) put(i);
|
||||
os << '-';
|
||||
for (int i = 8; i < 10; ++i) put(i);
|
||||
os << '-';
|
||||
for (int i = 10; i < 12; ++i) put(i);
|
||||
os << '-';
|
||||
for (int i = 12; i < 16; ++i) put(i);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
static UID from_string(const std::string& str) {
|
||||
std::istringstream is(str);
|
||||
is >> std::hex;
|
||||
UID u;
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
int byte;
|
||||
is >> byte;
|
||||
u.bytes[i] = static_cast<uint8_t>(byte);
|
||||
if (is.peek() == '-') is.get();
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
bool operator==(const UID &other) const noexcept {
|
||||
return bytes == other.bytes;
|
||||
}
|
||||
|
||||
bool operator!=(const UID &other) const noexcept {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user