Camera: fly or orbit (View > Camera, Shift+C, kept): orbiting turns round what you look at on a right drag and the wheel zooms; the hints follow it; the site's 0.8 (getting round, smoother); fry_where for trying it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Nexoniarz 2026-10-21 10:02:04 +02:00
commit d313a9452a
7 changed files with 31 additions and 7 deletions

View file

@ -22,6 +22,7 @@ keel::Cvar fry_touch("fry_touch", "0", keel::CVAR_ARCHIVE, "Fry3DX: the phone la
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_movespeed("fry_movespeed", "1", keel::CVAR_ARCHIVE, "Fry3DX: how fast the camera moves (keys, wheel, pan): Shift + wheel changes it");
keel::Cvar fry_camera("fry_camera", "0", keel::CVAR_ARCHIVE, "Fry3DX: the camera: 0 flies (right drag turns it where it stands, WASD), 1 orbits (right drag round what you look at, the wheel zooms)");
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_autosave("fry_autosave", "2", keel::CVAR_ARCHIVE, "Fry3DX: autosave every this many minutes while there are changes (0: off)");
@ -283,6 +284,10 @@ auto App::init() -> void {
}
self->say("fry_hold: no key called " + a[1]);
}, "Fry3DX: fry_hold key frames: hold a key down (UpArrow, PageUp...)");
static keel::Cmd where("fry_where", [](const keel::Args&) {
self->say(std::format("camera: yaw {:.1f} pitch {:.1f} distance {:.3f} target {:.2f} {:.2f} {:.2f} eye {:.2f} {:.2f} {:.2f}", self->yaw_, self->pitch_, self->dist_,
self->target_.x, self->target_.y, self->target_.z, self->eye_.x, self->eye_.y, self->eye_.z));
}, "Fry3DX: where the camera is");
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]));
@ -1758,6 +1763,7 @@ auto App::shortcuts(ImGuiIO& io) -> void {
if (key(ImGuiKey_X)) setTool(Tool::Extrude);
if (key(ImGuiKey_I)) setTool(Tool::Inset);
if (key(ImGuiKey_B)) setTool(Tool::Bevel);
if (key(ImGuiKey_C) && shift) return runCommand("view.camera");
if (key(ImGuiKey_C)) setTool(Tool::LoopCut);
if (key(ImGuiKey_K)) setTool(Tool::Knife);
if (key(ImGuiKey_O) && !shift) setTool(Tool::Hole);

View file

@ -276,6 +276,7 @@ extern keel::Cvar fry_uiscale;
extern keel::Cvar fry_uisize;
extern keel::Cvar fry_theme;
extern keel::Cvar fry_movespeed;
extern keel::Cvar fry_camera;
extern keel::Cvar fry_pluginsoff;
extern keel::Cvar fry_autosave;

View file

@ -506,6 +506,11 @@ auto App::buildCommands() -> void {
add("view.only", "Show only the selected (again: all)", "View", "/", [this] { showOnly(selected_); }, picked, "isolate local view hide");
add("view.unhide", "Show everything", "View", "Alt+H", [this] { for (Object& o : scene_.objects) o.visible = true; }, {}, "unhide");
add("view.layout", "Put the panels back as they were", "View", "", [this] { resetLayout(); }, {}, "reset layout workspace default panels");
add("view.camera", "Camera: fly or orbit (switch)", "View", "Shift+C", [this] {
keel::execute(std::format("fry_camera {}", fry_camera.i() == 1 ? 0 : 1));
say(fry_camera.i() == 1 ? "Orbit camera: right drag turns round what you look at, the wheel zooms (Shift+C: back to flying)"
: "Fly camera: right drag turns it where it stands, WASD moves, the wheel goes forward and back (Shift+C: orbit)");
}, {}, "orbit fly navigation blender game turntable");
add("view.workspace", "Workspaces (save the panels as they are)...", "View", "", [this] { showWorkspaces_ = true; }, {}, "layout save panels");
for (int i = 0; i < 3; ++i)
add(std::string("view.look") + char('0' + i), std::string("Appearance: ") + (i == 0 ? "like the desktop" : i == 1 ? "dark" : "light"), "View", "",
@ -744,6 +749,9 @@ auto App::menus() -> void {
if (ImGui::MenuItem("Frame everything", "Home")) frameAll();
if (ImGui::MenuItem("Show only the selected (again: all)", "/", false, !selected_.empty())) showOnly(selected_);
ImGui::Separator();
if (ImGui::MenuItem("Camera: fly (as in games)", nullptr, fry_camera.i() == 0)) keel::execute("fry_camera 0");
if (ImGui::MenuItem("Camera: orbit (as in Blender)", "Shift+C", fry_camera.i() == 1)) keel::execute("fry_camera 1");
ImGui::Separator();
commandItem("tools.measure");
commandItem("view.workspace");
ImGui::EndMenu();
@ -1997,7 +2005,10 @@ auto App::statusBar() -> void {
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
for (const ToolInfo& t : kTools)
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);
if (t.tool == tool_)
ImGui::TextDisabled("%s: %s · %s F1: all keys", t.name, t.hint,
fry_camera.i() == 1 ? "Right drag: orbit Middle drag: pan Wheel: zoom Shift+C: fly"
: "Right drag + WASD: look and move Alt+right: orbit Wheel: forward / back Shift+C: orbit");
ImGui::End();
}

View file

@ -449,7 +449,8 @@ auto App::helpWindow() -> void {
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 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"},
{"Alt + right drag", "Orbit round what's picked (or what you look at)"},
{"Shift+C", "Switch the camera: fly / orbit (right drag round what you look at, the wheel zooms)"}, {"Middle drag / Shift+Right drag", "Pan"},
{"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"},
{"Z / Shift+Z", "Solid or Rendered / Wireframe"}, {"Alt+Z", "X-ray: see and pick through"}});

View file

@ -220,10 +220,11 @@ auto App::updateCamera(ImGuiIO& io) -> void {
const bool rightDown = ImGui::IsMouseDown(ImGuiMouseButton_Right), midDown = ImGui::IsMouseDown(ImGuiMouseButton_Middle);
// Right drag turns the camera where it stands (as in games, Unreal, Unity); Alt + right drag
// turns it round what's picked (or what it looks at). A finger on a phone: round, still.
const bool orbitCamera = fry_camera.i() == 1; // (View > Camera: round what you look at, as in Blender)
if (inView && ImGui::IsMouseClicked(ImGuiMouseButton_Right) && !io.KeyShift) {
if (io.KeyAlt || touch()) {
if (io.KeyAlt || touch() || orbitCamera) {
orbiting_ = true;
if (!selected_.empty() && !touch()) { // (round the middle of what's picked)
if (!selected_.empty() && !touch() && !orbitCamera) { // (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);
@ -258,7 +259,8 @@ auto App::updateCamera(ImGuiIO& io) -> void {
// The wheel moves the camera straight forward or back (where it looks), a fifth of the
// way to what's ahead each notch, but never less than a little of the scene's size: so it
// doesn't creep to a stop in front of a surface (it goes through). In a flat view: closer.
if (view_ != View::Perspective) dist_ = std::clamp(dist_ * std::pow(0.85f, io.MouseWheel), 0.01f, 5000.0f);
// The orbiting camera: closer to (or further from) what it turns round.
if (view_ != View::Perspective || orbitCamera) dist_ = std::clamp(dist_ * std::pow(0.85f, io.MouseWheel), 0.01f, 5000.0f);
else {
float size = 0.0f; // (the biggest object's size: the least step is a little of it)
for (const Object& o : scene_.objects)