mirror of
https://github.com/Nexoniarz/Fry3DX.git
synced 2026-11-03 04:24:26 +00:00
Speed: the shown shape, its model, picking tree and edges are made again only when the shape changes (a key of its points, faces and touches), not each time it's moved; big smoothed or cut shapes made on another thread (what was there stays till it's ready)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
93c65638d8
commit
1cda87b19a
1 changed files with 80 additions and 10 deletions
90
src/app.cpp
90
src/app.cpp
|
|
@ -1038,9 +1038,12 @@ private:
|
||||||
std::map<uint32_t, uint64_t> versions_;
|
std::map<uint32_t, uint64_t> versions_;
|
||||||
uint64_t versionClock_ = 1;
|
uint64_t versionClock_ = 1;
|
||||||
struct Shown {
|
struct Shown {
|
||||||
uint64_t version = 0;
|
uint64_t version = 0; // (the shape key it was made for)
|
||||||
bool mirrorOnly = false;
|
bool mirrorOnly = false;
|
||||||
Mesh mesh;
|
Mesh mesh;
|
||||||
|
std::future<Mesh> coming; // (a big one being made on another thread)
|
||||||
|
uint64_t comingVersion = 0;
|
||||||
|
bool comingMirror = false;
|
||||||
};
|
};
|
||||||
std::map<uint32_t, Shown> shown_;
|
std::map<uint32_t, Shown> shown_;
|
||||||
struct Picture {
|
struct Picture {
|
||||||
|
|
@ -1116,6 +1119,42 @@ private:
|
||||||
}
|
}
|
||||||
Object* editing() { return mode_ == Mode::Edit ? find(active_) : nullptr; }
|
Object* editing() { return mode_ == Mode::Edit ? find(active_) : nullptr; }
|
||||||
void touched(uint32_t id) { versions_[id] = ++versionClock_, dirty_ = true, ++edits_; }
|
void touched(uint32_t id) { versions_[id] = ++versionClock_, dirty_ = true, ++edits_; }
|
||||||
|
// What decides the shape an object shows, as a number: its points and faces, its finishing
|
||||||
|
// touches, creases, cuts, shading and painting's layout (not where it is). Worked out again
|
||||||
|
// only when it's been touched: moving a smoothed object doesn't smooth it again each frame.
|
||||||
|
struct ShapeKey {
|
||||||
|
uint64_t version = 0, key = 0;
|
||||||
|
};
|
||||||
|
std::map<uint32_t, ShapeKey> shapeKeys_;
|
||||||
|
uint64_t shapeKey(const Object& o) {
|
||||||
|
ShapeKey& k = shapeKeys_[o.id];
|
||||||
|
const uint64_t v = versions_[o.id];
|
||||||
|
if (k.version == v && v) return k.key;
|
||||||
|
uint64_t h = 1469598103934665603ull;
|
||||||
|
auto mix = [&](const void* p, size_t n) {
|
||||||
|
const auto* b = static_cast<const uint8_t*>(p);
|
||||||
|
size_t i = 0;
|
||||||
|
for (; i + 8 <= n; i += 8) { // (8 bytes at a time: a big mesh in a few ms)
|
||||||
|
uint64_t w;
|
||||||
|
std::memcpy(&w, b + i, 8);
|
||||||
|
h = (h ^ w) * 1099511628211ull;
|
||||||
|
}
|
||||||
|
for (; i < n; ++i) h = (h ^ b[i]) * 1099511628211ull;
|
||||||
|
};
|
||||||
|
mix(o.mesh.verts.data(), o.mesh.verts.size() * sizeof(glm::vec3));
|
||||||
|
for (const auto& f : o.mesh.faces) {
|
||||||
|
const uint64_t n = f.size();
|
||||||
|
mix(&n, 8), mix(f.data(), f.size() * sizeof(int));
|
||||||
|
}
|
||||||
|
for (const auto& [a, b] : o.creases) mix(&a, 4), mix(&b, 4);
|
||||||
|
for (const auto& f : o.uvs) mix(f.data(), f.size() * sizeof(glm::vec2));
|
||||||
|
const uint64_t cut = cutKeys_.count(o.id) ? cutKeys_[o.id] : 0;
|
||||||
|
const void* pic = o.picture.get();
|
||||||
|
mix(&cut, 8), mix(&pic, sizeof pic);
|
||||||
|
mix(&o.mirror, 4), mix(&o.round, 4), mix(&o.roundAngle, 4), mix(&o.roundSteps, 4), mix(&o.smoothLevels, 4), mix(&o.smoothKeepSharp, 1), mix(&o.smooth, 1), mix(&o.smoothAngle, 4);
|
||||||
|
k = {v, h ? h : 1};
|
||||||
|
return k.key;
|
||||||
|
}
|
||||||
void touchAll() {
|
void touchAll() {
|
||||||
for (const Object& o : scene_.objects) versions_[o.id] = ++versionClock_;
|
for (const Object& o : scene_.objects) versions_[o.id] = ++versionClock_;
|
||||||
}
|
}
|
||||||
|
|
@ -4382,8 +4421,8 @@ private:
|
||||||
float reach = 1.0f; // (how far from the middle the furthest object reaches, m)
|
float reach = 1.0f; // (how far from the middle the furthest object reaches, m)
|
||||||
for (const Object& o : scene_.objects) {
|
for (const Object& o : scene_.objects) {
|
||||||
auto& [version, radius] = radii_[o.id];
|
auto& [version, radius] = radii_[o.id];
|
||||||
if (version != versions_[o.id] || !version) {
|
if (version != shapeKey(o) || !version) {
|
||||||
version = versions_[o.id], radius = 0;
|
version = shapeKey(o), radius = 0;
|
||||||
for (const glm::vec3& v : o.mesh.verts) radius = std::max(radius, glm::length(v));
|
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)}));
|
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)}));
|
||||||
|
|
@ -4422,16 +4461,47 @@ private:
|
||||||
// The mesh as seen: with its rounding, smoothing and mirror — but while its shape is being
|
// The mesh as seen: with its rounding, smoothing and mirror — but while its shape is being
|
||||||
// edited, only the mirror (the rest would hide the points you're moving).
|
// edited, only the mirror (the rest would hide the points you're moving).
|
||||||
// Editable cuts show (but not while its own shape's being edited: the points you move).
|
// Editable cuts show (but not while its own shape's being edited: the points you move).
|
||||||
|
// A big one (smoothed into many faces, cut) is made on another thread: what was shown stays
|
||||||
|
// till it's ready, so the window doesn't stop.
|
||||||
const Mesh& shownMesh(const Object& o) {
|
const Mesh& shownMesh(const Object& o) {
|
||||||
const bool editingThis = mode_ == Mode::Edit && o.id == active_;
|
const bool editingThis = mode_ == Mode::Edit && o.id == active_;
|
||||||
const bool cut = !editingThis && cutKeys_.count(o.id);
|
const bool cut = !editingThis && cutKeys_.count(o.id);
|
||||||
if ((!o.hasFinish() && !cut) || (editingThis && !o.mirror)) return o.mesh;
|
if ((!o.hasFinish() && !cut) || (editingThis && !o.mirror)) return o.mesh;
|
||||||
Shown& s = shown_[o.id];
|
Shown& s = shown_[o.id];
|
||||||
const uint64_t v = versions_[o.id];
|
const uint64_t v = shapeKey(o);
|
||||||
if (!(s.version == v && v && s.mirrorOnly == editingThis))
|
if (s.coming.valid() && s.coming.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
|
||||||
|
Mesh got = s.coming.get();
|
||||||
|
if (s.comingVersion == v && s.comingMirror == editingThis) s.mesh = std::move(got), s.version = v, s.mirrorOnly = editingThis;
|
||||||
|
}
|
||||||
|
if (s.version == v && v && s.mirrorOnly == editingThis) return s.mesh;
|
||||||
|
// (what it'll come to: its faces four times over for each level of smoothing)
|
||||||
|
const size_t faces = o.mesh.faces.size() * (size_t(1) << (2 * std::clamp(editingThis ? 0 : o.smoothLevels, 0, 6))) * (o.mirror ? 2 : 1);
|
||||||
|
const bool big = faces > 40000 || (cut && o.mesh.faces.size() > 20000);
|
||||||
|
if (!big || s.mesh.verts.empty()) { // (small: now; the first time: now too, there's nothing to show yet)
|
||||||
s.mesh = cut ? fry::cutMesh(scene_, o) : finishedMesh(o, editingThis), s.version = v, s.mirrorOnly = editingThis;
|
s.mesh = cut ? fry::cutMesh(scene_, o) : finishedMesh(o, editingThis), s.version = v, s.mirrorOnly = editingThis;
|
||||||
|
return s.mesh;
|
||||||
|
}
|
||||||
|
if (!s.coming.valid() || s.comingVersion != v || s.comingMirror != editingThis) {
|
||||||
|
if (s.coming.valid()) s.coming.wait(); // (one at a time for each object: the old one's thrown away)
|
||||||
|
Scene part; // (just it and what cuts it: the thread has its own copies)
|
||||||
|
part.objects.push_back(o);
|
||||||
|
if (cut)
|
||||||
|
for (const Object& c : scene_.objects)
|
||||||
|
if (c.cuts == o.id && c.id != o.id) part.objects.push_back(c);
|
||||||
|
s.comingVersion = v, s.comingMirror = editingThis;
|
||||||
|
s.coming = std::async(std::launch::async, [part = std::move(part), cut, editingThis] {
|
||||||
|
return cut ? fry::cutMesh(part, part.objects[0]) : finishedMesh(part.objects[0], editingThis);
|
||||||
|
});
|
||||||
|
message_ = "Working it out...", messageAt_ = now();
|
||||||
|
}
|
||||||
return s.mesh;
|
return s.mesh;
|
||||||
}
|
}
|
||||||
|
// The key of the mesh actually shown (the one before, while a new one's being made).
|
||||||
|
uint64_t meshKey(const Object& o, const Mesh& mesh) {
|
||||||
|
if (&mesh == &o.mesh) return shapeKey(o);
|
||||||
|
auto it = shown_.find(o.id);
|
||||||
|
return it != shown_.end() && &it->second.mesh == &mesh ? it->second.version * 2 + it->second.mirrorOnly : shapeKey(o);
|
||||||
|
}
|
||||||
// Each object something's cut out of, and what its cutters are (their shapes, where they are):
|
// Each object something's cut out of, and what its cutters are (their shapes, where they are):
|
||||||
// a change, and it's made again (the caches follow its version).
|
// a change, and it's made again (the caches follow its version).
|
||||||
std::map<uint32_t, uint64_t> cutKeys_;
|
std::map<uint32_t, uint64_t> cutKeys_;
|
||||||
|
|
@ -4541,7 +4611,7 @@ private:
|
||||||
const fry::TriangleTree& treeOf(const Object& o, const Mesh& mesh) {
|
const fry::TriangleTree& treeOf(const Object& o, const Mesh& mesh) {
|
||||||
Tree& t = trees_[{o.id, &mesh != &o.mesh}];
|
Tree& t = trees_[{o.id, &mesh != &o.mesh}];
|
||||||
const glm::mat4 m = objectMatrix(o);
|
const glm::mat4 m = objectMatrix(o);
|
||||||
const uint64_t version = versions_[o.id];
|
const uint64_t version = meshKey(o, mesh);
|
||||||
if (t.version != version || !version || t.matrix != m || t.faces != mesh.faces.size() || t.tree.points().size() != mesh.verts.size())
|
if (t.version != version || !version || t.matrix != m || t.faces != mesh.faces.size() || t.tree.points().size() != mesh.verts.size())
|
||||||
t.version = version, t.matrix = m, t.faces = mesh.faces.size(), t.tree = fry::TriangleTree(mesh, m);
|
t.version = version, t.matrix = m, t.faces = mesh.faces.size(), t.tree = fry::TriangleTree(mesh, m);
|
||||||
return t.tree;
|
return t.tree;
|
||||||
|
|
@ -4591,7 +4661,7 @@ private:
|
||||||
std::map<std::pair<uint32_t, bool>, Adjacency> adjacency_;
|
std::map<std::pair<uint32_t, bool>, Adjacency> adjacency_;
|
||||||
const Adjacency& adjacencyOf(const Object& o, const Mesh& mesh) {
|
const Adjacency& adjacencyOf(const Object& o, const Mesh& mesh) {
|
||||||
Adjacency& a = adjacency_[{o.id, &mesh != &o.mesh}];
|
Adjacency& a = adjacency_[{o.id, &mesh != &o.mesh}];
|
||||||
const uint64_t version = versions_[o.id];
|
const uint64_t version = meshKey(o, mesh);
|
||||||
if (a.version == version && version && a.verts == mesh.verts.size() && a.faces == mesh.faces.size()) return a;
|
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.version = version, a.verts = mesh.verts.size(), a.faces = mesh.faces.size();
|
||||||
a.edges.clear(), a.plane.resize(mesh.faces.size());
|
a.edges.clear(), a.plane.resize(mesh.faces.size());
|
||||||
|
|
@ -5727,10 +5797,10 @@ private:
|
||||||
|
|
||||||
keel::ModelId modelOf(const Object& o) {
|
keel::ModelId modelOf(const Object& o) {
|
||||||
const bool editingThis = mode_ == Mode::Edit && o.id == active_;
|
const bool editingThis = mode_ == Mode::Edit && o.id == active_;
|
||||||
const uint64_t want = (versions_[o.id] * 2 + editingThis) * 2 + painted(o);
|
|
||||||
auto it = models_.find(o.id);
|
|
||||||
if (it != models_.end() && it->second.second == want && versions_[o.id]) return it->second.first;
|
|
||||||
const Mesh& mesh = shownMesh(o);
|
const Mesh& mesh = shownMesh(o);
|
||||||
|
const uint64_t want = (meshKey(o, mesh) * 2 + editingThis) * 2 + painted(o);
|
||||||
|
auto it = models_.find(o.id);
|
||||||
|
if (it != models_.end() && it->second.second == want) return it->second.first;
|
||||||
keel::KmdlModel km;
|
keel::KmdlModel km;
|
||||||
keel::KmdlMaterial mat{};
|
keel::KmdlMaterial mat{};
|
||||||
mat.baseColor[0] = mat.baseColor[1] = mat.baseColor[2] = mat.baseColor[3] = 1.0f;
|
mat.baseColor[0] = mat.baseColor[1] = mat.baseColor[2] = mat.baseColor[3] = 1.0f;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue