mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-16 21:42:43 +00:00
84 lines
3.4 KiB
C++
84 lines
3.4 KiB
C++
#pragma once
|
|
|
|
// Adaptive subdivision to a target edge length, by global marked-edge (red-green) refinement rather
|
|
// than longest-edge bisection. Marking is global, so two triangles sharing an edge always agree and
|
|
// the result is crack-free by construction. A triangle is rebuilt from its marked-edge count: 0
|
|
// keeps, 1 bisects, 2 fans into three, 3 does the regular 1->4 split.
|
|
//
|
|
// The 1->4 case is what keeps the tessellation regular - its children are similar to the parent. An
|
|
// irregular one shows up after displacement as adjacent triangles tilting alternately, i.e. noise.
|
|
|
|
#include <functional>
|
|
#include <vector>
|
|
|
|
#include "TextureBakeIndex.hpp"
|
|
|
|
namespace Slic3r {
|
|
namespace TextureBake {
|
|
|
|
// Memory guard for the stages downstream. At roughly 145 bytes per triangle this is about 2.9 GB.
|
|
static constexpr int SUBDIVIDE_SAFETY_CAP = 16'000'000;
|
|
|
|
// Vertices at one position stay separate when their faces disagree by more than this: a cube keeps
|
|
// hard edges, a cylinder keeps averaged ones.
|
|
static constexpr double SUBDIVIDE_SHARP_ANGLE_DEG = 30.0;
|
|
|
|
// A depth bound, not a work bound: the loop stops as soon as a pass changes nothing.
|
|
static constexpr int SUBDIVIDE_MAX_ITERATIONS = 12;
|
|
|
|
// Built by the indexers, appended to by the passes. Double precision so repeated midpointing does
|
|
// not drift.
|
|
struct VertStore
|
|
{
|
|
std::vector<Vec3d> pos;
|
|
std::vector<Vec3d> nrm;
|
|
std::vector<double> wgt; // exclusion weights; empty when the caller supplied none
|
|
std::vector<int> canon; // canonical position ids; empty in fast mode
|
|
|
|
size_t count() const { return pos.size(); }
|
|
int push(const Vec3d &p, const Vec3d &n)
|
|
{
|
|
const int idx = int(pos.size());
|
|
pos.push_back(p);
|
|
nrm.push_back(n);
|
|
return idx;
|
|
}
|
|
};
|
|
|
|
struct IndexedMesh
|
|
{
|
|
VertStore verts;
|
|
std::vector<int> indices; // 3 per triangle
|
|
QuantizedPointMap pos_canon_map{ WELD_GRID_GEOMETRY, 256 };
|
|
bool has_canon = false;
|
|
};
|
|
|
|
// Fraction, triangle count, longest remaining edge. Returning false cancels; what comes back is
|
|
// still watertight, because passes apply whole or not at all.
|
|
using SubdivideProgressFn = std::function<bool(double fraction, size_t triangles, double longest_edge)>;
|
|
|
|
struct SubdivideResult
|
|
{
|
|
TriSoup geometry;
|
|
// Output triangle -> input triangle it descends from, so per-face data survives with no remap.
|
|
std::vector<int> face_parent_id;
|
|
bool safety_cap_hit = false;
|
|
};
|
|
|
|
// `face_excluded`: one entry per input triangle; non-zero means its interior is never refined. Its
|
|
// edges still split when an included neighbour marks them, so no T-junction appears at the boundary.
|
|
// `fast` selects the cheap position-only indexer for previews.
|
|
SubdivideResult subdivide(const TriSoup &geometry, double max_edge_length,
|
|
const std::vector<uint8_t> &face_excluded = {}, bool fast = false,
|
|
int safety_cap = SUBDIVIDE_SAFETY_CAP,
|
|
const SubdivideProgressFn &on_progress = {});
|
|
|
|
// Displacement needs the same welding and sharp-edge clustering.
|
|
IndexedMesh to_indexed(const TriSoup &geometry);
|
|
IndexedMesh to_indexed_fast(const TriSoup &geometry);
|
|
TriSoup to_non_indexed(const VertStore &verts, const std::vector<int> &indices,
|
|
const std::vector<uint8_t> &face_excluded);
|
|
|
|
} // namespace TextureBake
|
|
} // namespace Slic3r
|