载入中...
搜索中...
未找到
GtsMeshSplitter.cpp
浏览该文件的文档.
2#include <algorithm>
3#include <cmath>
4#include <limits>
5
6namespace eve::procgen {
7namespace {
8struct Vertex {
9 float x, y, z, nx, ny, nz, u, v, r, g, b, a;
10};
11Vertex mix(const Vertex& a, const Vertex& b, float t) {
12 Vertex r{};
13 const float* pa = &a.x;
14 const float* pb = &b.x;
15 float* pr = &r.x;
16 for (int i = 0; i < 12; ++i) pr[i] = pa[i] + (pb[i] - pa[i]) * t;
17 return r;
18}
19template <class Distance>
20std::vector<Vertex> clip(const std::vector<Vertex>& in, Distance distance) {
21 std::vector<Vertex> out;
22 if (in.empty()) return out;
23 out.reserve(in.size() + 1);
24 Vertex previous = in.back();
25 float dp = distance(previous);
26 bool previousInside = dp >= 0.f;
27 for (const auto& current : in) {
28 const float dc = distance(current);
29 const bool currentInside = dc >= 0.f;
30 if (currentInside != previousInside) {
31 const float denominator = dp - dc;
32 const float t = std::abs(denominator) > 1e-20f ? dp / denominator : 0.f;
33 out.push_back(mix(previous, current, std::clamp(t, 0.f, 1.f)));
34 }
35 if (currentInside) out.push_back(current);
37 dp = dc;
38 previousInside = currentInside;
39 }
40 return out;
41}
42bool finiteVertex(const Vertex& v) {
43 const float* p = &v.x;
44 for (int i = 0; i < 12; ++i) {
45 if (!std::isfinite(p[i])) return false;
46 }
47 return true;
48}
49bool nonDegenerate(const Vertex& a, const Vertex& b, const Vertex& c) {
50 const double abx = double(b.x) - a.x, aby = double(b.y) - a.y, abz = double(b.z) - a.z;
51 const double acx = double(c.x) - a.x, acy = double(c.y) - a.y, acz = double(c.z) - a.z;
52 const double nx = aby * acz - abz * acy, ny = abz * acx - abx * acz, nz = abx * acy - aby * acx;
53 return nx * nx + ny * ny + nz * nz > 1e-24;
54}
55} // namespace
56
58 return index >= 0 && index < getTileCount() ? &tiles_[static_cast<std::size_t>(index)] : nullptr;
59}
60std::unique_ptr<MeshBuild> GtsMeshSplitResult::copyTileMesh(int index) const {
61 const auto* tile = tileAt(index);
62 return tile ? std::make_unique<MeshBuild>(tile->mesh) : nullptr;
63}
64float GtsMeshSplitResult::getTileOffsetX(int index) const noexcept {
65 const auto* tile = tileAt(index);
66 return tile ? tile->offsetX : 0.f;
67}
68float GtsMeshSplitResult::getTileOffsetZ(int index) const noexcept {
69 const auto* tile = tileAt(index);
70 return tile ? tile->offsetZ : 0.f;
71}
72
73Result<GtsMeshSplitResult> splitGtsMesh(const MeshBuild& source, int xSplits, int zSplits, GtsMeshPivot pivot) {
74 const int vertexCount = source.getVertexCount(), indexCount = source.getIndexCount();
75 if (vertexCount < 3 || indexCount < 3 || indexCount % 3 != 0 || xSplits < 0 || zSplits < 0 || xSplits > 255 ||
76 zSplits > 255 || static_cast<int>(pivot) < 0 || static_cast<int>(pivot) > 2)
78 DiagnosticCode::InvalidArgument, "GTS mesh split requires a valid triangle mesh, split counts and pivot",
79 "procgen.gtsMeshSplit"));
80 float minX = std::numeric_limits<float>::max(), maxX = -minX, minZ = minX, maxZ = -minX;
81 for (int i = 0; i < vertexCount; ++i) {
82 Vertex v{source.getPositionX(i), source.getPositionY(i), source.getPositionZ(i), source.getNormalX(i),
83 source.getNormalY(i), source.getNormalZ(i), source.getUvU(i), source.getUvV(i),
84 source.getColor(i, 0), source.getColor(i, 1), source.getColor(i, 2), source.getColor(i, 3)};
85 if (!finiteVertex(v))
87 Diagnostic::error(DiagnosticCode::InvalidArgument, "GTS mesh streams must contain only finite values",
88 "procgen.gtsMeshSplit"));
89 minX = std::min(minX, v.x);
90 maxX = std::max(maxX, v.x);
91 minZ = std::min(minZ, v.z);
92 maxZ = std::max(maxZ, v.z);
93 }
94 for (int i = 0; i < indexCount; ++i)
95 if (source.getIndex(i) < 0 || source.getIndex(i) >= vertexCount)
97 DiagnosticCode::InvalidArgument, "GTS mesh index is out of range", "procgen.gtsMeshSplit"));
98 if (!(maxX > minX) || !(maxZ > minZ))
100 DiagnosticCode::InvalidArgument, "GTS mesh must have non-zero X/Z bounds", "procgen.gtsMeshSplit"));
101 GtsMeshSplitResult result;
102 result.columns_ = xSplits + 1;
103 result.rows_ = zSplits + 1;
104 result.tiles_.resize(static_cast<std::size_t>(result.columns_ * result.rows_));
105 const float cellX = (maxX - minX) / result.columns_, cellZ = (maxZ - minZ) / result.rows_;
106 for (int row = 0; row < result.rows_; ++row)
107 for (int column = 0; column < result.columns_; ++column) {
108 const float loX = minX + column * cellX,
109 hiX = column + 1 == result.columns_ ? maxX : minX + (column + 1) * cellX;
110 const float loZ = minZ + row * cellZ, hiZ = row + 1 == result.rows_ ? maxZ : minZ + (row + 1) * cellZ;
111 auto& tile = result.tiles_[static_cast<std::size_t>(row * result.columns_ + column)];
112 float pivotX = 0.f, pivotZ = 0.f;
113 if (pivot == GtsMeshPivot::MinimumXZ) {
114 pivotX = loX;
115 pivotZ = loZ;
116 } else if (pivot == GtsMeshPivot::CenterXZ) {
117 pivotX = (loX + hiX) * .5f;
118 pivotZ = (loZ + hiZ) * .5f;
119 }
120 tile.offsetX = pivotX;
121 tile.offsetZ = pivotZ;
122 std::vector<float> tileColors;
123 for (int i = 0; i < indexCount; i += 3) {
124 std::vector<Vertex> polygon;
125 polygon.reserve(7);
126 for (int k = 0; k < 3; ++k) {
127 const int n = source.getIndex(i + k);
128 polygon.push_back({source.getPositionX(n), source.getPositionY(n), source.getPositionZ(n),
129 source.getNormalX(n), source.getNormalY(n), source.getNormalZ(n),
130 source.getUvU(n), source.getUvV(n), source.getColor(n, 0), source.getColor(n, 1),
131 source.getColor(n, 2), source.getColor(n, 3)});
132 }
133 polygon = clip(polygon, [&](const Vertex& v) { return v.x - loX; });
134 polygon = clip(polygon, [&](const Vertex& v) { return hiX - v.x; });
135 polygon = clip(polygon, [&](const Vertex& v) { return v.z - loZ; });
136 polygon = clip(polygon, [&](const Vertex& v) { return hiZ - v.z; });
137 if (polygon.size() < 3) continue;
138 const int group = source.getTriangleGroup(i / 3);
139 if (group >= 0)
140 tile.mesh.setActiveGroup(source.getGroupName(group));
141 else
142 tile.mesh.setActiveGroup("");
143 const auto add = [&](const Vertex& v) {
144 const auto n = static_cast<std::uint32_t>(tile.mesh.getVertexCount());
145 tile.mesh.addVertex(v.x - pivotX, v.y, v.z - pivotZ, v.nx, v.ny, v.nz, v.u, v.v);
146 if (source.hasVertexColors()) tileColors.insert(tileColors.end(), {v.r, v.g, v.b, v.a});
147 return n;
148 };
149 for (std::size_t k = 1; k + 1 < polygon.size(); ++k) {
150 if (!nonDegenerate(polygon[0], polygon[k], polygon[k + 1])) continue;
151 tile.mesh.addTriangle(add(polygon[0]), add(polygon[k]), add(polygon[k + 1]));
152 }
153 }
154 if (!tileColors.empty())
155 tile.mesh.setVertexColors(std::move(tileColors)).ignore("validated clipped color stream");
156 }
157 return Result<GtsMeshSplitResult>::success(std::move(result));
158}
160 GtsMeshPivot pivot) {
161 auto result = splitGtsMesh(source, xSplits, zSplits, pivot);
162 if (!result.ok()) return Result<void>::failure(result.status());
163 output = std::move(result).value();
164 return Result<void>::success();
165}
166} // namespace eve::procgen
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 nx
float nz
float ny
glm::vec4 p[6]
int column
std::uint32_t vertexCount
std::uint32_t indexCount
tensor::Graph g
Definition GpuGraph.cpp:7
glm::vec4 clip
float u
Definition Grass.cpp:233
glm::vec3 n
Definition Grass.cpp:63
double r
float v
std::int32_t c
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float distance
graphics::Canvas * previous
float t
double current
uint32_t index
const UnitySourceAsset & source
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
Owned row-major result of a GTS X/Z plane split.
float getTileOffsetZ(int index) const noexcept
Return the Z offset that restores one translated tile to world space.
std::unique_ptr< MeshBuild > copyTileMesh(int index) const
Copy one tile mesh for independent ownership, or null for an invalid index.
float getTileOffsetX(int index) const noexcept
Return the X offset that restores one translated tile to world space.
const GtsMeshSplitTile * tileAt(int index) const noexcept
Borrow one indexed tile. @ownership This result retains ownership; the caller must not delete the poi...
CPU triangle mesh from procedural mesh recipes (e.g. marching cubes). Positions/normals are xyz-packe...
Definition MeshBuild.h:19
Result< GtsMeshSplitResult > splitGtsMesh(const MeshBuild &source, int xSplits, int zSplits, GtsMeshPivot pivot)
Split every source triangle against a regular X/Z grid like GTSMeshSplitter.
Result< void > splitGtsMeshInto(GtsMeshSplitResult &output, const MeshBuild &source, int xSplits, int zSplits, GtsMeshPivot pivot)
Atomically replace an owned result with a GTS mesh split.
GtsMeshPivot
Pivot translation applied independently to each GTS split mesh.
One row-major GTS mesh cell and the world offset restoring its source position.