mirror of
https://github.com/Nexoniarz/Fry3DX.git
synced 2026-11-03 04:24:26 +00:00
Fly camera (as in games, Unreal, Unity): right drag turns it where it stands, WASD (Q / E down, up) while held and the arrows any time move where it looks, the wheel goes forward and back (never creeping to a stop), Shift + wheel sets how fast every way of moving goes (kept), Alt + right drag orbits round what's picked; it keeps track of what it looks at (the near plane and shade in corners go by what's nearest). Close up in Rendered no longer turns into a pattern: the shade in corners reaches a share of the view (its +1 a share of the reach in the engine). The grid made again: four sets of lines, each line's look only from how far apart it and its like are on the screen (no jumps as the view moves or the steps change, no gaps, soft edges), pieces short near the eye. F3 and Escape close the command search (the engine takes Escape first). fry_hold / fry_mouse / fry_orbit target for trying it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
de749dba11
commit
415f8fa352
9 changed files with 206 additions and 80 deletions
|
|
@ -102,8 +102,10 @@ Five tabs along the top: **Model**, **Paint**, **Layout**, **Render**, **Print**
|
||||||
- **Layout tab**: move, turn and size the flat frames of the surface yourself; the paint follows.
|
- **Layout tab**: move, turn and size the flat frames of the surface yourself; the paint follows.
|
||||||
|
|
||||||
**Seeing it**
|
**Seeing it**
|
||||||
- **Getting round**: right drag orbits, middle drag pans, the wheel goes towards what's under the
|
- **Getting round**, as in games, Unreal or Unity: right drag turns the camera where it stands,
|
||||||
mouse; the **arrow keys** move you along the ground (Page Up / Down: up and down, Shift: faster).
|
and while it's held W A S D move where you look (Q / E: down, up). The **arrow keys** move any
|
||||||
|
time (Page Up / Down: down and up), the **wheel** goes forward and back, **Shift + wheel** sets
|
||||||
|
how fast it all goes. Alt + right drag orbits round what's picked; middle drag pans.
|
||||||
- **Solid** view for modelling, **Rendered** with a real sky, sun and soft shadows (Z). Wireframe
|
- **Solid** view for modelling, **Rendered** with a real sky, sun and soft shadows (Z). Wireframe
|
||||||
(Shift+Z), X-ray (Alt+Z). The Render tab: the sun, sky, ground, brightness, shadows, glow;
|
(Shift+Z), X-ray (Alt+Z). The Render tab: the sun, sky, ground, brightness, shadows, glow;
|
||||||
F12 saves a picture of just the view.
|
F12 saves a picture of just the view.
|
||||||
|
|
|
||||||
|
|
@ -745,7 +745,7 @@ void Renderer::buildPacket(uint32_t width, uint32_t height) {
|
||||||
p.exposure *= glm::mix(1.0f, 0.3f, std::clamp(p.frame.weather.z, 0.0f, 1.0f));
|
p.exposure *= glm::mix(1.0f, 0.3f, std::clamp(p.frame.weather.z, 0.0f, 1.0f));
|
||||||
const float tanY = std::tan(glm::radians(camera_.fovY) * 0.5f);
|
const float tanY = std::tan(glm::radians(camera_.fovY) * 0.5f);
|
||||||
p.post.aoStrength = std::clamp(r_ssao.f(), 0.0f, 1.0f); // (the TAA fields were set with the camera above)
|
p.post.aoStrength = std::clamp(r_ssao.f(), 0.0f, 1.0f); // (the TAA fields were set with the camera above)
|
||||||
p.post.aoRadius = std::max(r_ssao_radius.f(), 1.0f);
|
p.post.aoRadius = std::max(r_ssao_radius.f(), 0.001f); // (small for close-up work: a modeller sets it to its view)
|
||||||
p.post.bloomStrength = std::clamp(r_bloom.f(), 0.0f, 1.0f);
|
p.post.bloomStrength = std::clamp(r_bloom.f(), 0.0f, 1.0f);
|
||||||
p.post.saturation = r_saturation.f();
|
p.post.saturation = r_saturation.f();
|
||||||
p.post.vignette = r_vignette.f();
|
p.post.vignette = r_vignette.f();
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,8 @@ void main() {
|
||||||
vec3 v = viewPos(q) - p;
|
vec3 v = viewPos(q) - p;
|
||||||
float vv = dot(v, v);
|
float vv = dot(v, v);
|
||||||
float falloff = max(0.0, 1.0 - vv / (pc.radius * pc.radius));
|
float falloff = max(0.0, 1.0 - vv / (pc.radius * pc.radius));
|
||||||
occlusion += max(0.0, dot(v, n) - 0.01 * p.z) / (vv + 1.0) * falloff;
|
// (+ a little against dividing by nothing: its share of the reach, so any scale looks the same)
|
||||||
|
occlusion += max(0.0, dot(v, n) - 0.01 * p.z) / (vv + pc.radius * pc.radius * 0.0017) * falloff;
|
||||||
}
|
}
|
||||||
float ao = max(0.0, 1.0 - 2.0 * occlusion / float(kSamples) * pc.radius * 0.6);
|
float ao = max(0.0, 1.0 - 2.0 * occlusion / float(kSamples) * pc.radius * 0.6);
|
||||||
o_ao = vec4(mix(1.0, ao * ao, pc.strength));
|
o_ao = vec4(mix(1.0, ao * ao, pc.strength));
|
||||||
|
|
|
||||||
27
src/app.cpp
27
src/app.cpp
|
|
@ -21,6 +21,7 @@ keel::Cvar fry_touch("fry_touch", "0", keel::CVAR_ARCHIVE, "Fry3DX: the phone la
|
||||||
#endif
|
#endif
|
||||||
keel::Cvar fry_uiscale("fry_uiscale", "0", 0, "Fry3DX: draw the UI this many times bigger (0: by the screen)");
|
keel::Cvar fry_uiscale("fry_uiscale", "0", 0, "Fry3DX: draw the UI this many times bigger (0: by the screen)");
|
||||||
keel::Cvar fry_uisize("fry_uisize", "1", keel::CVAR_ARCHIVE, "Fry3DX: the phone layout's size, times");
|
keel::Cvar fry_uisize("fry_uisize", "1", keel::CVAR_ARCHIVE, "Fry3DX: the phone layout's size, times");
|
||||||
|
keel::Cvar fry_movespeed("fry_movespeed", "1", keel::CVAR_ARCHIVE, "Fry3DX: how fast the camera moves (keys, wheel, pan): Shift + wheel changes it");
|
||||||
keel::Cvar fry_theme("fry_theme", "0", keel::CVAR_ARCHIVE, "Fry3DX: how it looks: 0 like the desktop, 1 dark, 2 light");
|
keel::Cvar fry_theme("fry_theme", "0", keel::CVAR_ARCHIVE, "Fry3DX: how it looks: 0 like the desktop, 1 dark, 2 light");
|
||||||
keel::Cvar fry_pluginsoff("fry_pluginsoff", "", keel::CVAR_ARCHIVE, "Fry3DX: plug-ins turned off (their file names, with | between)");
|
keel::Cvar fry_pluginsoff("fry_pluginsoff", "", keel::CVAR_ARCHIVE, "Fry3DX: plug-ins turned off (their file names, with | between)");
|
||||||
keel::Cvar fry_autosave("fry_autosave", "2", keel::CVAR_ARCHIVE, "Fry3DX: autosave every this many minutes while there are changes (0: off)");
|
keel::Cvar fry_autosave("fry_autosave", "2", keel::CVAR_ARCHIVE, "Fry3DX: autosave every this many minutes while there are changes (0: off)");
|
||||||
|
|
@ -54,7 +55,8 @@ auto App::init() -> void {
|
||||||
static keel::Cmd orbit("fry_orbit", [](const keel::Args& a) {
|
static keel::Cmd orbit("fry_orbit", [](const keel::Args& a) {
|
||||||
if (a.size() > 2) self->view_ = View::Perspective, self->fov_ = 50, self->yaw_ = std::stof(a[1]), self->pitch_ = std::stof(a[2]);
|
if (a.size() > 2) self->view_ = View::Perspective, self->fov_ = 50, self->yaw_ = std::stof(a[1]), self->pitch_ = std::stof(a[2]);
|
||||||
if (a.size() > 3) self->dist_ = std::stof(a[3]);
|
if (a.size() > 3) self->dist_ = std::stof(a[3]);
|
||||||
}, "Fry3DX: fry_orbit yaw pitch [distance]");
|
if (a.size() > 6) self->target_ = {std::stof(a[4]), std::stof(a[5]), std::stof(a[6])};
|
||||||
|
}, "Fry3DX: fry_orbit yaw pitch [distance [target x y z]]");
|
||||||
// A sculpt stroke across the view (0..1 of it), for pictures of it: brush 1-6, then from and to.
|
// A sculpt stroke across the view (0..1 of it), for pictures of it: brush 1-6, then from and to.
|
||||||
static keel::Cmd sculptStroke("fry_sculpt", [](const keel::Args& a) {
|
static keel::Cmd sculptStroke("fry_sculpt", [](const keel::Args& a) {
|
||||||
if (!self->viewReady()) return self->say("fry_sculpt: the view isn't up yet (+wait 1 first)");
|
if (!self->viewReady()) return self->say("fry_sculpt: the view isn't up yet (+wait 1 first)");
|
||||||
|
|
@ -264,6 +266,16 @@ auto App::init() -> void {
|
||||||
}, "Fry3DX: fry_bench orbit|smooth|angle|round frames: time that many frames while the camera turns or the picked object's setting is dragged");
|
}, "Fry3DX: fry_bench orbit|smooth|angle|round frames: time that many frames while the camera turns or the picked object's setting is dragged");
|
||||||
static keel::Cmd hold("fry_hold", [](const keel::Args& a) { // a key held down for some frames, as if pressed (for trying keys out)
|
static keel::Cmd hold("fry_hold", [](const keel::Args& a) { // a key held down for some frames, as if pressed (for trying keys out)
|
||||||
if (a.size() < 3) return self->say("fry_hold key frames (UpArrow, PageUp, LeftShift...)");
|
if (a.size() < 3) return self->say("fry_hold key frames (UpArrow, PageUp, LeftShift...)");
|
||||||
|
if (a[1] == "MouseRight") { // (pressed now, let go after that many frames)
|
||||||
|
ImGui::GetIO().AddMouseButtonEvent(ImGuiMouseButton_Right, true);
|
||||||
|
return self->holding_.push_back({ImGuiKey_MouseRight, std::stoi(a[2])});
|
||||||
|
}
|
||||||
|
if (a[1] == "Escape") { // (the engine takes this one from the window's own events, before the panels)
|
||||||
|
SDL_Event e{};
|
||||||
|
e.type = SDL_EVENT_KEY_DOWN, e.key.scancode = SDL_SCANCODE_ESCAPE, e.key.key = SDLK_ESCAPE, e.key.down = true;
|
||||||
|
SDL_PushEvent(&e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (int k = ImGuiKey_NamedKey_BEGIN; k < ImGuiKey_NamedKey_END; ++k)
|
for (int k = ImGuiKey_NamedKey_BEGIN; k < ImGuiKey_NamedKey_END; ++k)
|
||||||
if (a[1] == ImGui::GetKeyName(ImGuiKey(k))) {
|
if (a[1] == ImGui::GetKeyName(ImGuiKey(k))) {
|
||||||
ImGui::GetIO().AddKeyEvent(ImGuiKey(k), true);
|
ImGui::GetIO().AddKeyEvent(ImGuiKey(k), true);
|
||||||
|
|
@ -271,6 +283,10 @@ auto App::init() -> void {
|
||||||
}
|
}
|
||||||
self->say("fry_hold: no key called " + a[1]);
|
self->say("fry_hold: no key called " + a[1]);
|
||||||
}, "Fry3DX: fry_hold key frames: hold a key down (UpArrow, PageUp...)");
|
}, "Fry3DX: fry_hold key frames: hold a key down (UpArrow, PageUp...)");
|
||||||
|
static keel::Cmd mouse("fry_mouse", [](const keel::Args& a) { // the mouse to x y (0..1 of the view)
|
||||||
|
if (a.size() < 3) return;
|
||||||
|
ImGui::GetIO().AddMousePosEvent(self->viewMin_.x + (self->viewMax_.x - self->viewMin_.x) * std::stof(a[1]), self->viewMin_.y + (self->viewMax_.y - self->viewMin_.y) * std::stof(a[2]));
|
||||||
|
}, "Fry3DX: fry_mouse x y: the mouse to that point of the view");
|
||||||
static keel::Cmd wheel("fry_wheel", [](const keel::Args& a) { // the wheel turned at x y (0..1 of the view)
|
static keel::Cmd wheel("fry_wheel", [](const keel::Args& a) { // the wheel turned at x y (0..1 of the view)
|
||||||
if (a.size() < 4) return;
|
if (a.size() < 4) return;
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
@ -459,7 +475,10 @@ auto App::allowQuit() -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto App::onEscape() -> void {
|
auto App::onEscape() -> void {
|
||||||
if (askSave_) askSave_ = 0;
|
// (the engine takes Escape before the panels see it: whatever's open on top goes first)
|
||||||
|
if (search_.open) search_.open = false;
|
||||||
|
else if (pie_) pie_ = 0;
|
||||||
|
else if (askSave_) askSave_ = 0;
|
||||||
else if (showRecover_) showRecover_ = false;
|
else if (showRecover_) showRecover_ = false;
|
||||||
else if (editLayer()) cancelFloating();
|
else if (editLayer()) cancelFloating();
|
||||||
else if (!knifePts_.empty()) knifePts_.clear();
|
else if (!knifePts_.empty()) knifePts_.clear();
|
||||||
|
|
@ -476,7 +495,7 @@ auto App::onEscape() -> void {
|
||||||
// (fry_bench) Each frame: the change being timed.
|
// (fry_bench) Each frame: the change being timed.
|
||||||
auto App::benchStep() -> void {
|
auto App::benchStep() -> void {
|
||||||
for (auto& [k, left] : holding_)
|
for (auto& [k, left] : holding_)
|
||||||
if (--left <= 0) ImGui::GetIO().AddKeyEvent(k, false);
|
if (--left <= 0) k == ImGuiKey_MouseRight ? ImGui::GetIO().AddMouseButtonEvent(ImGuiMouseButton_Right, false) : ImGui::GetIO().AddKeyEvent(k, false);
|
||||||
std::erase_if(holding_, [](const auto& h) { return h.second <= 0; });
|
std::erase_if(holding_, [](const auto& h) { return h.second <= 0; });
|
||||||
if (bench_.left <= 0) return;
|
if (bench_.left <= 0) return;
|
||||||
const int i = int(bench_.ms.size());
|
const int i = int(bench_.ms.size());
|
||||||
|
|
@ -1628,7 +1647,7 @@ auto App::setTab(Tab t) -> void {
|
||||||
// ───────────────────────────── keys ─────────────────────────────
|
// ───────────────────────────── keys ─────────────────────────────
|
||||||
|
|
||||||
auto App::shortcuts(ImGuiIO& io) -> void {
|
auto App::shortcuts(ImGuiIO& io) -> void {
|
||||||
if (io.WantTextInput || slide_.active || drag_.active) return; // (a slide, a drag: their own keys)
|
if (io.WantTextInput || slide_.active || drag_.active || looking_) return; // (a slide, a drag: their own keys; turning the camera: WASD, Q and E move it)
|
||||||
if (tool_ == Tool::Draw && !drawPts_.empty() && sketchKeys(io)) return;
|
if (tool_ == Tool::Draw && !drawPts_.empty() && sketchKeys(io)) return;
|
||||||
const bool ctrl = io.KeyCtrl, shift = io.KeyShift, alt = io.KeyAlt;
|
const bool ctrl = io.KeyCtrl, shift = io.KeyShift, alt = io.KeyAlt;
|
||||||
auto key = [](ImGuiKey k) { return ImGui::IsKeyPressed(k, false); };
|
auto key = [](ImGuiKey k) { return ImGui::IsKeyPressed(k, false); };
|
||||||
|
|
|
||||||
|
|
@ -275,6 +275,7 @@ extern keel::Cvar fry_touch;
|
||||||
extern keel::Cvar fry_uiscale;
|
extern keel::Cvar fry_uiscale;
|
||||||
extern keel::Cvar fry_uisize;
|
extern keel::Cvar fry_uisize;
|
||||||
extern keel::Cvar fry_theme;
|
extern keel::Cvar fry_theme;
|
||||||
|
extern keel::Cvar fry_movespeed;
|
||||||
extern keel::Cvar fry_pluginsoff;
|
extern keel::Cvar fry_pluginsoff;
|
||||||
extern keel::Cvar fry_autosave;
|
extern keel::Cvar fry_autosave;
|
||||||
|
|
||||||
|
|
@ -356,7 +357,8 @@ private:
|
||||||
ImVec2 viewMin_{0, 0}, viewMax_{1, 1};
|
ImVec2 viewMin_{0, 0}, viewMax_{1, 1};
|
||||||
glm::mat4 vp_{1.0f};
|
glm::mat4 vp_{1.0f};
|
||||||
glm::vec3 eye_{0.0f};
|
glm::vec3 eye_{0.0f};
|
||||||
bool orbiting_ = false, panning_ = false;
|
bool orbiting_ = false, panning_ = false, looking_ = false; // (looking: right drag turns the camera where it stands)
|
||||||
|
float nearest_ = 1.0f; // (how far the nearest thing is from the eye, m: worked out with the view)
|
||||||
bool xray_ = false, showGrid_ = true, renderGrid_ = false;
|
bool xray_ = false, showGrid_ = true, renderGrid_ = false;
|
||||||
std::string appliedLook_;
|
std::string appliedLook_;
|
||||||
keel::ModelId ground_{};
|
keel::ModelId ground_{};
|
||||||
|
|
@ -1464,6 +1466,11 @@ private:
|
||||||
std::vector<double> ms;
|
std::vector<double> ms;
|
||||||
} bench_;
|
} bench_;
|
||||||
std::vector<std::pair<ImGuiKey, int>> holding_; // (fry_hold: keys held down for that many more frames)
|
std::vector<std::pair<ImGuiKey, int>> holding_; // (fry_hold: keys held down for that many more frames)
|
||||||
|
float aoReach() const;
|
||||||
|
void lookBy(ImVec2 d);
|
||||||
|
void refocus();
|
||||||
|
float nearestThing();
|
||||||
|
float viewScale() const { return std::max(std::min(dist_, nearest_), 0.001f); } // (how big the view is where it matters: what's ahead, or nearer)
|
||||||
void benchStep();
|
void benchStep();
|
||||||
void benchEnd(std::chrono::steady_clock::time_point began);
|
void benchEnd(std::chrono::steady_clock::time_point began);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1985,19 +1985,19 @@ auto App::statusBar() -> void {
|
||||||
else if (tab_ == Tab::Layout) {
|
else if (tab_ == Tab::Layout) {
|
||||||
for (const LayToolInfo& t : kLayTools)
|
for (const LayToolInfo& t : kLayTools)
|
||||||
if (t.tool == layTool_)
|
if (t.tool == layTool_)
|
||||||
ImGui::TextDisabled("%s: %s · Tab: frames or corners Right drag: orbit (pan in the Picture panel) F1: all keys", t.name, t.hint);
|
ImGui::TextDisabled("%s: %s · Tab: frames or corners Right drag: look (pan in the Picture panel) F1: all keys", t.name, t.hint);
|
||||||
} else if ((tab_ == Tab::Print && printTool_ != PrintTool::None) || printTool_ == PrintTool::Measure) {
|
} else if ((tab_ == Tab::Print && printTool_ != PrintTool::None) || printTool_ == PrintTool::Measure) {
|
||||||
ImGui::TextDisabled(printTool_ == PrintTool::Measure ? "Measure: click two points on the model (corners catch the mouse); a third click starts again · M (Print) or Esc: done"
|
ImGui::TextDisabled(printTool_ == PrintTool::Measure ? "Measure: click two points on the model (corners catch the mouse); a third click starts again · M (Print) or Esc: done"
|
||||||
: "Lay flat: click the face it should stand on: it turns so that face is down, on the bed · L: done");
|
: "Lay flat: click the face it should stand on: it turns so that face is down, on the bed · L: done");
|
||||||
} else if (tab_ == Tab::Paint && sculpting_) {
|
} else if (tab_ == Tab::Paint && sculpting_) {
|
||||||
for (const SculptToolInfo& t : kSculptTools)
|
for (const SculptToolInfo& t : kSculptTools)
|
||||||
if (t.brush == sculptBrush_) ImGui::TextDisabled("%s: %s · Shift: smooth X: both sides Tab: colour Right drag: orbit F1: all keys", t.name, t.hint);
|
if (t.brush == sculptBrush_) ImGui::TextDisabled("%s: %s · Shift: smooth X: both sides Tab: colour Right drag: look F1: all keys", t.name, t.hint);
|
||||||
} else if (tab_ == Tab::Paint) {
|
} else if (tab_ == Tab::Paint) {
|
||||||
for (const PaintToolInfo& t : kPaintTools)
|
for (const PaintToolInfo& t : kPaintTools)
|
||||||
if (t.tool == paintTool_) ImGui::TextDisabled("%s: %s · Right drag: orbit Wheel: zoom (in the Picture panel too) F1: all keys", t.name, t.hint);
|
if (t.tool == paintTool_) ImGui::TextDisabled("%s: %s · Right drag: look Wheel: forward / back (zoom in the Picture panel) F1: all keys", t.name, t.hint);
|
||||||
} else
|
} else
|
||||||
for (const ToolInfo& t : kTools)
|
for (const ToolInfo& t : kTools)
|
||||||
if (t.tool == tool_) ImGui::TextDisabled("%s: %s · Right drag: orbit Middle drag: pan Wheel: zoom F1: all keys", t.name, t.hint);
|
if (t.tool == tool_) ImGui::TextDisabled("%s: %s · Right drag + WASD: look and move Alt+right: orbit Wheel: forward / back F1: all keys", t.name, t.hint);
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -446,8 +446,10 @@ auto App::helpWindow() -> void {
|
||||||
rows("Finding things", {{"F3 or Ctrl+Space", "Search for any command by name and run it"}, {"Space (held)", "The tools round the mouse: point at one, let go"},
|
rows("Finding things", {{"F3 or Ctrl+Space", "Search for any command by name and run it"}, {"Space (held)", "The tools round the mouse: point at one, let go"},
|
||||||
{"Shift+Space (held)", "The views round the mouse"}});
|
{"Shift+Space (held)", "The views round the mouse"}});
|
||||||
myKeysTable();
|
myKeysTable();
|
||||||
rows("Looking around", {{"Right drag", "Orbit round what you look at"}, {"Middle drag / Shift+Right drag", "Pan"}, {"Wheel", "Zoom (towards what's under the mouse)"},
|
rows("Looking around", {{"Right drag", "Turn the camera where it stands"}, {"Right held + W A S D", "Move where you look (Q / E: down and up)"},
|
||||||
{"Arrow keys", "Move round (Page Up / Down: up and down; Shift: faster)"},
|
{"Arrow keys", "Move where you look, any time (Page Up / Down: down and up)"}, {"Wheel", "Forward and back"},
|
||||||
|
{"Shift + wheel", "How fast every way of moving goes"}, {"Shift (while moving)", "Faster"},
|
||||||
|
{"Alt + right drag", "Orbit round what's picked (or what you look at)"}, {"Middle drag / Shift+Right drag", "Pan"},
|
||||||
{"Orbit / Pan / Zoom buttons", "Drag them (touchpads)"}, {"F", "Frame the selection"}, {"Home", "Frame everything"},
|
{"Orbit / Pan / Zoom buttons", "Drag them (touchpads)"}, {"F", "Frame the selection"}, {"Home", "Frame everything"},
|
||||||
{"Numpad 1 / 3 / 7", "Front / side / top (Shift: the opposite)"}, {"Numpad 5", "Flat view / 3D"},
|
{"Numpad 1 / 3 / 7", "Front / side / top (Shift: the opposite)"}, {"Numpad 5", "Flat view / 3D"},
|
||||||
{"Z / Shift+Z", "Solid or Rendered / Wireframe"}, {"Alt+Z", "X-ray: see and pick through"}});
|
{"Z / Shift+Z", "Solid or Rendered / Wireframe"}, {"Alt+Z", "X-ray: see and pick through"}});
|
||||||
|
|
|
||||||
223
src/app_view.cpp
223
src/app_view.cpp
|
|
@ -171,57 +171,119 @@ auto App::orbitBy(ImVec2 d) -> void {
|
||||||
pitch_ = std::clamp(pitch_ + d.y * 0.3f, -89.9f, 89.9f);
|
pitch_ = std::clamp(pitch_ + d.y * 0.3f, -89.9f, 89.9f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Turning the camera where it stands (right drag): what it looks at moves, the eye doesn't.
|
||||||
|
auto App::lookBy(ImVec2 d) -> void {
|
||||||
|
if (std::abs(d.x) + std::abs(d.y) > 0.5f) leaveFlatView();
|
||||||
|
const glm::vec3 eye = target_ - viewDir() * dist_;
|
||||||
|
yaw_ -= d.x * 0.2f;
|
||||||
|
pitch_ = std::clamp(pitch_ + d.y * 0.2f, -89.9f, 89.9f);
|
||||||
|
target_ = eye + viewDir() * dist_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// What the camera looks at now: the object or the ground straight ahead (not further than the
|
||||||
|
// scene reaches). The point it turns round, how fast it moves and how near it draws follow it.
|
||||||
|
auto App::refocus() -> void {
|
||||||
|
const glm::vec3 f = viewDir(), eye = target_ - f * dist_;
|
||||||
|
const Ray ray{eye, f};
|
||||||
|
float t = -1.0f;
|
||||||
|
if (const Hit h = pickObject(ray); h.object) t = h.t;
|
||||||
|
else if (auto g = hitPlane(ray, glm::vec3(0.0f), glm::vec3(0, 0, 1))) t = std::min(glm::distance(eye, *g), std::max(glm::length(eye) * 2.0f, 50.0f));
|
||||||
|
if (t > 0.0f) dist_ = std::clamp(t, 0.01f, 5000.0f), target_ = eye + f * dist_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// How far the nearest thing (an object, the ground) is from the eye: the near plane and the
|
||||||
|
// shade in corners go by it, so looking past something close doesn't break them.
|
||||||
|
auto App::nearestThing() -> float {
|
||||||
|
float best = std::abs(eye_.z); // (the ground)
|
||||||
|
for (const Object& o : scene_.objects) {
|
||||||
|
if (!o.visible) continue;
|
||||||
|
auto& [version, radius] = radii_[o.id];
|
||||||
|
if (version != shapeKey(o) || !version) {
|
||||||
|
version = shapeKey(o), radius = 0;
|
||||||
|
for (const glm::vec3& v : o.mesh.verts) radius = std::max(radius, glm::length(v));
|
||||||
|
}
|
||||||
|
best = std::min(best, std::max(glm::distance(eye_, o.position) - radius * std::max({std::abs(o.scale.x), std::abs(o.scale.y), std::abs(o.scale.z)}), 0.0f));
|
||||||
|
}
|
||||||
|
return std::max(best, 0.001f);
|
||||||
|
}
|
||||||
|
|
||||||
auto App::panBy(ImVec2 d) -> void {
|
auto App::panBy(ImVec2 d) -> void {
|
||||||
const glm::vec3 f = viewDir(), right = glm::normalize(glm::cross(f, std::abs(f.z) > 0.99f ? glm::vec3(std::cos(glm::radians(yaw_)), std::sin(glm::radians(yaw_)), 0) : glm::vec3(0, 0, 1)));
|
const glm::vec3 f = viewDir(), right = glm::normalize(glm::cross(f, std::abs(f.z) > 0.99f ? glm::vec3(std::cos(glm::radians(yaw_)), std::sin(glm::radians(yaw_)), 0) : glm::vec3(0, 0, 1)));
|
||||||
const glm::vec3 up = glm::cross(right, f);
|
const glm::vec3 up = glm::cross(right, f);
|
||||||
const float perPixel = viewHeight() / std::max(ImGui::GetIO().DisplaySize.y, 1.0f);
|
const float perPixel = viewHeight() / std::max(ImGui::GetIO().DisplaySize.y, 1.0f);
|
||||||
target_ += (-right * d.x + up * d.y) * perPixel;
|
target_ += (-right * d.x + up * d.y) * perPixel * fry_movespeed.f();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto App::updateCamera(ImGuiIO& io) -> void {
|
auto App::updateCamera(ImGuiIO& io) -> void {
|
||||||
const bool inView = mouseInView(io);
|
const bool inView = mouseInView(io);
|
||||||
// Orbit: right drag. Pan: middle drag (or Shift + right). Zoom: the wheel.
|
// Orbit: right drag. Pan: middle drag (or Shift + right). Zoom: the wheel.
|
||||||
const bool rightDown = ImGui::IsMouseDown(ImGuiMouseButton_Right), midDown = ImGui::IsMouseDown(ImGuiMouseButton_Middle);
|
const bool rightDown = ImGui::IsMouseDown(ImGuiMouseButton_Right), midDown = ImGui::IsMouseDown(ImGuiMouseButton_Middle);
|
||||||
if (inView && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) orbiting_ = !io.KeyShift;
|
// Right drag turns the camera where it stands (as in games, Unreal, Unity); Alt + right drag
|
||||||
if (inView && (ImGui::IsMouseClicked(ImGuiMouseButton_Middle) || (io.KeyShift && ImGui::IsMouseClicked(ImGuiMouseButton_Right)))) panning_ = true, orbiting_ = false;
|
// turns it round what's picked (or what it looks at). A finger on a phone: round, still.
|
||||||
if (!rightDown) orbiting_ = false;
|
if (inView && ImGui::IsMouseClicked(ImGuiMouseButton_Right) && !io.KeyShift) {
|
||||||
|
if (io.KeyAlt || touch()) {
|
||||||
|
orbiting_ = true;
|
||||||
|
if (!selected_.empty() && !touch()) { // (round the middle of what's picked)
|
||||||
|
glm::vec3 lo(1e30f), hi(-1e30f);
|
||||||
|
for (const Object& o : scene_.objects)
|
||||||
|
if (selected_.count(o.id)) lo = glm::min(lo, o.position), hi = glm::max(hi, o.position);
|
||||||
|
const glm::vec3 eye = target_ - viewDir() * dist_, pivot = (lo + hi) * 0.5f;
|
||||||
|
if (glm::distance(eye, pivot) > 1e-4f) {
|
||||||
|
const glm::vec3 f = glm::normalize(pivot - eye);
|
||||||
|
yaw_ = glm::degrees(std::atan2(f.y, f.x)), pitch_ = glm::degrees(std::asin(std::clamp(-f.z, -1.0f, 1.0f)));
|
||||||
|
target_ = pivot, dist_ = glm::distance(eye, pivot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else looking_ = true;
|
||||||
|
}
|
||||||
|
if (inView && (ImGui::IsMouseClicked(ImGuiMouseButton_Middle) || (io.KeyShift && ImGui::IsMouseClicked(ImGuiMouseButton_Right)))) panning_ = true, orbiting_ = looking_ = false;
|
||||||
|
if (!rightDown) {
|
||||||
|
if (looking_) refocus();
|
||||||
|
orbiting_ = looking_ = false;
|
||||||
|
}
|
||||||
if (!midDown && !rightDown) panning_ = false;
|
if (!midDown && !rightDown) panning_ = false;
|
||||||
const ImVec2 d = io.MouseDelta;
|
const ImVec2 d = io.MouseDelta;
|
||||||
if (orbiting_ && (d.x != 0 || d.y != 0)) orbitBy(d);
|
if (orbiting_ && (d.x != 0 || d.y != 0)) orbitBy(d);
|
||||||
|
if (looking_ && (d.x != 0 || d.y != 0)) lookBy(d);
|
||||||
if (panning_ && (d.x != 0 || d.y != 0)) panBy(d);
|
if (panning_ && (d.x != 0 || d.y != 0)) panBy(d);
|
||||||
if (inView && io.MouseWheel != 0 && drag_.active && softSel_ && mode_ == Mode::Edit) { // the soft selection's reach, not zoom
|
if (inView && io.MouseWheel != 0 && drag_.active && softSel_ && mode_ == Mode::Edit) { // the soft selection's reach, not zoom
|
||||||
softReach_ = std::clamp(softReach_ * std::pow(1.15f, io.MouseWheel), 0.001f, 1000.0f);
|
softReach_ = std::clamp(softReach_ * std::pow(1.15f, io.MouseWheel), 0.001f, 1000.0f);
|
||||||
findSoft();
|
findSoft();
|
||||||
message_ = std::format("Soft selection reaches {:.3f} m (wheel)", softReach_), messageAt_ = now();
|
message_ = std::format("Soft selection reaches {:.3f} m (wheel)", softReach_), messageAt_ = now();
|
||||||
|
} else if (inView && io.MouseWheel != 0 && io.KeyShift) { // Shift + wheel: how fast every way of moving goes
|
||||||
|
const float speed = std::clamp(fry_movespeed.f() * std::pow(1.25f, io.MouseWheel), 0.05f, 20.0f);
|
||||||
|
keel::execute(std::format("fry_movespeed {:.3g}", speed));
|
||||||
|
message_ = std::format("Moving speed: x{:.2g} (Shift + wheel)", speed), messageAt_ = now();
|
||||||
} else if (inView && io.MouseWheel != 0) {
|
} else if (inView && io.MouseWheel != 0) {
|
||||||
// Towards what's under the mouse: an object, else the ground, else the plane the target's
|
// The wheel moves the camera straight forward or back (where it looks), a fifth of the
|
||||||
// on. The whole camera closes in on that point (it stays under the mouse), so far from
|
// way to what's ahead each notch, but never less than a little of the scene's size: so it
|
||||||
// everything you still get somewhere instead of creeping at a spot in the air.
|
// doesn't creep to a stop in front of a surface (it goes through). In a flat view: closer.
|
||||||
const float k = std::pow(0.85f, io.MouseWheel);
|
if (view_ != View::Perspective) dist_ = std::clamp(dist_ * std::pow(0.85f, io.MouseWheel), 0.01f, 5000.0f);
|
||||||
const Ray ray = mouseRay(io.MousePos);
|
|
||||||
std::optional<glm::vec3> at;
|
|
||||||
if (const Hit h = pickObject(ray); h.object) at = ray.origin + ray.dir * h.t;
|
|
||||||
else if (auto g = hitPlane(ray, glm::vec3(0.0f), glm::vec3(0, 0, 1)); g && glm::distance(*g, eye_) < dist_ * 40.0f) at = g;
|
|
||||||
if (!at && k < 1.0f) target_ += ray.dir * dist_ * (1.0f - k); // (nothing there: the sky; on that way)
|
|
||||||
else {
|
else {
|
||||||
if (!at) at = hitPlane(ray, target_, -viewDir());
|
float size = 0.0f; // (the biggest object's size: the least step is a little of it)
|
||||||
const float to = std::clamp(dist_ * k, 0.01f, 5000.0f);
|
for (const Object& o : scene_.objects)
|
||||||
if (at) target_ = *at + (target_ - *at) * (to / dist_);
|
if (auto r = radii_.find(o.id); r != radii_.end()) size = std::max(size, r->second.second * std::max({std::abs(o.scale.x), std::abs(o.scale.y), std::abs(o.scale.z)}));
|
||||||
dist_ = to;
|
const float step = std::clamp(std::max(dist_ * 0.2f, size * 0.05f), 0.001f, 500.0f) * fry_movespeed.f();
|
||||||
|
target_ += viewDir() * step * io.MouseWheel;
|
||||||
|
refocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Arrow keys: forward, back and sideways along the ground (in a flat view: the way they
|
// Moving: the arrow keys any time, and WASD (Q / E down and up) while the right button turns
|
||||||
// point on the screen), Page Up / Down: up and down; Shift: faster. As fast as the view is big.
|
// the camera; forward is where it looks (in a flat view: up the screen). Page Up / Down: up
|
||||||
|
// and down. Shift: faster. As fast as what's near is far.
|
||||||
if (!io.WantTextInput && !search_.open && !ImGui::IsAnyItemActive() && !io.KeyCtrl && !io.KeyAlt) {
|
if (!io.WantTextInput && !search_.open && !ImGui::IsAnyItemActive() && !io.KeyCtrl && !io.KeyAlt) {
|
||||||
const float x = float(ImGui::IsKeyDown(ImGuiKey_RightArrow)) - float(ImGui::IsKeyDown(ImGuiKey_LeftArrow));
|
auto down = [](ImGuiKey k) { return float(ImGui::IsKeyDown(k)); };
|
||||||
const float y = float(ImGui::IsKeyDown(ImGuiKey_UpArrow)) - float(ImGui::IsKeyDown(ImGuiKey_DownArrow));
|
const float wasd = looking_ ? 1.0f : 0.0f;
|
||||||
const float z = float(ImGui::IsKeyDown(ImGuiKey_PageUp)) - float(ImGui::IsKeyDown(ImGuiKey_PageDown));
|
const float x = down(ImGuiKey_RightArrow) - down(ImGuiKey_LeftArrow) + wasd * (down(ImGuiKey_D) - down(ImGuiKey_A));
|
||||||
|
const float y = down(ImGuiKey_UpArrow) - down(ImGuiKey_DownArrow) + wasd * (down(ImGuiKey_W) - down(ImGuiKey_S));
|
||||||
|
const float z = down(ImGuiKey_PageUp) - down(ImGuiKey_PageDown) + wasd * (down(ImGuiKey_E) - down(ImGuiKey_Q));
|
||||||
if (x != 0 || y != 0 || z != 0) {
|
if (x != 0 || y != 0 || z != 0) {
|
||||||
const glm::vec3 f = viewDir(), right = glm::normalize(glm::cross(f, std::abs(f.z) > 0.99f ? glm::vec3(std::cos(glm::radians(yaw_)), std::sin(glm::radians(yaw_)), 0) : glm::vec3(0, 0, 1)));
|
const glm::vec3 f = viewDir(), right = glm::normalize(glm::cross(f, std::abs(f.z) > 0.99f ? glm::vec3(std::cos(glm::radians(yaw_)), std::sin(glm::radians(yaw_)), 0) : glm::vec3(0, 0, 1)));
|
||||||
const glm::vec3 ahead = view_ == View::Perspective ? glm::normalize(glm::cross(glm::vec3(0, 0, 1), right)) : glm::cross(right, f);
|
const glm::vec3 ahead = view_ == View::Perspective ? f : glm::cross(right, f);
|
||||||
const float dt = std::min(io.DeltaTime, 0.1f), speed = dist_ * (io.KeyShift ? 2.5f : 0.8f) * dt;
|
const float dt = std::min(io.DeltaTime, 0.1f), speed = std::max(viewScale() * 1.5f, 0.3f) * fry_movespeed.f() * (io.KeyShift ? 3.0f : 1.0f) * dt;
|
||||||
target_ += (right * x + ahead * y + (view_ == View::Perspective ? glm::vec3(0, 0, z) : glm::vec3(0.0f))) * speed;
|
target_ += (right * x + ahead * y + (view_ == View::Perspective ? glm::vec3(0, 0, z) : glm::vec3(0.0f))) * speed;
|
||||||
if (view_ != View::Perspective && z != 0) dist_ = std::clamp(dist_ * std::pow(io.KeyShift ? 0.1f : 0.4f, z * dt), 0.01f, 5000.0f); // (flat: closer or further)
|
if (view_ != View::Perspective && z != 0) dist_ = std::clamp(dist_ * std::pow(io.KeyShift ? 0.1f : 0.4f, z * dt), 0.01f, 5000.0f); // (flat: closer or further)
|
||||||
|
if (view_ == View::Perspective && !looking_) refocus(); // (while turning: when the button's let go)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
settleView();
|
settleView();
|
||||||
|
|
@ -234,6 +296,7 @@ auto App::settleView() -> void {
|
||||||
const float aspect = io.DisplaySize.x / std::max(io.DisplaySize.y, 1.0f);
|
const float aspect = io.DisplaySize.x / std::max(io.DisplaySize.y, 1.0f);
|
||||||
shift_ = glm::vec2((viewMin_.x + viewMax_.x) / std::max(io.DisplaySize.x, 1.0f) - 1.0f, (viewMin_.y + viewMax_.y) / std::max(io.DisplaySize.y, 1.0f) - 1.0f);
|
shift_ = glm::vec2((viewMin_.x + viewMax_.x) / std::max(io.DisplaySize.x, 1.0f) - 1.0f, (viewMin_.y + viewMax_.y) / std::max(io.DisplaySize.y, 1.0f) - 1.0f);
|
||||||
eye_ = target_ - viewDir() * dist_;
|
eye_ = target_ - viewDir() * dist_;
|
||||||
|
nearest_ = nearestThing();
|
||||||
nearFar_ = nearFar();
|
nearFar_ = nearFar();
|
||||||
vp_ = viewProjection(eye_, yaw_, pitch_, fov_, aspect, shift_, nearFar_);
|
vp_ = viewProjection(eye_, yaw_, pitch_, fov_, aspect, shift_, nearFar_);
|
||||||
}
|
}
|
||||||
|
|
@ -252,7 +315,7 @@ auto App::nearFar() -> glm::vec2 {
|
||||||
}
|
}
|
||||||
// (the near plane as far out as it can be: the depth's precision is spread between them, so
|
// (the near plane as far out as it can be: the depth's precision is spread between them, so
|
||||||
// the nearer it is, the more surfaces far off flicker through each other)
|
// the nearer it is, the more surfaces far off flicker through each other)
|
||||||
const float nearM = std::clamp(dist_ * 0.005f, 1e-5f, 50.0f);
|
const float nearM = std::clamp(viewScale() * 0.005f, 1e-5f, 50.0f);
|
||||||
const float farM = std::max({dist_ * 60.0f, (glm::length(eye_) + reach) * 2.0f, 100.0f});
|
const float farM = std::max({dist_ * 60.0f, (glm::length(eye_) + reach) * 2.0f, 100.0f});
|
||||||
return glm::vec2(nearM, farM) * kUnits;
|
return glm::vec2(nearM, farM) * kUnits;
|
||||||
}
|
}
|
||||||
|
|
@ -1137,11 +1200,17 @@ auto App::dragNow(const ImGuiIO& io) -> void {
|
||||||
|
|
||||||
// Solid: a plain grey room and soft studio light, no shadows or effects — for seeing shapes.
|
// Solid: a plain grey room and soft studio light, no shadows or effects — for seeing shapes.
|
||||||
// Rendered: sky, sun, shadows and effects.
|
// Rendered: sky, sun, shadows and effects.
|
||||||
|
// How far the shade in corners reaches (units): a share of how far away you look, so close up it
|
||||||
|
// stays in the corners instead of smearing over everything in a pattern. (24 at the first view.)
|
||||||
|
auto App::aoReach() const -> float {
|
||||||
|
return std::clamp(viewScale() * kUnits * 0.15f, 0.005f, 400.0f);
|
||||||
|
}
|
||||||
|
|
||||||
auto App::applyShading() -> void {
|
auto App::applyShading() -> void {
|
||||||
const Look& k = scene_.look;
|
const Look& k = scene_.look;
|
||||||
const std::string want =
|
const std::string want =
|
||||||
shading() == Shading::Rendered
|
shading() == Shading::Rendered
|
||||||
? std::format("r_ssao {}; r_bloom {}; r_vignette {}; r_exposure {}; r_saturation {}; r_sharpen {}; r_shadowsoft {}; r_gi {}; r_fog 0; r_skycolor \"{} {} {}\"", k.ao,
|
? std::format("r_ssao {}; r_ssao_radius {:.2g}; r_bloom {}; r_vignette {}; r_exposure {}; r_saturation {}; r_sharpen {}; r_shadowsoft {}; r_gi {}; r_fog 0; r_skycolor \"{} {} {}\"", k.ao, aoReach(),
|
||||||
k.bloom, k.vignette, k.exposure, k.saturation, k.sharpen, k.shadowSoft, kRayLight, k.background.r, k.background.g, k.background.b)
|
k.bloom, k.vignette, k.exposure, k.saturation, k.sharpen, k.shadowSoft, kRayLight, k.background.r, k.background.g, k.background.b)
|
||||||
: std::string("r_ssao 0; r_bloom 0; r_vignette 0; r_exposure 1; r_saturation 1; r_sharpen 0; r_gi 0; r_fog 0; r_skycolor \"0.2 0.2 0.215\"");
|
: std::string("r_ssao 0; r_bloom 0; r_vignette 0; r_exposure 1; r_saturation 1; r_sharpen 0; r_gi 0; r_fog 0; r_skycolor \"0.2 0.2 0.215\"");
|
||||||
if (want == appliedLook_) return;
|
if (want == appliedLook_) return;
|
||||||
|
|
@ -1305,53 +1374,77 @@ auto App::draw3D(keel::Renderer& r) -> void {
|
||||||
r.drawModel(id, toUnits, glm::vec4(1.0f, 1.0f, 1.0f, ref.opacity), nullptr, &flat, false);
|
r.drawModel(id, toUnits, glm::vec4(1.0f, 1.0f, 1.0f, ref.opacity), nullptr, &flat, false);
|
||||||
}
|
}
|
||||||
// The grid on the work plane (the ground, or what a flat view looks at), and the axes.
|
// The grid on the work plane (the ground, or what a flat view looks at), and the axes.
|
||||||
|
// Four sets of lines, each ten times further apart than the one before, each drawn round
|
||||||
|
// where you look and fading out before its edge. A line's look goes only by how far apart
|
||||||
|
// it and its like are on the screen: too close together, it fades away; far apart, it's
|
||||||
|
// brighter and a little wider. So nothing jumps as the view moves or the steps change: new
|
||||||
|
// lines come in too close together to see and fade in. Each line is drawn by the widest set
|
||||||
|
// it belongs to (once), in pieces, each a pixel wide where it is.
|
||||||
if (tab_ == Tab::Render ? renderGrid_ : showGrid_) {
|
if (tab_ == Tab::Render ? renderGrid_ : showGrid_) {
|
||||||
glm::vec3 p, n;
|
glm::vec3 p, n;
|
||||||
workPlane(p, n);
|
workPlane(p, n);
|
||||||
const glm::vec3 u = n.z > 0.5f ? glm::vec3(1, 0, 0) : n.x > 0.5f ? glm::vec3(0, 1, 0) : glm::vec3(1, 0, 0);
|
const glm::vec3 u = n.z > 0.5f ? glm::vec3(1, 0, 0) : n.x > 0.5f ? glm::vec3(0, 1, 0) : glm::vec3(1, 0, 0);
|
||||||
const glm::vec3 v = glm::cross(n, u);
|
const glm::vec3 v = glm::cross(n, u);
|
||||||
const float step = gridStep();
|
const float first = gridStep() * 0.1f;
|
||||||
const int count = 20;
|
const int count = 20, pieces = 12, levels = 4;
|
||||||
const glm::vec3 centre = target_ - n * glm::dot(target_, n);
|
const glm::vec3 centre = target_ - n * glm::dot(target_, n);
|
||||||
|
const float cu = glm::dot(centre, u), cv = glm::dot(centre, v);
|
||||||
const float shade = rendered ? 0.0f : 1.0f; // (dark lines on the sunny ground, light ones in the grey room)
|
const float shade = rendered ? 0.0f : 1.0f; // (dark lines on the sunny ground, light ones in the grey room)
|
||||||
// Two rings: the fine lines round where you look, then lines ten times apart out to
|
const float dim = rendered ? 0.14f : 0.05f, bright = rendered ? 0.4f : 0.16f;
|
||||||
// the horizon (they leave out the middle, so no line is drawn twice).
|
auto lift = [&](const glm::vec3& q) { return q + (eye_ - q) * 0.002f; }; // (towards the eye along its line of sight: the same place on the screen, but in front of the ground)
|
||||||
for (int level = 0; level < 2; ++level) {
|
// One line, at `at` across, from `from` to `to` along; s: how far apart it and its like
|
||||||
const float s = step * (level ? 10.0f : 1.0f), inner = count * step; // (the fine ring's reach)
|
// are; ring: how far its set reaches from the middle (0: no end, the axes).
|
||||||
const float cu = snapTo(glm::dot(centre, u), step * 10), cv = snapTo(glm::dot(centre, v), step * 10);
|
auto line = [&](int dir, float at, float from, float to, float s, float ring, glm::vec4 col, bool axis) {
|
||||||
const float fade = level ? 0.6f : 1.0f;
|
const glm::vec3 along = dir ? u : v, across = dir ? v : u;
|
||||||
for (int i = -count; i <= count; ++i)
|
const float mid = dir ? cu : cv, side = std::abs(at - (dir ? cv : cu));
|
||||||
for (int dir = 0; dir < 2; ++dir) {
|
// (short pieces near the eye, longer further off: each one's width and fading is
|
||||||
const glm::vec3 along = dir ? u : v, across = dir ? v : u;
|
// worked out at its middle, so it has to be about the same all along it)
|
||||||
const float at = (dir ? cv : cu) + float(i) * s, mid = dir ? cu : cv;
|
const float near = std::clamp(glm::dot(eye_, along), from, to);
|
||||||
const bool major = std::abs(snapTo(at, s * 10) - at) < s * 0.01f;
|
for (int k = 0; k < pieces * 2; ++k) {
|
||||||
const bool axis = std::abs(at) < s * 0.01f;
|
const int sideOf = k < pieces ? -1 : 1, j = k % pieces;
|
||||||
glm::vec4 col = glm::vec4(glm::vec3(shade), (major ? (rendered ? 0.35f : 0.13f) : (rendered ? 0.16f : 0.06f)) * fade);
|
const float len = sideOf < 0 ? near - from : to - near;
|
||||||
if (axis) col = dir ? (n.x > 0.5f ? glm::vec4(0.3f, 0.75f, 0.25f, 0.9f) : glm::vec4(0.9f, 0.25f, 0.25f, 0.9f))
|
if (len <= 0.0f) continue;
|
||||||
: (n.z > 0.5f ? glm::vec4(0.3f, 0.75f, 0.25f, 0.9f) : glm::vec4(0.25f, 0.45f, 0.95f, 0.9f));
|
const float f0 = float(j) / float(pieces), f1 = float(j + 1) / float(pieces);
|
||||||
const float from = mid - count * s, to = mid + count * s;
|
const float t0 = near + float(sideOf) * len * f0 * f0 * f0, t1 = near + float(sideOf) * len * f1 * f1 * f1;
|
||||||
// (a far line through the fine ring, which has it already: only its two ends)
|
const glm::vec3 a = across * at + along * t0, b = across * at + along * t1, c = (a + b) * 0.5f;
|
||||||
const bool split = level == 1 && std::abs(at - (dir ? cv : cu)) <= inner * 1.0001f;
|
// (pixels to the next line like it, across this one as it lies on the screen)
|
||||||
const int pieces = level ? 24 : 8;
|
const glm::vec4 c0 = vp_ * glm::vec4(c, 1.0f), c1 = vp_ * glm::vec4(c + across * s, 1.0f), c2 = vp_ * glm::vec4(c + along * s, 1.0f);
|
||||||
for (int k = 0; k < pieces; ++k) {
|
if (c0.w <= 1e-6f || c1.w <= 1e-6f || c2.w <= 1e-6f) continue; // (behind the eye)
|
||||||
const float t0 = from + (to - from) * float(k) / float(pieces), t1 = from + (to - from) * float(k + 1) / float(pieces);
|
const glm::vec2 half = glm::vec2(ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y) * 0.5f;
|
||||||
if (split && t1 > mid - inner && t0 < mid + inner) continue;
|
const glm::vec2 s0 = glm::vec2(c0) / c0.w * half, s1 = glm::vec2(c1) / c1.w * half, s2 = glm::vec2(c2) / c2.w * half;
|
||||||
const glm::vec3 a = across * at + along * t0, b = across * at + along * t1, c = (a + b) * 0.5f;
|
const glm::vec2 way = s2 - s0;
|
||||||
// Each piece about a pixel wide where it is, and fading as the lines get
|
const float wayLen = glm::length(way), d = s1.x - s0.x, e = s1.y - s0.y;
|
||||||
// too close together on the screen to tell apart (far off, or seen edge
|
const float apart = wayLen > 1e-6f ? std::abs(d * way.y - e * way.x) / wayLen : glm::length(s1 - s0);
|
||||||
// on): no crawling, flickering band towards the horizon.
|
const float px = pixelSize(c);
|
||||||
const glm::vec3 sight = glm::normalize(c - eye_);
|
float alpha = axis ? std::max(glm::smoothstep(1.5f, 6.0f, apart), 0.35f) : glm::smoothstep(5.0f, 20.0f, apart);
|
||||||
const float px = pixelSize(c), seen = glm::length(across - sight * glm::dot(across, sight));
|
if (ring > 0.0f) alpha *= glm::smoothstep(ring, ring * 0.6f, std::max(side, std::abs((t0 + t1) * 0.5f - mid)));
|
||||||
const float apart = s * (major ? 10.0f : 1.0f) * seen / std::max(px, 1e-9f); // (pixels to the next line like it: a major line's are ten apart)
|
const float strong = axis ? 1.0f : glm::smoothstep(20.0f, 160.0f, apart);
|
||||||
glm::vec4 pc = col;
|
glm::vec4 pc = axis ? col : glm::vec4(glm::vec3(shade), glm::mix(dim, bright, strong));
|
||||||
pc.a *= axis ? std::max(glm::smoothstep(1.5f, 6.0f, apart), 0.35f) : glm::smoothstep(3.0f, 12.0f, apart);
|
pc.a *= alpha;
|
||||||
if (pc.a < 0.004f) continue;
|
if (pc.a < 0.004f) continue;
|
||||||
// (each end a hair towards the eye along its own line of sight: the same
|
r.drawStreak(lift(a) * kUnits, lift(b) * kUnits, px * (1.0f + 0.6f * strong) * kUnits, pc, keel::kWhiteTexture);
|
||||||
// place on the screen, but in front of the ground it lies on, so they don't fight)
|
}
|
||||||
auto lift = [&](const glm::vec3& q) { return q + (eye_ - q) * 0.002f; };
|
};
|
||||||
r.drawStreak(lift(a) * kUnits, lift(b) * kUnits, px * (major || axis ? 1.6f : 1.0f) * kUnits, pc, keel::kWhiteTexture);
|
for (int level = 0; level < levels; ++level) {
|
||||||
}
|
const float s = first * std::pow(10.0f, float(level)), ring = count * s;
|
||||||
|
const bool top = level == levels - 1;
|
||||||
|
for (int dir = 0; dir < 2; ++dir) {
|
||||||
|
const float c0 = snapTo(dir ? cv : cu, s), mid = dir ? cu : cv;
|
||||||
|
for (int i = -count; i <= count; ++i) {
|
||||||
|
const float at = c0 + float(i) * s;
|
||||||
|
if (std::abs(at) < s * 0.01f) continue; // (the axis: drawn on its own)
|
||||||
|
if (!top && std::abs(snapTo(at, s * 10.0f) - at) < s * 0.01f) continue; // (the next set's)
|
||||||
|
line(dir, at, mid - ring, mid + ring, s, ring, {}, false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The axes: right through, as far as the widest set reaches.
|
||||||
|
const float reach = count * first * std::pow(10.0f, float(levels - 1));
|
||||||
|
for (int dir = 0; dir < 2; ++dir) {
|
||||||
|
const glm::vec4 col = dir ? (n.x > 0.5f ? glm::vec4(0.3f, 0.75f, 0.25f, 0.9f) : glm::vec4(0.9f, 0.25f, 0.25f, 0.9f))
|
||||||
|
: (n.z > 0.5f ? glm::vec4(0.3f, 0.75f, 0.25f, 0.9f) : glm::vec4(0.25f, 0.45f, 0.95f, 0.9f));
|
||||||
|
const float mid = dir ? cu : cv;
|
||||||
|
line(dir, 0.0f, mid - reach, mid + reach, first, 0.0f, col, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tab_ == Tab::Print) drawPrintBed(r, light);
|
if (tab_ == Tab::Print) drawPrintBed(r, light);
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,8 @@ int CommandSearch::draw(const std::vector<Command>& all, const std::vector<std::
|
||||||
open = false;
|
open = false;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
// F3 or Ctrl+Space again: closed (not the press that opened it)
|
||||||
|
if (!focus && (ImGui::IsKeyPressed(ImGuiKey_F3, false) || (ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_Space, false)))) open = false;
|
||||||
if (focus) ImGui::SetKeyboardFocusHere(), focus = false;
|
if (focus) ImGui::SetKeyboardFocusHere(), focus = false;
|
||||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||||
ImGui::InputTextWithHint("##find", "Type what you want to do: round edges, hole, export STL...", text, sizeof text);
|
ImGui::InputTextWithHint("##find", "Type what you want to do: round edges, hole, export STL...", text, sizeof text);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue