mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-20 22:18:30 +00:00
The "Mix by" combo main reworded is gone on this branch: ColorMixMode went with the per-triangle interleave. Kept its removal, with main's spelling for the line that stays, and updated the shader comments main added, which still described the interleave.
176 lines
9.1 KiB
C++
176 lines
9.1 KiB
C++
#pragma once
|
|
|
|
// The bake pipeline:
|
|
//
|
|
// subdivide -> [regularize -> re-subdivide] -> [paint test] -> [relocate] -> [flip edges]
|
|
// -> displace -> [decimate] -> bottom clamp -> bottom snap -> [resolve T-junctions]
|
|
//
|
|
// Regularization sits between two subdivisions on purpose: it dissolves the slivers refinement
|
|
// inherited, which lengthens some edges past the target, and the second pass brings those back.
|
|
// Before any subdivision it would have nothing to work on, since the slivers come from refining a
|
|
// needle; after a single pass it would leave the mesh coarser than asked for.
|
|
//
|
|
// Decimation and repair are export-only - decimation drops the output-to-input face mapping a bake
|
|
// needs to carry per-face data forward.
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <vector>
|
|
|
|
#include "libslic3r/Point.hpp"
|
|
#include "TextureBakeDecimate.hpp"
|
|
#include "TextureBakeDisplace.hpp"
|
|
#include "TextureBakeIndex.hpp"
|
|
#include "TextureBakeRegularize.hpp"
|
|
#include "TextureBakeFlip.hpp"
|
|
#include "TextureBakeRelocate.hpp"
|
|
#include "TextureBakeSubdivide.hpp"
|
|
|
|
namespace Slic3r {
|
|
|
|
// Optional step-by-step capture; see TextureBakeDebug.hpp. A pointer, and forward declared, so the
|
|
// pipeline header stays free of the mesh types the recorder converts into.
|
|
class BakeStageRecorder;
|
|
|
|
namespace TextureBake {
|
|
|
|
enum class PipelineMode
|
|
{
|
|
// Keeps the face-parent mapping; skips decimation and repair.
|
|
Bake,
|
|
// The full sequence, including decimation and repair.
|
|
Export,
|
|
};
|
|
|
|
struct PipelineSettings
|
|
{
|
|
// Target edge length for the refinement, in mm.
|
|
double refine_length = 1.0;
|
|
|
|
// Sliver removal between the two subdivision passes.
|
|
bool regularize = true;
|
|
RegularizeOptions regularize_opts;
|
|
// Slightly above the first pass, so it recovers the edges regularization lengthened instead of
|
|
// re-refining what it just merged.
|
|
double regularize_second_pass_mul = 1.1;
|
|
|
|
// Slide vertices onto the height map's own edges before displacing, so a step lands on a mesh
|
|
// edge instead of being quantised to wherever the grid fell.
|
|
bool relocate = false;
|
|
RelocateSettings relocate_opts;
|
|
|
|
// Choose each quad's diagonal to follow the height field before displacing, so a step that crosses
|
|
// the grid at an angle comes out as a straight wall instead of a sawtooth. See TextureBakeFlip.hpp.
|
|
bool flip_edges = true;
|
|
FlipSettings flip_opts;
|
|
|
|
DisplaceSettings displace;
|
|
|
|
// Optional. Asked once per refined face (its centroid, in the soup's coordinates) after the
|
|
// refinement stages and before displacement, for faces whose source triangle was included:
|
|
// false marks the face as unpainted (no displacement), so paint finer than the input triangles
|
|
// is honoured. Faces excluded from the start are never asked. Called from several threads at
|
|
// once, so it must be safe to call concurrently.
|
|
std::function<bool(const Vec3f ¢roid)> painted;
|
|
|
|
// Optional. Whether the paint comes within a radius of a point, which grades the refinement (see
|
|
// subdivide()): only the paint and its surroundings reach refine_length, and the unpainted surface
|
|
// further out stays in whole pieces of its own grid. That is what a later bake painted there
|
|
// refines, so it meets the same grid a first bake does. Called from several threads at once.
|
|
SubdivideWithinFn paint_within;
|
|
|
|
// Export mode only.
|
|
// What this bake may spend on what it refines. Geometry it only preserves (see preserve_untextured),
|
|
// the unpainted surface and an earlier bake's relief on it, is counted on top of it, so that relief
|
|
// does not have to be evicted to fit this one.
|
|
size_t max_triangles = 750'000;
|
|
// Keep removing zero-cost flat faces past the target. Only applies when decimation runs, i.e. when
|
|
// the displaced mesh is over the budget - an under-budget mesh is never decimated.
|
|
bool harvest_flat = true;
|
|
double harvest_tol = DECIMATE_DEFAULT_HARVEST_TOL;
|
|
// Lock the untextured region against both regularization and decimation.
|
|
bool preserve_untextured = true;
|
|
|
|
// Push anything that displaced below the plate back up to it. Downward movement is otherwise left
|
|
// alone, so relief on the underside is kept - only what would sink through the plate is stopped.
|
|
bool clamp_below_plate = false;
|
|
|
|
// Snap vertices within this of the bottom plane onto it. 0 disables.
|
|
double bottom_snap_tol = 0.1;
|
|
|
|
int safety_cap = SUBDIVIDE_SAFETY_CAP;
|
|
};
|
|
|
|
// Stage name and a fraction within it. Returning false cancels the run.
|
|
using PipelineProgressFn = std::function<bool(const char *stage, double fraction)>;
|
|
// Colour class of a point of the surface (a palette index, -1 for none), for the decimation's
|
|
// colour-boundary creases. Only consulted when the mesh is over budget.
|
|
using ColorSampleFn = std::function<int(const Vec3f ¢roid, const Vec3f &normal)>;
|
|
|
|
// Sentinels for PipelineResult::face_color.
|
|
static constexpr int FACE_UNPAINTED = -1; // the paint did not cover this face's origin
|
|
static constexpr int FACE_NO_COLOUR = -2; // painted, but the sampler returned nothing at this point
|
|
|
|
struct PipelineResult
|
|
{
|
|
TriSoup geometry;
|
|
// One entry per output face, carried through decimation, the T-junction repair and the weld.
|
|
// FACE_UNPAINTED means the paint never covered the geometry this face came from; anything else means
|
|
// it did, and is the palette index `color_sample` returned there (FACE_NO_COLOUR when it returned
|
|
// none). The distinction matters: the sampler answers for points on the *base* surface, and these
|
|
// are sampled on the displaced one, so a painted face can easily come back without a colour. Only
|
|
// the painted/unpainted split is reliable here, and that is what a caller should use it for.
|
|
//
|
|
// Empty unless the caller gave a `color_sample`.
|
|
//
|
|
// A caller that needs per-face colour must use this rather than sampling the result again. The
|
|
// result is displaced geometry: a point on it is no longer where its base surface was, so matching
|
|
// it back by proximity colours whatever base surface happens to be nearest - which on a part thinner
|
|
// than the relief depth is the *opposite* face, picking up the texture meant for the painted one.
|
|
std::vector<int> face_color;
|
|
// Output face -> input face. Empty in Export mode, where decimation invalidates it.
|
|
std::vector<int> face_parent_id;
|
|
bool safety_cap_hit = false;
|
|
bool locked_over_budget = false;
|
|
size_t collapse_count = 0;
|
|
bool canceled = false;
|
|
// What the refinement produced, before decimation, and the count it had to fit into (the budget
|
|
// plus the preserved geometry). budget_limited says the refined mesh did not fit without collapsing
|
|
// more than flat faces: the result carries less of the texture than the resolution asked for,
|
|
// which is what a caller warns about.
|
|
size_t triangles_refined = 0;
|
|
size_t triangles_budget = 0;
|
|
bool budget_limited = false;
|
|
// The budget coarsened the mesh, so its triangles are no longer the fine, even ones the refinement
|
|
// laid down - they can be as large as the features on them, which a caller that post-processes per
|
|
// triangle (the colour despeckle) has to know. Harvesting flat faces does not count: what it
|
|
// removes carried nothing. Not the same question as "did a collapse run", which is why
|
|
// face_parent_id being empty is no longer used to answer it.
|
|
bool simplified = false;
|
|
};
|
|
|
|
// `debug`, when given and enabled, receives the mesh after every stage that ran - which is the only
|
|
// way to tell which stage a bad result came from, since each one rewrites the whole mesh.
|
|
PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample,
|
|
const PipelineSettings &settings, const DisplaceBounds &bounds,
|
|
PipelineMode mode, const std::vector<uint8_t> &face_excluded = {},
|
|
const PipelineProgressFn &on_progress = {},
|
|
BakeStageRecorder *debug = nullptr, const ColorSampleFn &color_sample = {});
|
|
|
|
// Snap anything that ended below the model's original bottom back up to it.
|
|
void clamp_below_bottom(TriSoup &geometry, float bottom_z);
|
|
|
|
// Flatten the bed-contact surface by snapping positions within `tol` of the bottom plane onto it.
|
|
//
|
|
// Gated, not unconditional: an unconditional band snap also flattens the undersides of texture relief
|
|
// near the base, folding them coplanar into the bottom face. Folded faces overlap the plate, so edges
|
|
// there pick up four incident faces - non-manifold edges and phantom shells on re-import. All copies
|
|
// of a position move together, and the move is rejected if any incident triangle would go degenerate
|
|
// or rotate more than about 75 degrees. A real bed-contact sliver rotates by a fraction of a degree
|
|
// and still snaps. Returns how many triangles moved.
|
|
size_t snap_bottom_to_flat(TriSoup &geometry, float bottom_z, double tol = 0.1);
|
|
|
|
} // namespace TextureBake
|
|
} // namespace Slic3r
|