载入中...
搜索中...
未找到
PcgMeshLod.cpp
浏览该文件的文档.
2
3#include <cmath>
4#include <limits>
5#include <unordered_map>
6
7namespace eve::procgen {
8namespace {}
9
11 matrix_[0] = matrix_[5] = matrix_[10] = matrix_[15] = 1.F;
12}
14 if (row < 0 || row >= 4 || column < 0 || column >= 4 || !std::isfinite(value))
16 "Pcg mesh transform element is invalid", "procgen.pcgMeshLod"));
17 matrix_[static_cast<std::size_t>(row * 4 + column)] = value;
18 return Result<void>::success();
19}
20float PcgMeshTransform::getElement(int row, int column) const noexcept {
21 return row >= 0 && row < 4 && column >= 0 && column < 4
22 ? matrix_[static_cast<std::size_t>(row * 4 + column)] : 0.F;
23}
24
26 const std::string& defaultMaterialId) {
27 if (mesh.empty() || sources_.size() >= 4096 || defaultMaterialId.empty())
29 DiagnosticCode::InvalidArgument, "Pcg mesh combine source, material identity or source budget is invalid",
30 "procgen.pcgMeshLod"));
31 const int vertices = mesh.getVertexCount(), indices = mesh.getIndexCount();
32 if (vertices <= 0 || indices <= 0 || indices % 3 != 0 ||
33 mesh.positions().size() != static_cast<std::size_t>(vertices) * 3U ||
34 mesh.normals().size() != static_cast<std::size_t>(vertices) * 3U ||
35 mesh.uvs().size() != static_cast<std::size_t>(vertices) * 2U ||
36 (mesh.hasVertexColors() && mesh.colors().size() != static_cast<std::size_t>(vertices) * 4U))
38 DiagnosticCode::InvalidArgument, "Pcg mesh combine source streams are invalid", "procgen.pcgMeshLod"));
39 for (float value : transform.matrix_) if (!std::isfinite(value))
41 DiagnosticCode::InvalidArgument, "Pcg mesh combine transform is non-finite", "procgen.pcgMeshLod"));
42 for (std::uint32_t index : mesh.indices()) if (index >= static_cast<std::uint32_t>(vertices))
44 "Pcg mesh combine source index is out of range",
45 "procgen.pcgMeshLod"));
46 sources_.push_back(Source{mesh, transform, defaultMaterialId});
47 return Result<void>::success();
48}
49void PcgMeshCombinePlan::clear() noexcept { sources_.clear(); }
50int PcgMeshCombinePlan::getSourceCount() const noexcept { return static_cast<int>(sources_.size()); }
51
53 if (plan.sources_.empty())
55 "Pcg static mesh combine plan is empty", "procgen.pcgMeshLod"));
56 std::uint64_t vertexCount = 0, indexCount = 0;
57 bool anyColors = false;
58 for (const auto& source : plan.sources_) {
59 vertexCount += static_cast<std::uint64_t>(source.mesh.getVertexCount());
60 indexCount += static_cast<std::uint64_t>(source.mesh.getIndexCount());
61 anyColors = anyColors || source.mesh.hasVertexColors();
62 }
63 if (vertexCount > 8U * 1024U * 1024U || indexCount > 48U * 1024U * 1024U ||
64 vertexCount > static_cast<std::uint64_t>(std::numeric_limits<int>::max()) ||
65 indexCount > static_cast<std::uint64_t>(std::numeric_limits<int>::max()))
67 "Pcg static mesh combine output exceeds native mesh budgets",
68 "procgen.pcgMeshLod"));
69 MeshBuild candidate;
70 candidate.reserve(static_cast<int>(vertexCount), static_cast<int>(indexCount));
71 std::vector<float> colors;
72 if (anyColors) colors.reserve(static_cast<std::size_t>(vertexCount) * 4U);
73 struct Bucket { std::string material; std::vector<std::uint32_t> indices; };
74 std::vector<Bucket> buckets;
75 std::unordered_map<std::string, std::size_t> materialMap;
76 std::uint32_t vertexOffset = 0;
77 for (const auto& source : plan.sources_) {
78 const auto& mesh = source.mesh;
79 const auto& m = source.transform.matrix_;
80 for (int vertex = 0; vertex < mesh.getVertexCount(); ++vertex) {
81 const float x=mesh.getPositionX(vertex),y=mesh.getPositionY(vertex),z=mesh.getPositionZ(vertex);
82 const float nx=mesh.getNormalX(vertex),ny=mesh.getNormalY(vertex),nz=mesh.getNormalZ(vertex);
83 const float px=m[0]*x+m[1]*y+m[2]*z+m[3],py=m[4]*x+m[5]*y+m[6]*z+m[7];
84 const float pz=m[8]*x+m[9]*y+m[10]*z+m[11];
85 const float tx=m[0]*nx+m[1]*ny+m[2]*nz,ty=m[4]*nx+m[5]*ny+m[6]*nz;
86 const float tz=m[8]*nx+m[9]*ny+m[10]*nz;
87 if (!std::isfinite(px) || !std::isfinite(py) || !std::isfinite(pz) || !std::isfinite(tx) ||
88 !std::isfinite(ty) || !std::isfinite(tz))
90 DiagnosticCode::InvalidArgument, "Pcg static mesh transform overflowed", "procgen.pcgMeshLod"));
91 candidate.addVertex(px,py,pz,tx,ty,tz,mesh.getUvU(vertex),mesh.getUvV(vertex));
92 if(anyColors)for(int component=0;component<4;++component)
93 colors.push_back(mesh.hasVertexColors()?mesh.getColor(vertex,component):1.F);
94 }
95 for (int triangle = 0; triangle < mesh.getIndexCount() / 3; ++triangle) {
96 const int group = mesh.getTriangleGroup(triangle);
97 std::string material = group >= 0 ? mesh.getGroupName(group) : source.defaultMaterialId;
98 if (material.empty())
100 "Pcg static mesh triangle has no material identity",
101 "procgen.pcgMeshLod"));
102 auto [found, inserted] = materialMap.emplace(material,buckets.size());
103 if(inserted)buckets.push_back(Bucket{material,{}});
104 auto& indices=buckets[found->second].indices;
105 for(int corner=0;corner<3;++corner)
106 indices.push_back(vertexOffset+static_cast<std::uint32_t>(mesh.getIndex(triangle*3+corner)));
107 }
108 vertexOffset += static_cast<std::uint32_t>(mesh.getVertexCount());
109 }
110 for(auto& bucket:buckets){candidate.setActiveGroup(bucket.material);
111 for(std::size_t i=0;i<bucket.indices.size();i+=3)
112 candidate.addTriangle(bucket.indices[i],bucket.indices[i+1],bucket.indices[i+2]);}
113 if(anyColors){auto set=candidate.setVertexColors(std::move(colors));if(!set.ok())return Result<int>::failure(set.status());}
114 candidate.setMeta("kind","pcg.mesh.combined");
115 candidate.setMeta("sourceCount",std::to_string(plan.sources_.size()));
116 output=std::move(candidate);
117 return Result<int>::success(static_cast<int>(indexCount/3U));
118}
119Result<void> PcgMeshLodProfile::appendLevel(float transition, float fade, float quality,
120 bool combineMeshes, bool combineSubMeshes) {
121 if (levels_.size() >= 4 || !std::isfinite(transition) || transition < 0.F || transition > 1.F ||
122 !std::isfinite(fade) || fade < 0.F || fade > 1.F || !std::isfinite(quality) || quality < 0.F ||
123 quality > 1.F || (combineSubMeshes && !combineMeshes))
125 "Pcg mesh LOD level values are invalid", "procgen.pcgMeshLod"));
126 if (!levels_.empty() && transition >= levels_.back().screenRelativeTransitionHeight)
128 "Pcg mesh LOD transition heights must be strictly descending",
129 "procgen.pcgMeshLod"));
132 level.fadeTransitionWidth = fade;
133 level.quality = quality;
134 level.combineMeshes = combineMeshes;
135 level.combineSubMeshes = combineSubMeshes;
136 level.simplification.preserveBorderEdges = false;
137 level.simplification.vertexLinkDistance = std::numeric_limits<double>::denorm_min();
138 levels_.push_back(level);
139 return Result<void>::success();
140}
141
142Result<void> PcgMeshLodProfile::setLevelRendererState(int index, int skinQuality, int shadowCastingMode,
143 bool receiveShadows, int motionVectorMode,
144 bool skinnedMotionVectors, int lightProbeUsage,
145 int reflectionProbeUsage) {
146 const bool validSkinQuality = skinQuality == 0 || skinQuality == 1 || skinQuality == 2 || skinQuality == 4;
147 const bool validLightProbe = lightProbeUsage == 0 || lightProbeUsage == 1 || lightProbeUsage == 2 ||
148 lightProbeUsage == 4;
149 if (index < 0 || index >= static_cast<int>(levels_.size()) || !validSkinQuality ||
150 shadowCastingMode < 0 || shadowCastingMode > 3 || motionVectorMode < 0 || motionVectorMode > 2 ||
151 !validLightProbe || reflectionProbeUsage < 0 || reflectionProbeUsage > 2)
153 "Pcg mesh LOD renderer state is invalid", "procgen.pcgMeshLod"));
155 state.skinQuality = skinQuality;
156 state.shadowCastingMode = shadowCastingMode;
157 state.receiveShadows = receiveShadows;
158 state.motionVectorMode = motionVectorMode;
159 state.skinnedMotionVectors = skinnedMotionVectors;
160 state.lightProbeUsage = lightProbeUsage;
161 state.reflectionProbeUsage = reflectionProbeUsage;
162 levels_[static_cast<std::size_t>(index)].renderer = state;
163 return Result<void>::success();
164}
165
166int PcgMeshLodProfile::getLevelRendererState(int index, int field) const noexcept {
167 const auto* level = levelAt(index);
168 if (!level || field < 0 || field > 6) return -1;
169 const auto& state = level->renderer;
170 switch (field) {
171 case 0: return state.skinQuality;
172 case 1: return state.shadowCastingMode;
173 case 2: return state.receiveShadows ? 1 : 0;
174 case 3: return state.motionVectorMode;
175 case 4: return state.skinnedMotionVectors ? 1 : 0;
176 case 5: return state.lightProbeUsage;
177 case 6: return state.reflectionProbeUsage;
178 default: return -1;
179 }
180}
181
182Result<void> PcgMeshLodProfile::setFadePolicy(int fadeMode, bool animateCrossFading,
183 float animationDuration) {
184 if ((fadeMode < 0 || fadeMode > 2) || !std::isfinite(animationDuration) || animationDuration <= 0.F ||
185 (animateCrossFading && fadeMode == 0))
187 "Pcg mesh LOD fade policy is invalid", "procgen.pcgMeshLod"));
188 fadeMode_ = fadeMode;
189 animateCrossFading_ = animateCrossFading;
190 crossFadeAnimationDuration_ = animationDuration;
191 return Result<void>::success();
192}
193
195 return index >= 0 && index < static_cast<int>(levels_.size()) ? &levels_[static_cast<std::size_t>(index)] : nullptr;
196}
197
198float PcgMeshLodSet::getTransitionHeight(int index) const noexcept {
199 return index >= 0 && index < static_cast<int>(levels_.size())
200 ? levels_[static_cast<std::size_t>(index)].screenRelativeTransitionHeight : -1.F;
201}
202float PcgMeshLodSet::getFadeWidth(int index) const noexcept {
203 return index >= 0 && index < static_cast<int>(levels_.size())
204 ? levels_[static_cast<std::size_t>(index)].fadeTransitionWidth : -1.F;
205}
206float PcgMeshLodSet::getQuality(int index) const noexcept {
207 return index >= 0 && index < static_cast<int>(levels_.size())
208 ? levels_[static_cast<std::size_t>(index)].quality : -1.F;
209}
210const MeshBuild* PcgMeshLodSet::meshAt(int index) const noexcept {
211 return index >= 0 && index < static_cast<int>(meshes_.size()) ? &meshes_[static_cast<std::size_t>(index)] : nullptr;
212}
214 return index >= 0 && index < static_cast<int>(levels_.size())
215 ? &levels_[static_cast<std::size_t>(index)].renderer : nullptr;
216}
217int PcgMeshLodSet::selectLevel(float relativeHeight) const noexcept {
218 if (!std::isfinite(relativeHeight) || relativeHeight < 0.F || levels_.empty()) return -2;
219 for (int i = 0; i < static_cast<int>(levels_.size()); ++i)
220 if (relativeHeight >= levels_[static_cast<std::size_t>(i)].screenRelativeTransitionHeight) return i;
221 return -1;
222}
223float PcgMeshLodSet::getSwitchDistance(int level, float diameter, float fov) const noexcept {
224 if (level < 0 || level >= static_cast<int>(levels_.size()) || !std::isfinite(diameter) || diameter <= 0.F ||
225 !std::isfinite(fov) || fov <= 0.F || fov >= 180.F) return -1.F;
226 constexpr float radians = 0.01745329251994329577F;
227 const float threshold = levels_[static_cast<std::size_t>(level)].screenRelativeTransitionHeight;
228 if (threshold <= 0.F) return -1.F;
229 return diameter / (2.F * std::tan(fov * 0.5F * radians) * threshold);
230}
231
233 const PcgMeshLodProfile& profile) {
234 const auto& levels = profile.levels();
235 if (source.empty() || levels.empty() || levels.size() > 4)
237 DiagnosticCode::InvalidArgument, "Pcg mesh LOD requires a non-empty mesh and one through four levels",
238 "procgen.pcgMeshLod"));
239 PcgMeshLodSet candidate;
240 candidate.levels_ = levels;
241 candidate.fadeMode_ = profile.getFadeMode();
242 candidate.animateCrossFading_ = profile.getAnimateCrossFading();
243 candidate.crossFadeAnimationDuration_ = profile.getCrossFadeAnimationDuration();
244 candidate.meshes_.reserve(levels.size());
245 for (const PcgMeshLodLevel& level : levels) {
247 auto simplified = simplifyGtsMesh(mesh, source, level.quality, level.simplification);
248 if (!simplified.ok()) return Result<void>::failure(simplified.status());
249 candidate.meshes_.push_back(std::move(mesh));
250 }
251 output = std::move(candidate);
252 return Result<void>::success();
253}
254
256 const PcgMeshLodProfile& profile) {
257 if (profile.levels().empty())
259 "Pcg combined mesh LOD requires at least one level",
260 "procgen.pcgMeshLod"));
261 for (const PcgMeshLodLevel& level : profile.levels())
262 if (!level.combineMeshes)
264 DiagnosticCode::InvalidArgument, "Every Pcg combined mesh LOD level must enable mesh combination",
265 "procgen.pcgMeshLod"));
266 MeshBuild combined;
267 auto combinedResult = combinePcgStaticMeshesInto(combined, plan);
268 if (!combinedResult.ok()) return Result<void>::failure(combinedResult.status());
269 return buildPcgMeshLodsInto(output, combined, profile);
270}
271
272} // namespace eve::procgen
double value
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
std::string output
building::EdgeCurveGroup group
float py
float nx
float nz
float ny
float pz
int column
int triangle
std::uint32_t vertexCount
std::uint32_t indexCount
std::vector< std::uint32_t > indices
std::vector< Colorf > px
int level
std::vector< Point > vertices
Mesh * mesh
int lightProbeUsage
Material * material
std::vector< float > colors
bool found
uint32_t index
const UnitySourceAsset & source
float m[16]
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
CPU triangle mesh from procedural mesh recipes (e.g. marching cubes). Positions/normals are xyz-packe...
Definition MeshBuild.h:19
void addVertex(float px, float py, float pz, float nx, float ny, float nz, float u, float v)
Adds vertex.
Definition MeshBuild.cpp:33
void addTriangle(uint32_t i0, uint32_t i1, uint32_t i2)
Adds triangle.
Definition MeshBuild.cpp:46
void reserve(int vertexCount, int indexCount)
Reserve.
Definition MeshBuild.cpp:20
void setMeta(const std::string &key, const std::string &value)
Sets the meta.
int setActiveGroup(const std::string &name)
Select/create the named group assigned to subsequently added triangles.
Definition MeshBuild.cpp:53
eve::Result< void > setVertexColors(std::vector< float > colors)
Atomically replace the optional packed RGBA stream; empty removes it.
Owned ordered input plan for Pcg static MeshCombiner.
Definition PcgMeshLod.h:64
void clear() noexcept
Remove every owned source.
int getSourceCount() const noexcept
Return the ordered source count.
Result< void > appendSource(const MeshBuild &mesh, const PcgMeshTransform &transform, const std::string &defaultMaterialId)
Copy one readable mesh and root-relative affine transform into the plan.
Mutable value profile for at most four native renderer LOD levels.
Definition PcgMeshLod.h:97
const std::vector< PcgMeshLodLevel > & levels() const noexcept
Borrow all levels for synchronous native compilation. @ownership The profile retains ownership....
Definition PcgMeshLod.h:129
bool getAnimateCrossFading() const noexcept
Return whether transitions advance from injected dt.
Definition PcgMeshLod.h:115
int getLevelRendererState(int index, int field) const noexcept
Return one renderer state field (0..6), or -1 for invalid input.
const PcgMeshLodLevel * levelAt(int index) const noexcept
Return an immutable level, or null for an invalid index. @ownership The profile retains ownership....
float getCrossFadeAnimationDuration() const noexcept
Return time-driven transition duration in seconds.
Definition PcgMeshLod.h:117
Result< void > setLevelRendererState(int index, int skinQuality, int shadowCastingMode, bool receiveShadows, int motionVectorMode, bool skinnedMotionVectors, int lightProbeUsage, int reflectionProbeUsage)
Replace one level's renderer policy after validating every Pcg enum value.
Result< void > appendLevel(float transitionHeight, float fadeWidth, float quality, bool combineMeshes=false, bool combineSubMeshes=false)
Append one level after validating its scalar ranges.
Result< void > setFadePolicy(int fadeMode, bool animateCrossFading, float animationDuration=0.5F)
Configure Unity LODFadeMode (0 None, 1 SpeedTree, 2 CrossFade) and optional time animation.
int getFadeMode() const noexcept
Return configured Unity LODFadeMode.
Definition PcgMeshLod.h:113
Owned generic mesh LOD output generated with Pcg's independent-per-level policy.
Definition PcgMeshLod.h:144
float getQuality(int index) const noexcept
Return one level's quality, or -1 for an invalid index.
int selectLevel(float relativeHeight) const noexcept
Select a level from projected relative height; -1 means culled and -2 invalid input.
const PcgMeshLodRendererState * rendererStateAt(int index) const noexcept
Borrow one generated level's immutable renderer state, or null.
float getTransitionHeight(int index) const noexcept
Return one level's transition height, or -1 for an invalid index.
const MeshBuild * meshAt(int index) const noexcept
Borrow a generated mesh, or null for an invalid index. @ownership The set retains ownership....
float getSwitchDistance(int level, float worldDiameter, float verticalFovDegrees) const noexcept
Convert one level threshold to camera distance, or -1 for invalid input.
float getFadeWidth(int index) const noexcept
Return one level's fade width, or -1 for an invalid index.
Row-major affine matrix used by Pcg's root-relative static mesh combination.
Definition PcgMeshLod.h:43
PcgMeshTransform() noexcept
Construct an identity transform.
float getElement(int row, int column) const noexcept
Return one element, or zero for an invalid coordinate.
Result< void > setElement(int row, int column, float value)
Set one finite row/column element in the 4x4 matrix.
Result< int > simplifyGtsMesh(MeshBuild &output, const MeshBuild &source, float quality, const GtsMeshSimplificationOptions &o)
Simplify a mesh with deterministic quadric edge collapse.
Result< int > combinePcgStaticMeshesInto(MeshBuild &output, const PcgMeshCombinePlan &plan)
Combine static meshes with Pcg material deduplication and root-relative transforms.
Result< void > buildPcgCombinedMeshLodsInto(PcgMeshLodSet &output, const PcgMeshCombinePlan &plan, const PcgMeshLodProfile &profile)
Combine an ordered static-renderer plan and generate every combined LOD atomically.
Result< void > buildPcgMeshLodsInto(PcgMeshLodSet &output, const MeshBuild &source, const PcgMeshLodProfile &profile)
Generate a complete generic Pcg LOD chain and publish it atomically.
One UnityMeshSimplifierPcg LOD level translated to native mesh settings.
Definition PcgMeshLod.h:80
Pcg/Unity renderer policies retained per generated mesh LOD.
Definition PcgMeshLod.h:32
uint32_t bucket