载入中...
搜索中...
未找到
StaticMeshSurface.cpp
浏览该文件的文档.
2
4#include "grid/GridConfig.h"
5
6#include <algorithm>
7#include <array>
8#include <cmath>
9#include <limits>
10#include <numeric>
11#include <utility>
12
13namespace eve::building {
14namespace {
15
16constexpr float kEpsilon = 1e-6f;
17
18struct Vec3 {
19 float x;
20 float y;
21 float z;
22};
23
24Vec3 subtract(const Vec3 &a, const Vec3 &b) { return {a.x - b.x, a.y - b.y, a.z - b.z}; }
25
26Vec3 cross(const Vec3 &a, const Vec3 &b) {
27 return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z,
28 a.x * b.y - a.y * b.x};
29}
30
31float lengthSquared(const Vec3 &value) {
32 return value.x * value.x + value.y * value.y + value.z * value.z;
33}
34
35template <class T>
36eve::Result<T> meshFailure(eve::DiagnosticCode code, const std::string &message,
37 const std::string &subject = {}) {
39 code, message, subject, {}, "building.static-mesh-surface"));
40}
41
42} // namespace
43
44StaticMeshSurface::StaticMeshSurface(Config config, std::vector<float> vertices,
45 std::vector<uint32_t> indices,
46 std::vector<float> normals)
47 : config_(std::move(config)),
48 vertices_(std::move(vertices)),
49 indices_(std::move(indices)),
50 normals_(std::move(normals)) {
51 const uint32_t triangleCount = static_cast<uint32_t>(indices_.size() / 3);
52 triangleOrder_.resize(triangleCount);
53 std::iota(triangleOrder_.begin(), triangleOrder_.end(), uint32_t{0});
54 triangleBounds_.reserve(triangleCount);
55 for (uint32_t triangle = 0; triangle < triangleCount; ++triangle) {
56 Bounds bounds{std::numeric_limits<float>::max(), std::numeric_limits<float>::max(),
57 std::numeric_limits<float>::max(), std::numeric_limits<float>::lowest(),
58 std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest()};
59 for (uint32_t corner = 0; corner < 3; ++corner) {
60 const size_t vertex = static_cast<size_t>(indices_[triangle * 3 + corner]) * 3;
61 bounds.minX = std::min(bounds.minX, vertices_[vertex]);
62 bounds.minY = std::min(bounds.minY, vertices_[vertex + 1]);
63 bounds.minZ = std::min(bounds.minZ, vertices_[vertex + 2]);
64 bounds.maxX = std::max(bounds.maxX, vertices_[vertex]);
65 bounds.maxY = std::max(bounds.maxY, vertices_[vertex + 1]);
66 bounds.maxZ = std::max(bounds.maxZ, vertices_[vertex + 2]);
67 }
68 triangleBounds_.push_back(bounds);
69 }
70 nodes_.reserve(triangleCount * 2);
71 buildNode(0, triangleCount);
72}
73
75 Config config, std::vector<float> vertices, std::vector<uint32_t> indices,
76 std::vector<float> normals) {
77 if (vertices.size() < 9 || vertices.size() % 3 != 0 || indices.empty() ||
78 indices.size() % 3 != 0) {
79 return meshFailure<std::shared_ptr<const StaticMeshSurface>>(
81 "static mesh requires packed XYZ vertices and complete indexed triangles",
82 config.surfaceId);
83 }
84 if (indices.size() / 3 > std::numeric_limits<uint32_t>::max()) {
85 return meshFailure<std::shared_ptr<const StaticMeshSurface>>(
86 eve::DiagnosticCode::InvalidArgument, "static mesh has too many triangles",
87 config.surfaceId);
88 }
89 if (!normals.empty() && normals.size() != vertices.size()) {
90 return meshFailure<std::shared_ptr<const StaticMeshSurface>>(
92 "static mesh normals must be empty or match packed vertices", config.surfaceId);
93 }
94 if (!std::isfinite(config.referenceHeight) || config.leafTriangleCount == 0 ||
95 config.leafTriangleCount > 64) {
96 return meshFailure<std::shared_ptr<const StaticMeshSurface>>(
98 "static mesh requires a finite reference height and leaf size in [1, 64]",
99 config.surfaceId);
100 }
101 for (float value : vertices) {
102 if (!std::isfinite(value))
103 return meshFailure<std::shared_ptr<const StaticMeshSurface>>(
104 eve::DiagnosticCode::InvalidArgument, "static mesh vertices must be finite",
105 config.surfaceId);
106 }
107 for (float value : normals) {
108 if (!std::isfinite(value))
109 return meshFailure<std::shared_ptr<const StaticMeshSurface>>(
110 eve::DiagnosticCode::InvalidArgument, "static mesh normals must be finite",
111 config.surfaceId);
112 }
113 const size_t vertexCount = vertices.size() / 3;
114 for (size_t triangle = 0; triangle < indices.size() / 3; ++triangle) {
115 std::array<Vec3, 3> points;
116 for (size_t corner = 0; corner < 3; ++corner) {
117 const uint32_t index = indices[triangle * 3 + corner];
118 if (index >= vertexCount)
119 return meshFailure<std::shared_ptr<const StaticMeshSurface>>(
121 "static mesh contains an out-of-range vertex index", config.surfaceId);
122 const size_t offset = static_cast<size_t>(index) * 3;
123 points[corner] = {vertices[offset], vertices[offset + 1], vertices[offset + 2]};
124 }
125 if (lengthSquared(cross(subtract(points[1], points[0]),
126 subtract(points[2], points[0]))) <= kEpsilon * kEpsilon) {
127 return meshFailure<std::shared_ptr<const StaticMeshSurface>>(
129 "static mesh contains a degenerate triangle", config.surfaceId);
130 }
131 }
133 std::shared_ptr<const StaticMeshSurface>(new StaticMeshSurface(
134 std::move(config), std::move(vertices), std::move(indices), std::move(normals))));
135}
136
137uint32_t StaticMeshSurface::buildNode(uint32_t first, uint32_t count) {
138 Bounds bounds{std::numeric_limits<float>::max(), std::numeric_limits<float>::max(),
139 std::numeric_limits<float>::max(), std::numeric_limits<float>::lowest(),
140 std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest()};
141 Bounds centroids = bounds;
142 for (uint32_t i = first; i < first + count; ++i) {
143 const Bounds &triangle = triangleBounds_[triangleOrder_[i]];
144 bounds.minX = std::min(bounds.minX, triangle.minX);
145 bounds.minY = std::min(bounds.minY, triangle.minY);
146 bounds.minZ = std::min(bounds.minZ, triangle.minZ);
147 bounds.maxX = std::max(bounds.maxX, triangle.maxX);
148 bounds.maxY = std::max(bounds.maxY, triangle.maxY);
149 bounds.maxZ = std::max(bounds.maxZ, triangle.maxZ);
150 const float cx = triangle.minX + triangle.maxX;
151 const float cy = triangle.minY + triangle.maxY;
152 const float cz = triangle.minZ + triangle.maxZ;
153 centroids.minX = std::min(centroids.minX, cx);
154 centroids.minY = std::min(centroids.minY, cy);
155 centroids.minZ = std::min(centroids.minZ, cz);
156 centroids.maxX = std::max(centroids.maxX, cx);
157 centroids.maxY = std::max(centroids.maxY, cy);
158 centroids.maxZ = std::max(centroids.maxZ, cz);
159 }
160 const uint32_t nodeIndex = static_cast<uint32_t>(nodes_.size());
161 nodes_.push_back({bounds, first, count, 0, 0, true});
162 if (count <= config_.leafTriangleCount) return nodeIndex;
163
164 const std::array<float, 3> extents{centroids.maxX - centroids.minX,
165 centroids.maxY - centroids.minY,
166 centroids.maxZ - centroids.minZ};
167 const int axis = static_cast<int>(
168 std::distance(extents.begin(), std::max_element(extents.begin(), extents.end())));
169 const auto center = [&](uint32_t triangle) {
170 const Bounds &value = triangleBounds_[triangle];
171 if (axis == 0) return value.minX + value.maxX;
172 if (axis == 1) return value.minY + value.maxY;
173 return value.minZ + value.maxZ;
174 };
175 const uint32_t middle = first + count / 2;
176 std::nth_element(triangleOrder_.begin() + first, triangleOrder_.begin() + middle,
177 triangleOrder_.begin() + first + count,
178 [&](uint32_t a, uint32_t b) {
179 const float ac = center(a);
180 const float bc = center(b);
181 return ac == bc ? a < b : ac < bc;
182 });
183 const uint32_t left = buildNode(first, middle - first);
184 const uint32_t right = buildNode(middle, first + count - middle);
185 nodes_[nodeIndex].leaf = false;
186 nodes_[nodeIndex].left = left;
187 nodes_[nodeIndex].right = right;
188 nodes_[nodeIndex].count = 0;
189 return nodeIndex;
190}
191
193 const PlacementWorld &world, float planeX, float planeY) const {
194 if (!std::isfinite(planeX) || !std::isfinite(planeY))
195 return meshFailure<PlacementSystem::PlacementHit>(
196 eve::DiagnosticCode::InvalidArgument, "mesh sample coordinates must be finite",
197 config_.surfaceId);
198
199 const bool xz = world.getGrid().plane == grid::GridPlane::XZ;
200 bool found = false;
201 float bestHeight = 0.f;
202 float bestDistance = 0.f;
203 uint32_t bestTriangle = 0;
204 float bestA = 0.f;
205 float bestB = 0.f;
206 float bestC = 0.f;
207 std::vector<uint32_t> pending{0};
208 while (!pending.empty()) {
209 const Node &node = nodes_[pending.back()];
210 pending.pop_back();
211 const bool inside = planeX >= node.bounds.minX - kEpsilon &&
212 planeX <= node.bounds.maxX + kEpsilon &&
213 planeY >= (xz ? node.bounds.minZ : node.bounds.minY) - kEpsilon &&
214 planeY <= (xz ? node.bounds.maxZ : node.bounds.maxY) + kEpsilon;
215 if (!inside) continue;
216 if (!node.leaf) {
217 pending.push_back(node.right);
218 pending.push_back(node.left);
219 continue;
220 }
221 for (uint32_t ordered = node.first; ordered < node.first + node.count; ++ordered) {
222 const uint32_t triangle = triangleOrder_[ordered];
223 std::array<Vec3, 3> p;
224 for (uint32_t corner = 0; corner < 3; ++corner) {
225 const size_t offset = static_cast<size_t>(indices_[triangle * 3 + corner]) * 3;
226 p[corner] = {vertices_[offset], vertices_[offset + 1], vertices_[offset + 2]};
227 }
228 const float p0x = p[0].x;
229 const float p0y = xz ? p[0].z : p[0].y;
230 const float p1x = p[1].x;
231 const float p1y = xz ? p[1].z : p[1].y;
232 const float p2x = p[2].x;
233 const float p2y = xz ? p[2].z : p[2].y;
234 const float denominator =
235 (p1y - p2y) * (p0x - p2x) + (p2x - p1x) * (p0y - p2y);
236 if (std::fabs(denominator) <= kEpsilon) continue;
237 const float a = ((p1y - p2y) * (planeX - p2x) +
238 (p2x - p1x) * (planeY - p2y)) /
239 denominator;
240 const float b = ((p2y - p0y) * (planeX - p2x) +
241 (p0x - p2x) * (planeY - p2y)) /
242 denominator;
243 const float c = 1.f - a - b;
244 if (a < -kEpsilon || b < -kEpsilon || c < -kEpsilon) continue;
245 const float height = xz ? a * p[0].y + b * p[1].y + c * p[2].y
246 : a * p[0].z + b * p[1].z + c * p[2].z;
247 const float distance = std::fabs(height - config_.referenceHeight);
248 bool better = !found;
249 if (found && config_.hitSelection == HitSelection::Highest)
250 better = height > bestHeight + kEpsilon;
251 else if (found && config_.hitSelection == HitSelection::Lowest)
252 better = height < bestHeight - kEpsilon;
253 else if (found && config_.hitSelection == HitSelection::ClosestToReference)
254 better = distance < bestDistance - kEpsilon;
255 const bool tied = config_.hitSelection == HitSelection::ClosestToReference
256 ? std::fabs(distance - bestDistance) <= kEpsilon
257 : std::fabs(height - bestHeight) <= kEpsilon;
258 if (!better && found && tied) better = triangle < bestTriangle;
259 if (!better) continue;
260 found = true;
261 bestHeight = height;
262 bestDistance = distance;
263 bestTriangle = triangle;
264 bestA = a;
265 bestB = b;
266 bestC = c;
267 }
268 }
269 if (!found)
270 return meshFailure<PlacementSystem::PlacementHit>(
272 "no projectable mesh triangle exists at the plane coordinate", config_.surfaceId);
273
274 std::array<Vec3, 3> p;
275 std::array<Vec3, 3> n;
276 for (uint32_t corner = 0; corner < 3; ++corner) {
277 const size_t offset = static_cast<size_t>(indices_[bestTriangle * 3 + corner]) * 3;
278 p[corner] = {vertices_[offset], vertices_[offset + 1], vertices_[offset + 2]};
279 if (!normals_.empty())
280 n[corner] = {normals_[offset], normals_[offset + 1], normals_[offset + 2]};
281 }
282 Vec3 normal = normals_.empty()
283 ? cross(subtract(p[1], p[0]), subtract(p[2], p[0]))
284 : Vec3{bestA * n[0].x + bestB * n[1].x + bestC * n[2].x,
285 bestA * n[0].y + bestB * n[1].y + bestC * n[2].y,
286 bestA * n[0].z + bestB * n[1].z + bestC * n[2].z};
287 if (lengthSquared(normal) <= kEpsilon * kEpsilon)
288 normal = cross(subtract(p[1], p[0]), subtract(p[2], p[0]));
289 if (config_.orientNormalsToGridUp && (xz ? normal.y : normal.z) < 0.f) {
290 normal.x = -normal.x;
291 normal.y = -normal.y;
292 normal.z = -normal.z;
293 }
294 Vec3 tangent = subtract(p[1], p[0]);
295 if (lengthSquared(tangent) <= kEpsilon * kEpsilon) tangent = subtract(p[2], p[0]);
296
298 hit.worldX = planeX;
299 hit.worldY = xz ? bestHeight : planeY;
300 hit.worldZ = xz ? planeY : bestHeight;
301 hit.normalX = normal.x;
302 hit.normalY = normal.y;
303 hit.normalZ = normal.z;
304 hit.tangentX = tangent.x;
305 hit.tangentY = tangent.y;
306 hit.tangentZ = tangent.z;
307 hit.surfaceId = config_.surfaceId;
308 hit.surfaceRevision = config_.surfaceRevision;
309 hit.primitiveId = bestTriangle;
310 hit.tags = config_.tags;
312}
313
314} // namespace eve::building
double value
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
int subject
Definition AnimSmr.cpp:163
std::vector< QuestEvent > pending
float cx
Definition CardTypes.cpp:33
float cy
Definition CardTypes.cpp:34
Vec3 tangent
Definition CaveMesh.cpp:80
glm::vec4 p[6]
int triangle
std::string message
DiagnosticCode code
std::uint32_t vertexCount
glm::vec3 n
Definition Grass.cpp:63
Module-neutral grid topology and sizing (no building/map dependency).
std::vector< std::uint32_t > indices
std::vector< float > normals
HexVec3 left
HexVec3 right
std::int32_t c
std::int32_t first
std::uint32_t height
size_t offset
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float distance
Texture * normal
World3D * world
std::vector< Point > vertices
std::shared_ptr< const std::vector< glm::vec2 > > points
bool hit
const RoadNode * node
bool found
std::uint32_t count
uint32_t index
std::vector< char > inside
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
格子型建筑放置世界(脚本可直接操作)。
Immutable world-space triangle mesh accelerated for placement-surface projection.
建筑放置模块入口:定义 / 放置世界 / 鬼影 / 变更事件的脚本绑定点。 设计文档:docs/dev/建筑放置系统设计.md
Definition Building.cpp:7
float lengthSquared(Vec3 value)
Length squared.
double cross(const Vec2 &a, const Vec2 &b)
Cross.
Definition UrbanTypes.h:36
int axis(int64_t a, size_t rank)
Axis.
DiagnosticCode
Stable machine-readable diagnostic codes.
Definition Diagnostic.h:47
One sampled point and orthonormal frame on a placement surface.
Immutable identity, hit policy and metadata for one static mesh snapshot.
glm::vec4 bounds