From 22abfadbebbfd1f1d8b2d79fa52f5fb7dda2d1fb Mon Sep 17 00:00:00 2001 From: Tatjam Date: Sun, 20 Sep 2020 21:29:19 +0200 Subject: [PATCH] Initial implementation of machine markers --- src/Main.cpp | 21 +- src/assets/Model.cpp | 4 +- .../editor/interfaces/WireInterface.cpp | 433 ++++++++++-------- .../scenes/editor/interfaces/WireInterface.h | 5 + src/universe/vehicle/part/Machine.cpp | 2 + src/universe/vehicle/part/Machine.h | 2 + 6 files changed, 254 insertions(+), 213 deletions(-) diff --git a/src/Main.cpp b/src/Main.cpp index 2f204fd7..7bb91c7e 100755 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -1,27 +1,10 @@ #include "util/Timer.h" -#include "renderer/camera/SimpleCamera.h" - -#include - - - -#include "universe/vehicle/Vehicle.h" #include "assets/Model.h" - #include "physics/ground/GroundShape.h" -#include "renderer/lighting/SunLight.h" - #include "OSP.h" - -#include "universe/vehicle/VehicleLoader.h" #include "game/scenes/flight/FlightScene.h" #include "game/scenes/editor/EditorScene.h" - -#include - -#include - #include static int iteration = 0; @@ -38,12 +21,12 @@ int main(int argc, char** argv) PROFILE_FUNC(); - osp.game_state.load_scene(new FlightScene()); + osp.game_state.load_scene(new EditorScene()); while (osp.should_loop()) { PROFILE_BLOCK("frame"); - + osp.start_frame(); osp.update(); osp.render(); diff --git a/src/assets/Model.cpp b/src/assets/Model.cpp index bcb5c53d..dfaffc10 100755 --- a/src/assets/Model.cpp +++ b/src/assets/Model.cpp @@ -171,13 +171,15 @@ void Model::process_mesh(aiMesh* mesh, const aiScene* scene, Node* to, bool draw ai_mat->GetTexture(type_t, 0, &path); std::string path_s = std::string(path.C_Str()); + logger->info("Loading texture: {}", path_s); // Sanitize the path replace_all(path_s, "\\", "/"); // We find 'res/', after it is the package name, and then the name, pretty easy size_t pos = path_s.find("res/"); if (pos == std::string::npos) { - logger->warn("Invalid path '{}' given in texture, ignoring!", path_s); + // It may be a relative path + logger->warn("Invalid path '{}' given in texture, ignoring! Make sure you enable absolute path mode", path_s); } else { diff --git a/src/game/scenes/editor/interfaces/WireInterface.cpp b/src/game/scenes/editor/interfaces/WireInterface.cpp index 5021ea50..82f405ae 100755 --- a/src/game/scenes/editor/interfaces/WireInterface.cpp +++ b/src/game/scenes/editor/interfaces/WireInterface.cpp @@ -62,18 +62,18 @@ bool WireInterface::do_interface(const CameraUniforms& cu, glm::dvec3 ray_start, if(!see_all) { std::vector all_pieces; - for(auto pair : p->pieces) + for(const auto& pair : p->pieces) { all_pieces.push_back(pair.second); } - for(Piece* p : all_pieces) + for(Piece* sp : all_pieces) { - EditorVehicleInterface::RaycastResult res = - edveh_int->raycast(cu.cam_pos, - to_dvec3(p->packed_tform.getOrigin()), false, all_pieces); + EditorVehicleInterface::RaycastResult sres = + edveh_int->raycast(cu.cam_pos, + to_dvec3(sp->packed_tform.getOrigin()), false, all_pieces); - if(!res.has_hit) + if(!sres.has_hit) { visible = true; } @@ -95,26 +95,46 @@ bool WireInterface::do_interface(const CameraUniforms& cu, glm::dvec3 ray_start, int rnd_idx = 0; for(auto& pair : visible_machines) { + std::vector machines_with_marker; + + for(auto it = pair.second.begin(); it != pair.second.end(); ) + { + Machine* m = *it; + if(m->editor_location_marker.empty()) + { + machines_with_marker.push_back(m); + it = pair.second.erase(it); + } + else + { + it++; + } + } + Part* p = pair.first; glm::dvec3 pos = to_dvec3(p->get_piece("p_root")->packed_tform.getOrigin()); - auto[clip, in_front] = MathUtil::world_to_clip(cu.tform, pos); - glm::vec2 screen_pos = glm::round(MathUtil::clip_to_screen(clip, vport)); // Draw the polygon with the machines on its edges // and a central "indicator" - std::vector machines = pair.second; - float dangle = glm::two_pi() / machines.size(); + int polygon_machines = machines_with_marker.size(); + + float dangle = glm::two_pi() / polygon_machines; + float scale = glm::clamp(200.0f / ((float)glm::distance2(pos, cu.cam_pos)), 0.5f, 1.0f); float real_icon_size = 22.0f; float icon_size = real_icon_size * scale; + // This formula gives appropiate icon distancing - float radius = glm::max((float)log(machines.size()) * icon_size * scale * 0.6f, icon_size); - if(machines.size() == 1) + float radius = glm::max((float)log(polygon_machines) * icon_size * scale * 0.6f, icon_size); + if(polygon_machines == 1) { radius = 0.0f; } + auto[clip, in_front] = MathUtil::world_to_clip(cu.tform, pos); + glm::vec2 screen_pos = glm::round(MathUtil::clip_to_screen(clip, vport)); + // Indicator - if(machines.size() > 1 && in_front) + if(polygon_machines > 1 && in_front) { nvgBeginPath(vg); nvgCircle(vg, screen_pos.x, screen_pos.y, 4.0f); @@ -122,199 +142,44 @@ bool WireInterface::do_interface(const CameraUniforms& cu, glm::dvec3 ray_start, nvgFill(vg); } - int i = 0; - for(float angle = 0.0f; angle < glm::two_pi(); angle += dangle) + float angle = 0.0f; + for(int i = 0; i < polygon_machines; i++) { - Machine* m = machines[i]; + Machine* m = pair.second[i]; float adj_angle = angle + glm::half_pi(); glm::vec2 offset = glm::vec2(sin(adj_angle), cos(adj_angle)); glm::vec2 final_pos = glm::round(screen_pos + offset * radius); - if(!in_front) - { - // Calculate final_pos from relative position to the camera of - // the machine in 3D space instead of screen space - glm::dvec3 rel = cu.cam_pos - pos; - final_pos.y = rel.z * 1e10f + vport.y + vport.w * 0.5f; - final_pos.x = 2.0f * vport.x + vport.z - final_pos.x; - } - - bool contained_in_wired = false; - for(Machine *sm : selected_wired) - { - if(sm == m) - { - contained_in_wired = true; - break; - } - } - if(m == selected) - { - contained_in_wired = true; - } + bool was_visible = do_machine(vg, gui_input, m, final_pos, pos, vport, in_front, seen_positions, rnd_idx, + polygon_machines, new_wire, machine_to_pos); - // Clamp to screen edges - glm::vec2 old_final_pos = final_pos; - final_pos = glm::clamp(final_pos, glm::vec2(vport.x, vport.y) + real_icon_size * 0.5f, - glm::vec2(vport.x + vport.z, vport.y + vport.w) - real_icon_size * 0.5f); - - bool clamped = final_pos != old_final_pos; - - bool done = false; - int its = 0; - while(!done && its < 20 && clamped) + // Indicator + if(was_visible && polygon_machines != 1) { - - bool any = false; - // Make sure there are no overlaps - // It also gives special priority to top-bottom overlaps, - // as side overlaps will be rarer due to the camera movement - // TODO: Write a better method, this gets a bit jittery - for(glm::vec2 other_pos : seen_positions) - { - float dist = glm::distance(other_pos, final_pos); - if(dist < real_icon_size) - { - glm::vec2 rand_offset; - if(rnd_idx % 2 == 0) - { - rand_offset = glm::vec2(1.0f, 0.0f); - } - else - { - rand_offset = glm::vec2(-1.0f, 0.0f); - } - final_pos += rand_offset * real_icon_size * 2.0f; - - - any = true; - break; - } - } - - done = any == false; - its++; - - } - - final_pos = glm::clamp(final_pos, glm::vec2(vport.x, vport.y) + real_icon_size * 0.5f, - glm::vec2(vport.x + vport.z, vport.y + vport.w) - real_icon_size * 0.5f); - - bool visible = contained_in_wired || (!clamped && in_front); - - glm::vec2 rect_pos = glm::round(final_pos - icon_size * 0.5f); - glm::vec2 tex_pos = glm::round(final_pos + icon_size * 0.5f); - - glm::vec2 fpos = rect_pos + glm::vec2(icon_size * 0.5f); - machine_to_pos[m] = std::make_pair(fpos, in_front); - - - if(visible) - { - // Indicator - if(machines.size() > 1) - { - nvgBeginPath(vg); - nvgMoveTo(vg, screen_pos.x, screen_pos.y); - nvgLineTo(vg, round(screen_pos.x + offset.x * 4.0f), round(screen_pos.y + offset.y * 4.0f)); - nvgStrokeColor(vg, nvgRGB(255, 255, 255)); - nvgStrokeWidth(vg, 2.0f); - nvgStroke(vg); - } - - - bool is_hovered = gui_input->mouse_inside(rect_pos, glm::ivec2(icon_size)); - - if(is_hovered || selected == m || contained_in_wired) - { - hovered = m; - // Hover / Selected indicator - nvgBeginPath(vg); - //nvgRect(vg, rect_pos.x - 0.5f, rect_pos.y - 0.5f, icon_size + 1.0f, icon_size + 1.0f); - nvgCircle(vg, rect_pos.x + icon_size * 0.5f, rect_pos.y + icon_size * 0.5f, icon_size * 0.71f); - if((gui_input->mouse_down(GUI_LEFT_BUTTON) && is_hovered) || selected == m) - { - selected = m; - selected_wired = m->get_all_wired_machines(false); - nvgFillColor(vg, nvgRGB(255, 255, 255)); - } - else - { - nvgFillColor(vg, nvgRGBA(0, 0, 0, 128)); - } - nvgFill(vg); - if(is_hovered && selected != nullptr && selected != m) - { - if(contained_in_wired) - { - // Remove (RED) - nvgStrokeColor(vg, nvgRGB(255, 0, 0)); - if(gui_input->mouse_down(GUI_RIGHT_BUTTON)) - { - // We have to do two iterations - auto sel_to_m = edveh->veh->wires.equal_range(selected); - auto m_to_sel = edveh->veh->wires.equal_range(m); - for(auto i = sel_to_m.first; i != sel_to_m.second; i++) - { - if(i->second == m) - { - edveh->veh->wires.erase(i); - break; - } - } - for(auto i = m_to_sel.first; i != m_to_sel.second; i++) - { - if(i->second == selected) - { - edveh->veh->wires.erase(i); - break; - } - } - - } - } - else - { - // Create (GREEN) - nvgStrokeColor(vg, nvgRGB(0, 255, 0)); - if(gui_input->mouse_down(GUI_RIGHT_BUTTON)) - { - edveh->veh->wires.insert(std::make_pair(selected, m)); - edveh->veh->wires.insert(std::make_pair(m, selected)); - } - else - { - new_wire = m; - } - } - nvgStrokeWidth(vg, 2.0f); - } - else - { - nvgStrokeColor(vg, nvgRGB(255, 255, 255)); - } - nvgStroke(vg); - nvgStrokeWidth(vg, 1.0f); - - } - - // Machine - AssetHandle img = m->get_icon(); - int image = nvglCreateImageFromHandleGL3(vg, img->id, img->get_width(), img->get_height(), 0); - NVGpaint paint = nvgImagePattern(vg, tex_pos.x, tex_pos.y, icon_size, icon_size, 0.0f, image, 1.0f); nvgBeginPath(vg); - nvgRect(vg, rect_pos.x, rect_pos.y, icon_size, icon_size); - nvgFillPaint(vg, paint); - nvgFill(vg); + nvgMoveTo(vg, screen_pos.x, screen_pos.y); + nvgLineTo(vg, round(screen_pos.x + offset.x * 4.0f), round(screen_pos.y + offset.y * 4.0f)); + nvgStrokeColor(vg, nvgRGB(255, 255, 255)); + nvgStrokeWidth(vg, 2.0f); + nvgStroke(vg); + } - seen_positions.push_back(final_pos); + angle += dangle; - } + } + // Draw the machines at a marker location + for(Machine* m : pair.second) + { - i++; + glm::dvec3 mpos = p->get_piece("p_root")->get_marker_position(m->editor_location_marker); + mpos = to_dmat4(p->get_piece("p_root")->get_global_transform()) * glm::dvec4(mpos, 1.0); + auto[clip, in_front] = MathUtil::world_to_clip(cu.tform, mpos); + glm::vec2 mscreen_pos = glm::round(MathUtil::clip_to_screen(clip, vport)); + do_machine(vg, gui_input, m, mscreen_pos, pos, vport, in_front, seen_positions, rnd_idx, + polygon_machines, new_wire, machine_to_pos); } rnd_idx++; @@ -410,6 +275,188 @@ void WireInterface::see_part(Part* p) } if(!mch.empty()) { - visible_machines.push_back(std::make_pair(p, mch)); + visible_machines.emplace_back(p, mch); + } +} + +bool WireInterface::do_machine(NVGcontext* vg, GUIInput* gui_input, Machine* m, glm::vec2 final_pos, glm::dvec3 pos, + glm::ivec4 vport, bool in_front, std::vector& seen_positions, + int rnd_idx, int polygon_machines, Machine*& new_wire, + std::unordered_map>& machine_to_pos) +{ + float scale = glm::clamp(200.0f / ((float)glm::distance2(pos, cu.cam_pos)), 0.5f, 1.0f); + float real_icon_size = 22.0f; + float icon_size = real_icon_size * scale; + + if(!in_front) + { + // Calculate final_pos from relative position to the camera of + // the machine in 3D space instead of screen space + glm::dvec3 rel = cu.cam_pos - pos; + final_pos.y = rel.z * 1e10f + vport.y + vport.w * 0.5f; + final_pos.x = 2.0f * vport.x + vport.z - final_pos.x; + } + + bool contained_in_wired = false; + for(Machine *sm : selected_wired) + { + if(sm == m) + { + contained_in_wired = true; + break; + } + } + if(m == selected) + { + contained_in_wired = true; + } + + // Clamp to screen edges + glm::vec2 old_final_pos = final_pos; + final_pos = glm::clamp(final_pos, glm::vec2(vport.x, vport.y) + real_icon_size * 0.5f, + glm::vec2(vport.x + vport.z, vport.y + vport.w) - real_icon_size * 0.5f); + + bool clamped = final_pos != old_final_pos; + + bool done = false; + int its = 0; + while(!done && its < 20 && clamped) + { + + bool any = false; + // Make sure there are no overlaps + // It also gives special priority to top-bottom overlaps, + // as side overlaps will be rarer due to the camera movement + // TODO: Write a better method, this gets a bit jittery + for(glm::vec2 other_pos : seen_positions) + { + float dist = glm::distance(other_pos, final_pos); + if(dist < real_icon_size) + { + glm::vec2 rand_offset; + if(rnd_idx % 2 == 0) + { + rand_offset = glm::vec2(1.0f, 0.0f); + } + else + { + rand_offset = glm::vec2(-1.0f, 0.0f); + } + final_pos += rand_offset * real_icon_size * 2.0f; + + + any = true; + break; + } + } + + done = !any; + its++; + + } + + final_pos = glm::clamp(final_pos, glm::vec2(vport.x, vport.y) + real_icon_size * 0.5f, + glm::vec2(vport.x + vport.z, vport.y + vport.w) - real_icon_size * 0.5f); + + bool visible = contained_in_wired || (!clamped && in_front); + + + if(visible) + { + glm::vec2 rect_pos = glm::round(final_pos - icon_size * 0.5f); + glm::vec2 tex_pos = glm::round(final_pos + icon_size * 0.5f); + + glm::vec2 fpos = rect_pos + glm::vec2(icon_size * 0.5f); + machine_to_pos[m] = std::make_pair(fpos, in_front); + + bool is_hovered = gui_input->mouse_inside(rect_pos, glm::ivec2(icon_size)); + + if(is_hovered || selected == m || contained_in_wired) + { + hovered = m; + // Hover / Selected indicator + nvgBeginPath(vg); + //nvgRect(vg, rect_pos.x - 0.5f, rect_pos.y - 0.5f, icon_size + 1.0f, icon_size + 1.0f); + nvgCircle(vg, rect_pos.x + icon_size * 0.5f, rect_pos.y + icon_size * 0.5f, icon_size * 0.71f); + if((gui_input->mouse_down(GUI_LEFT_BUTTON) && is_hovered) || selected == m) + { + selected = m; + selected_wired = m->get_all_wired_machines(false); + nvgFillColor(vg, nvgRGB(255, 255, 255)); + } + else + { + nvgFillColor(vg, nvgRGBA(0, 0, 0, 128)); + } + nvgFill(vg); + if(is_hovered && selected != nullptr && selected != m) + { + if(contained_in_wired) + { + // Remove (RED) + nvgStrokeColor(vg, nvgRGB(255, 0, 0)); + if(gui_input->mouse_down(GUI_RIGHT_BUTTON)) + { + // We have to do two iterations + auto sel_to_m = edveh->veh->wires.equal_range(selected); + auto m_to_sel = edveh->veh->wires.equal_range(m); + for(auto i = sel_to_m.first; i != sel_to_m.second; i++) + { + if(i->second == m) + { + edveh->veh->wires.erase(i); + break; + } + } + for(auto i = m_to_sel.first; i != m_to_sel.second; i++) + { + if(i->second == selected) + { + edveh->veh->wires.erase(i); + break; + } + } + + } + } + else + { + // Create (GREEN) + nvgStrokeColor(vg, nvgRGB(0, 255, 0)); + if(gui_input->mouse_down(GUI_RIGHT_BUTTON)) + { + edveh->veh->wires.insert(std::make_pair(selected, m)); + edveh->veh->wires.insert(std::make_pair(m, selected)); + } + else + { + new_wire = m; + } + } + nvgStrokeWidth(vg, 2.0f); + } + else + { + nvgStrokeColor(vg, nvgRGB(255, 255, 255)); + } + nvgStroke(vg); + nvgStrokeWidth(vg, 1.0f); + + } + + // Machine + AssetHandle img = m->get_icon(); + int image = nvglCreateImageFromHandleGL3(vg, img->id, img->get_width(), img->get_height(), 0); + NVGpaint paint = nvgImagePattern(vg, tex_pos.x, tex_pos.y, icon_size, icon_size, 0.0f, image, 1.0f); + nvgBeginPath(vg); + nvgRect(vg, rect_pos.x, rect_pos.y, icon_size, icon_size); + nvgFillPaint(vg, paint); + nvgFill(vg); + + seen_positions.push_back(final_pos); } + + return visible; + } + diff --git a/src/game/scenes/editor/interfaces/WireInterface.h b/src/game/scenes/editor/interfaces/WireInterface.h index 20245c8a..1866e3c1 100755 --- a/src/game/scenes/editor/interfaces/WireInterface.h +++ b/src/game/scenes/editor/interfaces/WireInterface.h @@ -24,6 +24,7 @@ class WireInterface : public BaseInterface void see_part(Part* p); + public: virtual void update(double dt) override; @@ -37,4 +38,8 @@ class WireInterface : public BaseInterface WireInterface(EditorVehicleInterface* edveh_int); + bool do_machine(NVGcontext *vg, GUIInput *gui_input, Machine *m, glm::vec2 final_pos, glm::dvec3 pos, + glm::ivec4 vport, bool in_front, std::vector &seen_positions, int rnd_idx, + int polygon_machines, Machine *&new_wire, + std::unordered_map>& machine_to_pos); }; diff --git a/src/universe/vehicle/part/Machine.cpp b/src/universe/vehicle/part/Machine.cpp index cf1cacd3..09de3f71 100755 --- a/src/universe/vehicle/part/Machine.cpp +++ b/src/universe/vehicle/part/Machine.cpp @@ -12,6 +12,8 @@ Machine::Machine(std::shared_ptr init_toml, std::string cur_pkg) { this->init_toml = init_toml; this->in_pkg = cur_pkg; + this->editor_location_marker = init_toml->get_as("__editor_marker").value_or(""); + default_icon = AssetHandle("core:machines/icons/default_icon.png"); } diff --git a/src/universe/vehicle/part/Machine.h b/src/universe/vehicle/part/Machine.h index 3974a2e3..5e81634d 100755 --- a/src/universe/vehicle/part/Machine.h +++ b/src/universe/vehicle/part/Machine.h @@ -33,6 +33,8 @@ class Machine std::shared_ptr init_toml; std::unordered_map interfaces; + // May be "", in that case the machine is "centered" on the piece + std::string editor_location_marker; // pre_update is mostly used for input void pre_update(double dt);