mirror of
https://github.com/Nexoniarz/Fry3DX.git
synced 2026-11-03 04:24:26 +00:00
The view: near and far go by the distance (no more vanishing far out or cutting close up), a coarser grid out to the horizon; outline and lines over the model agree while a touch turns the view (worked out once, just before drawing); outlines from a cached edge list, points tried for sight only when drawn
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
c2bbafd40c
commit
f1f7a3fecc
3 changed files with 120 additions and 77 deletions
|
|
@ -100,7 +100,7 @@ glm::mat4 viewProjection(const Camera& c, float aspect) {
|
||||||
const glm::vec3 forward = forwardOf(c);
|
const glm::vec3 forward = forwardOf(c);
|
||||||
const glm::vec3 up = glm::vec3(glm::rotate(glm::mat4(1), glm::radians(c.roll), forward) * glm::vec4(0, 0, 1, 0));
|
const glm::vec3 up = glm::vec3(glm::rotate(glm::mat4(1), glm::radians(c.roll), forward) * glm::vec4(0, 0, 1, 0));
|
||||||
glm::mat4 view = glm::lookAt(c.origin, c.origin + forward, up);
|
glm::mat4 view = glm::lookAt(c.origin, c.origin + forward, up);
|
||||||
glm::mat4 proj = glm::perspective(glm::radians(c.fovY), aspect, 2.0f, 16384.0f);
|
glm::mat4 proj = glm::perspective(glm::radians(c.fovY), aspect, c.zNear, c.zFar);
|
||||||
proj[1][1] *= -1.0f; // clip-space Y points down (Vulkan; OpenGL is set up to match)
|
proj[1][1] *= -1.0f; // clip-space Y points down (Vulkan; OpenGL is set up to match)
|
||||||
proj[2][0] -= c.shift.x, proj[2][1] -= c.shift.y;
|
proj[2][0] -= c.shift.x, proj[2][1] -= c.shift.y;
|
||||||
return proj * view;
|
return proj * view;
|
||||||
|
|
@ -789,7 +789,7 @@ void Renderer::buildPacket(uint32_t width, uint32_t height) {
|
||||||
p.post.sunColor = glm::vec4(sunColor_, 0.0f);
|
p.post.sunColor = glm::vec4(sunColor_, 0.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.post.proj = glm::vec4(tanY * float(width) / float(height), tanY, 2.0f, 16384.0f);
|
p.post.proj = glm::vec4(tanY * float(width) / float(height), tanY, camera_.zNear, camera_.zFar);
|
||||||
p.clearColor = glm::vec3(srgbToLinear(sky.r), srgbToLinear(sky.g), srgbToLinear(sky.b)) / p.exposure;
|
p.clearColor = glm::vec3(srgbToLinear(sky.r), srgbToLinear(sky.g), srgbToLinear(sky.b)) / p.exposure;
|
||||||
// The mirror's pass: this frame's data, but seen from the reflected eye, cut at the mirror,
|
// The mirror's pass: this frame's data, but seen from the reflected eye, cut at the mirror,
|
||||||
// lit by every light (the clusters are this view's), and no fog grid (that's this view's too).
|
// lit by every light (the clusters are this view's), and no fog grid (that's this view's too).
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ struct Camera {
|
||||||
float roll = 0.0f; // degrees, positive tilts right
|
float roll = 0.0f; // degrees, positive tilts right
|
||||||
float fovY = 74.0f; // degrees
|
float fovY = 74.0f; // degrees
|
||||||
glm::vec2 shift{0.0f}; // where the picture's middle sits on the screen (-1..1 across, down): a view beside panels
|
glm::vec2 shift{0.0f}; // where the picture's middle sits on the screen (-1..1 across, down): a view beside panels
|
||||||
|
float zNear = 2.0f, zFar = 16384.0f; // what's nearer or further isn't drawn (units)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Baked light for a moving model (from the map's probes): ambient cube + sun visibility.
|
// Baked light for a moving model (from the map's probes): ambient cube + sun visibility.
|
||||||
|
|
|
||||||
194
src/app.cpp
194
src/app.cpp
|
|
@ -176,10 +176,10 @@ glm::vec3 forwardOf(float yaw, float pitch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// The engine's view-projection (render/renderer.cpp), in metres.
|
// The engine's view-projection (render/renderer.cpp), in metres.
|
||||||
glm::mat4 viewProjection(const glm::vec3& eye, float yaw, float pitch, float fov, float aspect, glm::vec2 shift) {
|
glm::mat4 viewProjection(const glm::vec3& eye, float yaw, float pitch, float fov, float aspect, glm::vec2 shift, glm::vec2 nearFar) {
|
||||||
const glm::vec3 f = forwardOf(yaw, pitch);
|
const glm::vec3 f = forwardOf(yaw, pitch);
|
||||||
glm::mat4 view = glm::lookAt(eye * kUnits, eye * kUnits + f, glm::vec3(0, 0, 1));
|
glm::mat4 view = glm::lookAt(eye * kUnits, eye * kUnits + f, glm::vec3(0, 0, 1));
|
||||||
glm::mat4 proj = glm::perspective(glm::radians(fov), aspect, 2.0f, 16384.0f);
|
glm::mat4 proj = glm::perspective(glm::radians(fov), aspect, nearFar.x, nearFar.y);
|
||||||
proj[1][1] *= -1.0f;
|
proj[1][1] *= -1.0f;
|
||||||
proj[2][0] -= shift.x, proj[2][1] -= shift.y;
|
proj[2][0] -= shift.x, proj[2][1] -= shift.y;
|
||||||
return proj * view * glm::scale(glm::mat4(1.0f), glm::vec3(kUnits));
|
return proj * view * glm::scale(glm::mat4(1.0f), glm::vec3(kUnits));
|
||||||
|
|
@ -190,21 +190,6 @@ struct Ray {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Möller–Trumbore; t along the ray, or nothing.
|
// Möller–Trumbore; t along the ray, or nothing.
|
||||||
std::optional<float> hitTriangle(const Ray& r, const glm::vec3& a, const glm::vec3& b, const glm::vec3& c) {
|
|
||||||
const glm::vec3 e1 = b - a, e2 = c - a, p = glm::cross(r.dir, e2);
|
|
||||||
const float det = glm::dot(e1, p);
|
|
||||||
if (std::abs(det) < 1e-12f) return std::nullopt;
|
|
||||||
const float inv = 1.0f / det;
|
|
||||||
const glm::vec3 s = r.origin - a;
|
|
||||||
const float u = glm::dot(s, p) * inv;
|
|
||||||
if (u < 0 || u > 1) return std::nullopt;
|
|
||||||
const glm::vec3 q = glm::cross(s, e1);
|
|
||||||
const float v = glm::dot(r.dir, q) * inv;
|
|
||||||
if (v < 0 || u + v > 1) return std::nullopt;
|
|
||||||
const float t = glm::dot(e2, q) * inv;
|
|
||||||
return t > 1e-6f ? std::optional<float>(t) : std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<glm::vec3> hitPlane(const Ray& r, const glm::vec3& point, const glm::vec3& normal) {
|
std::optional<glm::vec3> hitPlane(const Ray& r, const glm::vec3& point, const glm::vec3& normal) {
|
||||||
const float d = glm::dot(r.dir, normal);
|
const float d = glm::dot(r.dir, normal);
|
||||||
if (std::abs(d) < 1e-8f) return std::nullopt;
|
if (std::abs(d) < 1e-8f) return std::nullopt;
|
||||||
|
|
@ -776,6 +761,8 @@ private:
|
||||||
View view_ = View::Perspective;
|
View view_ = View::Perspective;
|
||||||
glm::vec3 target_{0, 0, 0.4f};
|
glm::vec3 target_{0, 0, 0.4f};
|
||||||
float yaw_ = -135.0f, pitch_ = 25.0f, dist_ = 4.0f, fov_ = 50.0f;
|
float yaw_ = -135.0f, pitch_ = 25.0f, dist_ = 4.0f, fov_ = 50.0f;
|
||||||
|
glm::vec2 nearFar_{2.0f, 16384.0f};
|
||||||
|
std::map<uint32_t, std::pair<uint64_t, float>> radii_; // (each object's mesh: how far it reaches from its place)
|
||||||
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};
|
||||||
|
|
@ -4029,10 +4016,33 @@ private:
|
||||||
if (auto p = hitPlane(ray, target_, -viewDir())) target_ += (*p - target_) * (1.0f - k);
|
if (auto p = hitPlane(ray, target_, -viewDir())) target_ += (*p - target_) * (1.0f - k);
|
||||||
dist_ = std::clamp(dist_ * k, 0.01f, 5000.0f);
|
dist_ = std::clamp(dist_ * k, 0.01f, 5000.0f);
|
||||||
}
|
}
|
||||||
|
settleView();
|
||||||
|
}
|
||||||
|
// Where the eye is and how the view maps to the screen, from the camera as it is now: again
|
||||||
|
// just before drawing, so the model and the lines over it agree (a touch can turn it between).
|
||||||
|
void settleView() {
|
||||||
|
const ImGuiIO& io = ImGui::GetIO();
|
||||||
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_;
|
||||||
vp_ = viewProjection(eye_, yaw_, pitch_, fov_, aspect, shift_);
|
nearFar_ = nearFar();
|
||||||
|
vp_ = viewProjection(eye_, yaw_, pitch_, fov_, aspect, shift_, nearFar_);
|
||||||
|
}
|
||||||
|
// How near and far the view draws (units), going by how far away it looks: close up to a
|
||||||
|
// 5 mm part, or out to see a whole town, the same share of the depth's precision.
|
||||||
|
glm::vec2 nearFar() {
|
||||||
|
float reach = 1.0f; // (how far from the middle the furthest object reaches, m)
|
||||||
|
for (const Object& o : scene_.objects) {
|
||||||
|
auto& [version, radius] = radii_[o.id];
|
||||||
|
if (version != versions_[o.id] || !version) {
|
||||||
|
version = versions_[o.id], radius = 0;
|
||||||
|
for (const glm::vec3& v : o.mesh.verts) radius = std::max(radius, glm::length(v));
|
||||||
|
}
|
||||||
|
reach = std::max(reach, glm::length(o.position) + radius * std::max({std::abs(o.scale.x), std::abs(o.scale.y), std::abs(o.scale.z)}));
|
||||||
|
}
|
||||||
|
const float nearM = std::clamp(dist_ * 0.002f, 1e-5f, 50.0f);
|
||||||
|
const float farM = std::max({dist_ * 60.0f, (glm::length(eye_) + reach) * 2.0f, 100.0f});
|
||||||
|
return glm::vec2(nearM, farM) * kUnits;
|
||||||
}
|
}
|
||||||
|
|
||||||
// World point → screen (false behind the eye).
|
// World point → screen (false behind the eye).
|
||||||
|
|
@ -4114,43 +4124,70 @@ private:
|
||||||
const Hit h = pickObject({eye_, d / len}, o.id);
|
const Hit h = pickObject({eye_, d / len}, o.id);
|
||||||
return h.t > len - std::max(len * 0.002f, 0.0005f);
|
return h.t > len - std::max(len * 0.002f, 0.0005f);
|
||||||
}
|
}
|
||||||
// Which of an object's points the eye can see, worked out again only when it or the view
|
// Whether the eye can see an object's point (nothing of it in between): each tried when it's
|
||||||
// changes. The triangles are sorted into a grid on the screen first: a point is tried only
|
// first asked about, kept till the object or the view changes. (Only what's drawn is asked:
|
||||||
// against those over its own square (what could be between it and the eye).
|
// the outline's points, not every point of a big mesh, as the view turns.)
|
||||||
struct Visibility {
|
struct Visibility {
|
||||||
uint64_t version = 0;
|
uint64_t version = 0;
|
||||||
glm::vec3 eye{1e30f};
|
glm::vec3 eye{1e30f};
|
||||||
glm::mat4 vp{0.0f};
|
glm::mat4 vp{0.0f};
|
||||||
std::vector<char> point;
|
std::vector<char> point; // 0 not tried yet, 1 seen, 2 hidden
|
||||||
};
|
};
|
||||||
std::map<std::pair<uint32_t, bool>, Visibility> visibility_; // (an object's own mesh, or the one shown)
|
std::map<std::pair<uint32_t, bool>, Visibility> visibility_; // (an object's own mesh, or the one shown)
|
||||||
const std::vector<char>& visiblePoints(const Object& o) { return visiblePoints(o, o.mesh); }
|
bool seen(const Object& o, const Mesh& mesh, int i) {
|
||||||
const std::vector<char>& visiblePoints(const Object& o, const Mesh& mesh) {
|
|
||||||
Visibility& v = visibility_[{o.id, &mesh != &o.mesh}];
|
Visibility& v = visibility_[{o.id, &mesh != &o.mesh}];
|
||||||
const uint64_t version = versions_[o.id];
|
const uint64_t version = versions_[o.id];
|
||||||
if (v.version == version && glm::distance(v.eye, eye_) < 1e-6f && v.vp == vp_ && v.point.size() == mesh.verts.size()) return v.point;
|
if (v.version != version || glm::distance(v.eye, eye_) >= 1e-6f || v.vp != vp_ || v.point.size() != mesh.verts.size())
|
||||||
v.version = version, v.eye = eye_, v.vp = vp_;
|
v.version = version, v.eye = eye_, v.vp = vp_, v.point.assign(mesh.verts.size(), 0);
|
||||||
v.point.assign(mesh.verts.size(), 1);
|
char& got = v.point[size_t(i)];
|
||||||
// (each point: is anything of it between the point and the eye)
|
if (got) return got == 1;
|
||||||
const fry::TriangleTree& tree = treeOf(o, mesh);
|
const fry::TriangleTree& tree = treeOf(o, mesh);
|
||||||
const std::vector<glm::vec3>& w = tree.points();
|
const glm::vec3 w = tree.points()[size_t(i)];
|
||||||
for (size_t i = 0; i < w.size(); ++i) {
|
const glm::vec4 c = vp_ * glm::vec4(w, 1.0f);
|
||||||
const glm::vec4 c = vp_ * glm::vec4(w[i], 1.0f);
|
const glm::vec3 d = w - eye_;
|
||||||
if (c.w < 1e-5f || std::abs(c.x) > c.w || std::abs(c.y) > c.w) continue; // (off the screen: not drawn anyway)
|
const float len = glm::length(d);
|
||||||
const glm::vec3 d = w[i] - eye_;
|
if (c.w < 1e-5f || std::abs(c.x) > c.w || std::abs(c.y) > c.w || len < 1e-6f) return (got = 1); // (off the screen: not drawn anyway)
|
||||||
const float len = glm::length(d);
|
got = tree.blocked(eye_, d / len, len - std::max(len * 0.002f, 0.0005f), i) ? 2 : 1;
|
||||||
if (len < 1e-6f) continue;
|
return got == 1;
|
||||||
if (tree.blocked(eye_, d / len, len - std::max(len * 0.002f, 0.0005f), int(i))) v.point[i] = 0;
|
|
||||||
}
|
|
||||||
return v.point;
|
|
||||||
}
|
}
|
||||||
bool pointSeen(const Object& o, int v) { return xray_ || visiblePoints(o)[size_t(v)]; }
|
// Each edge of a mesh and the (first two) faces it's on, with the faces' normals: worked out
|
||||||
|
// again only when the mesh changes.
|
||||||
|
struct Adjacency {
|
||||||
|
uint64_t version = 0;
|
||||||
|
size_t verts = 0, faces = 0;
|
||||||
|
std::vector<std::array<int, 4>> edges; // a, b, a face, the other (or -1)
|
||||||
|
std::vector<glm::vec4> plane; // each face's normal, and its distance along it
|
||||||
|
};
|
||||||
|
std::map<std::pair<uint32_t, bool>, Adjacency> adjacency_;
|
||||||
|
const Adjacency& adjacencyOf(const Object& o, const Mesh& mesh) {
|
||||||
|
Adjacency& a = adjacency_[{o.id, &mesh != &o.mesh}];
|
||||||
|
const uint64_t version = versions_[o.id];
|
||||||
|
if (a.version == version && version && a.verts == mesh.verts.size() && a.faces == mesh.faces.size()) return a;
|
||||||
|
a.version = version, a.verts = mesh.verts.size(), a.faces = mesh.faces.size();
|
||||||
|
a.edges.clear(), a.plane.resize(mesh.faces.size());
|
||||||
|
std::unordered_map<uint64_t, int> at;
|
||||||
|
at.reserve(mesh.faces.size() * 2);
|
||||||
|
for (size_t f = 0; f < mesh.faces.size(); ++f) {
|
||||||
|
const auto& fv = mesh.faces[f];
|
||||||
|
const glm::vec3 n = faceNormal(mesh, int(f));
|
||||||
|
a.plane[f] = glm::vec4(n, fv.empty() ? 0.0f : glm::dot(n, mesh.verts[size_t(fv[0])]));
|
||||||
|
for (size_t i = 0; i < fv.size(); ++i) {
|
||||||
|
const int p = std::min(fv[i], fv[(i + 1) % fv.size()]), q = std::max(fv[i], fv[(i + 1) % fv.size()]);
|
||||||
|
auto [it, fresh] = at.try_emplace(uint64_t(uint32_t(p)) << 32 | uint32_t(q), int(a.edges.size()));
|
||||||
|
if (fresh) a.edges.push_back({p, q, int(f), -1});
|
||||||
|
else if (a.edges[size_t(it->second)][3] < 0) a.edges[size_t(it->second)][3] = int(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
bool pointSeen(const Object& o, int v) { return xray_ || seen(o, o.mesh, v); }
|
||||||
|
|
||||||
// The edge of the edited object nearest the mouse on screen (that can be seen), or (-1, -1).
|
// The edge of the edited object nearest the mouse on screen (that can be seen), or (-1, -1).
|
||||||
std::pair<int, int> nearestEdge(Object& o, ImVec2 mouse, float within) {
|
std::pair<int, int> nearestEdge(Object& o, ImVec2 mouse, float within) {
|
||||||
const glm::mat4 m = objectMatrix(o);
|
const glm::mat4 m = objectMatrix(o);
|
||||||
std::pair<int, int> best{-1, -1};
|
std::pair<int, int> best{-1, -1};
|
||||||
for (auto [a, b] : edges(o.mesh)) {
|
for (const auto& e : adjacencyOf(o, o.mesh).edges) {
|
||||||
|
const int a = e[0], b = e[1];
|
||||||
ImVec2 sa, sb;
|
ImVec2 sa, sb;
|
||||||
if (!project(glm::vec3(m * glm::vec4(o.mesh.verts[size_t(a)], 1.0f)), sa) || !project(glm::vec3(m * glm::vec4(o.mesh.verts[size_t(b)], 1.0f)), sb)) continue;
|
if (!project(glm::vec3(m * glm::vec4(o.mesh.verts[size_t(a)], 1.0f)), sa) || !project(glm::vec3(m * glm::vec4(o.mesh.verts[size_t(b)], 1.0f)), sb)) continue;
|
||||||
const float d = distToSegment(mouse, sa, sb);
|
const float d = distToSegment(mouse, sa, sb);
|
||||||
|
|
@ -5192,8 +5229,9 @@ private:
|
||||||
float gridStep() const { return std::pow(10.0f, std::floor(std::log10(std::max(viewHeight() / 4.0f, 1e-4f)))); }
|
float gridStep() const { return std::pow(10.0f, std::floor(std::log10(std::max(viewHeight() / 4.0f, 1e-4f)))); }
|
||||||
|
|
||||||
void draw3D(keel::Renderer& r) {
|
void draw3D(keel::Renderer& r) {
|
||||||
|
settleView();
|
||||||
keel::Camera cam;
|
keel::Camera cam;
|
||||||
cam.origin = eye_ * kUnits, cam.yaw = yaw_, cam.pitch = pitch_, cam.fovY = fov_, cam.shift = shift_;
|
cam.origin = eye_ * kUnits, cam.yaw = yaw_, cam.pitch = pitch_, cam.fovY = fov_, cam.shift = shift_, cam.zNear = nearFar_.x, cam.zFar = nearFar_.y;
|
||||||
r.setCamera(cam);
|
r.setCamera(cam);
|
||||||
const Look& look = scene_.look;
|
const Look& look = scene_.look;
|
||||||
const bool rendered = shading() == Shading::Rendered;
|
const bool rendered = shading() == Shading::Rendered;
|
||||||
|
|
@ -5271,20 +5309,32 @@ private:
|
||||||
const float step = gridStep();
|
const float step = gridStep();
|
||||||
const int count = 20;
|
const int count = 20;
|
||||||
const glm::vec3 centre = target_ - n * glm::dot(target_, n);
|
const glm::vec3 centre = target_ - n * glm::dot(target_, n);
|
||||||
const float cu = snapTo(glm::dot(centre, u), step * 10), cv = snapTo(glm::dot(centre, v), step * 10);
|
|
||||||
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)
|
||||||
for (int i = -count; i <= count; ++i)
|
// Two rings: the fine lines round where you look, then lines ten times apart out to
|
||||||
for (int dir = 0; dir < 2; ++dir) {
|
// the horizon (they leave out the middle, so no line is drawn twice).
|
||||||
const glm::vec3 along = dir ? u : v, across = dir ? v : u;
|
for (int level = 0; level < 2; ++level) {
|
||||||
const float at = (dir ? cv : cu) + float(i) * step, mid = dir ? cu : cv;
|
const float s = step * (level ? 10.0f : 1.0f), inner = count * step; // (the fine ring's reach)
|
||||||
const glm::vec3 a = across * at + along * (mid - count * step), b = across * at + along * (mid + count * step);
|
const float cu = snapTo(glm::dot(centre, u), step * 10), cv = snapTo(glm::dot(centre, v), step * 10);
|
||||||
const bool major = std::abs(snapTo(at, step * 10) - at) < step * 0.01f, axis = std::abs(at) < step * 0.01f;
|
const float fade = level ? 0.6f : 1.0f;
|
||||||
glm::vec4 col = glm::vec4(glm::vec3(shade), major ? (rendered ? 0.35f : 0.13f) : (rendered ? 0.16f : 0.06f));
|
for (int i = -count; i <= count; ++i)
|
||||||
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))
|
for (int dir = 0; dir < 2; ++dir) {
|
||||||
: (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 glm::vec3 along = dir ? u : v, across = dir ? v : u;
|
||||||
const float w = pixelSize(target_) * (major || axis ? 1.6f : 1.0f) * kUnits;
|
const float at = (dir ? cv : cu) + float(i) * s, mid = dir ? cu : cv;
|
||||||
r.drawStreak(a * kUnits, b * kUnits, w, col, keel::kWhiteTexture);
|
const bool major = std::abs(snapTo(at, s * 10) - at) < s * 0.01f;
|
||||||
}
|
const bool axis = std::abs(at) < s * 0.01f;
|
||||||
|
glm::vec4 col = glm::vec4(glm::vec3(shade), (major ? (rendered ? 0.35f : 0.13f) : (rendered ? 0.16f : 0.06f)) * fade);
|
||||||
|
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))
|
||||||
|
: (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 w = pixelSize(target_) * (major || axis ? 1.6f : 1.0f) * kUnits;
|
||||||
|
const float from = mid - count * s, to = mid + count * s;
|
||||||
|
const glm::vec3 a = across * at + along * from, b = across * at + along * to;
|
||||||
|
if (level == 0 || std::abs(at - (dir ? cv : cu)) > inner * 1.0001f) r.drawStreak(a * kUnits, b * kUnits, w, col, keel::kWhiteTexture);
|
||||||
|
else { // (a far line through the fine ring, which has it already: its two ends)
|
||||||
|
r.drawStreak(a * kUnits, (across * at + along * (mid - inner)) * kUnits, w, col, keel::kWhiteTexture);
|
||||||
|
r.drawStreak((across * at + along * (mid + inner)) * kUnits, b * kUnits, w, col, keel::kWhiteTexture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (tab_ == Tab::Print) drawPrintBed(r, light);
|
if (tab_ == Tab::Print) drawPrintBed(r, light);
|
||||||
}
|
}
|
||||||
|
|
@ -5298,38 +5348,30 @@ private:
|
||||||
// Every edge of a mesh (X-ray, wireframe), or just the edges the eye can see.
|
// Every edge of a mesh (X-ray, wireframe), or just the edges the eye can see.
|
||||||
auto lines = [&](const Object& o, const Mesh& mesh, ImU32 col, float width, bool seenOnly) {
|
auto lines = [&](const Object& o, const Mesh& mesh, ImU32 col, float width, bool seenOnly) {
|
||||||
const glm::mat4 m = objectMatrix(o);
|
const glm::mat4 m = objectMatrix(o);
|
||||||
const std::vector<char>* vis = seenOnly && !xray_ ? &visiblePoints(o) : nullptr;
|
const bool hide = seenOnly && !xray_;
|
||||||
std::vector<ImVec2> s(mesh.verts.size());
|
std::vector<ImVec2> s(mesh.verts.size());
|
||||||
std::vector<char> ok(mesh.verts.size());
|
std::vector<char> ok(mesh.verts.size());
|
||||||
for (size_t i = 0; i < s.size(); ++i) ok[i] = project(glm::vec3(m * glm::vec4(mesh.verts[i], 1.0f)), s[i]);
|
for (size_t i = 0; i < s.size(); ++i) ok[i] = project(glm::vec3(m * glm::vec4(mesh.verts[i], 1.0f)), s[i]);
|
||||||
for (auto [a, b] : edges(mesh)) {
|
for (const auto& e : adjacencyOf(o, mesh).edges) {
|
||||||
if (vis && !((*vis)[size_t(a)] && (*vis)[size_t(b)])) continue;
|
if (!ok[size_t(e[0])] || !ok[size_t(e[1])] || (hide && !(seen(o, mesh, e[0]) && seen(o, mesh, e[1])))) continue;
|
||||||
if (ok[size_t(a)] && ok[size_t(b)]) dl->AddLine(s[size_t(a)], s[size_t(b)], col, width);
|
dl->AddLine(s[size_t(e[0])], s[size_t(e[1])], col, width);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// The outline round a selected object (where it turns away from you, and open edges).
|
// The outline round a selected object (where it turns away from you, and open edges).
|
||||||
auto outline = [&](const Object& o, ImU32 col, float width) {
|
auto outline = [&](const Object& o, ImU32 col, float width) {
|
||||||
const Mesh& mesh = shownMesh(o);
|
const Mesh& mesh = shownMesh(o);
|
||||||
const glm::mat4 m = objectMatrix(o);
|
const glm::mat4 m = objectMatrix(o);
|
||||||
std::vector<glm::vec3> w(mesh.verts.size());
|
const Adjacency& adj = adjacencyOf(o, mesh);
|
||||||
for (size_t i = 0; i < w.size(); ++i) w[i] = glm::vec3(m * glm::vec4(mesh.verts[i], 1.0f));
|
// (which faces look at the eye: the eye taken into the object's own space, no point moved)
|
||||||
std::map<std::pair<int, int>, int> side; // bit 1: a face towards you, bit 2: away; + 4 each face
|
const glm::vec3 eye = glm::vec3(glm::inverse(m) * glm::vec4(eye_, 1.0f));
|
||||||
for (size_t f = 0; f < mesh.faces.size(); ++f) {
|
std::vector<char> facing(adj.plane.size());
|
||||||
const auto& fv = mesh.faces[f];
|
for (size_t f = 0; f < facing.size(); ++f) facing[f] = glm::dot(glm::vec3(adj.plane[f]), eye) > adj.plane[f].w;
|
||||||
glm::vec3 n(0.0f);
|
for (const auto& e : adj.edges) {
|
||||||
for (size_t i = 0; i < fv.size(); ++i) n += glm::cross(w[size_t(fv[i])], w[size_t(fv[(i + 1) % fv.size()])]);
|
const bool silhouette = e[3] >= 0 && facing[size_t(e[2])] != facing[size_t(e[3])], open = e[3] < 0 && facing[size_t(e[2])];
|
||||||
const int facing = glm::dot(n, eye_ - w[size_t(fv[0])]) > 0 ? 1 : 2;
|
if (!silhouette && !open) continue;
|
||||||
for (size_t i = 0; i < fv.size(); ++i) {
|
if (!xray_ && !(seen(o, mesh, e[0]) && seen(o, mesh, e[1]))) continue; // (not the edges of a pocket behind its rim)
|
||||||
int& s = side[{std::min(fv[i], fv[(i + 1) % fv.size()]), std::max(fv[i], fv[(i + 1) % fv.size()])}];
|
|
||||||
s = (s | facing) + 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const std::vector<char>* vis = xray_ ? nullptr : &visiblePoints(o, mesh); // (not the edges of a pocket behind its rim)
|
|
||||||
for (auto [e, s] : side) {
|
|
||||||
const bool silhouette = (s & 3) == 3, open = (s >> 2) == 1 && (s & 1);
|
|
||||||
if (vis && !((*vis)[size_t(e.first)] && (*vis)[size_t(e.second)])) continue;
|
|
||||||
ImVec2 a, b;
|
ImVec2 a, b;
|
||||||
if ((silhouette || open) && project(w[size_t(e.first)], a) && project(w[size_t(e.second)], b)) dl->AddLine(a, b, col, width);
|
if (project(glm::vec3(m * glm::vec4(mesh.verts[size_t(e[0])], 1.0f)), a) && project(glm::vec3(m * glm::vec4(mesh.verts[size_t(e[1])], 1.0f)), b)) dl->AddLine(a, b, col, width);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const bool clean = capturing(); // (a picture being saved: no outlines or gizmos)
|
const bool clean = capturing(); // (a picture being saved: no outlines or gizmos)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue