载入中...
搜索中...
未找到
HeightfieldSurface.cpp
浏览该文件的文档.
2
4#include "grid/GridConfig.h"
5
6#include <algorithm>
7#include <cmath>
8#include <limits>
9#include <utility>
10
11namespace eve::building {
12namespace {
13
14template <class T>
15eve::Result<T> heightfieldFailure(eve::DiagnosticCode code, const std::string &message,
16 const std::string &subject = {}) {
18 code, message, subject, {}, "building.heightfield-surface"));
19}
20
21} // namespace
22
23HeightfieldSurface::HeightfieldSurface(Config config, std::vector<float> samples)
24 : config_(std::move(config)), samples_(std::move(samples)) {}
25
27 Config config, std::vector<float> samples) {
28 if (config.width < 2 || config.height < 2) {
29 return heightfieldFailure<std::shared_ptr<const HeightfieldSurface>>(
31 "heightfield dimensions must both be at least two", config.surfaceId);
32 }
33 if (!std::isfinite(config.originX) || !std::isfinite(config.originY) ||
34 !std::isfinite(config.spacingX) || !std::isfinite(config.spacingY) ||
35 !std::isfinite(config.heightScale) || !std::isfinite(config.heightOffset) ||
36 config.spacingX <= 0.f || config.spacingY <= 0.f) {
37 return heightfieldFailure<std::shared_ptr<const HeightfieldSurface>>(
39 "heightfield transform requires finite values and positive spacing",
40 config.surfaceId);
41 }
42 const size_t width = static_cast<size_t>(config.width);
43 const size_t height = static_cast<size_t>(config.height);
44 if (width > std::numeric_limits<size_t>::max() / height || samples.size() != width * height) {
45 return heightfieldFailure<std::shared_ptr<const HeightfieldSurface>>(
47 "heightfield sample count must equal width times height", config.surfaceId);
48 }
49 for (float sample : samples) {
50 if (!std::isfinite(sample)) {
51 return heightfieldFailure<std::shared_ptr<const HeightfieldSurface>>(
53 "heightfield samples must all be finite", config.surfaceId);
54 }
55 }
57 std::shared_ptr<const HeightfieldSurface>(
58 new HeightfieldSurface(std::move(config), std::move(samples))));
59}
60
62 const PlacementWorld &world, float planeX, float planeY) const {
63 if (!std::isfinite(planeX) || !std::isfinite(planeY)) {
64 return heightfieldFailure<PlacementSystem::PlacementHit>(
66 "heightfield sample coordinates must be finite", config_.surfaceId);
67 }
68 const float u = (planeX - config_.originX) / config_.spacingX;
69 const float v = (planeY - config_.originY) / config_.spacingY;
70 const float maxU = static_cast<float>(config_.width - 1);
71 const float maxV = static_cast<float>(config_.height - 1);
72 if (u < 0.f || v < 0.f || u > maxU || v > maxV) {
73 return heightfieldFailure<PlacementSystem::PlacementHit>(
75 "heightfield sample coordinate is outside its finite extent", config_.surfaceId);
76 }
77
78 const int x0 = std::min(static_cast<int>(std::floor(u)), config_.width - 2);
79 const int y0 = std::min(static_cast<int>(std::floor(v)), config_.height - 2);
80 const float tx = u - static_cast<float>(x0);
81 const float ty = v - static_cast<float>(y0);
82 const auto at = [&](int x, int y) {
83 return samples_[static_cast<size_t>(y) * static_cast<size_t>(config_.width) +
84 static_cast<size_t>(x)];
85 };
86 const float h00 = at(x0, y0);
87 const float h10 = at(x0 + 1, y0);
88 const float h01 = at(x0, y0 + 1);
89 const float h11 = at(x0 + 1, y0 + 1);
90 const float h0 = h00 + (h10 - h00) * tx;
91 const float h1 = h01 + (h11 - h01) * tx;
92 const float sampledHeight = (h0 + (h1 - h0) * ty) * config_.heightScale +
93 config_.heightOffset;
94 const auto gradientX = [&](int x, int y) {
95 if (x == 0) return (at(1, y) - at(0, y)) / config_.spacingX;
96 if (x == config_.width - 1)
97 return (at(x, y) - at(x - 1, y)) / config_.spacingX;
98 return (at(x + 1, y) - at(x - 1, y)) / (2.f * config_.spacingX);
99 };
100 const auto gradientY = [&](int x, int y) {
101 if (y == 0) return (at(x, 1) - at(x, 0)) / config_.spacingY;
102 if (y == config_.height - 1)
103 return (at(x, y) - at(x, y - 1)) / config_.spacingY;
104 return (at(x, y + 1) - at(x, y - 1)) / (2.f * config_.spacingY);
105 };
106 const auto bilinear = [&](float g00, float g10, float g01, float g11) {
107 const float g0 = g00 + (g10 - g00) * tx;
108 const float g1 = g01 + (g11 - g01) * tx;
109 return (g0 + (g1 - g0) * ty) * config_.heightScale;
110 };
111 const float dx = bilinear(gradientX(x0, y0), gradientX(x0 + 1, y0),
112 gradientX(x0, y0 + 1), gradientX(x0 + 1, y0 + 1));
113 const float dy = bilinear(gradientY(x0, y0), gradientY(x0 + 1, y0),
114 gradientY(x0, y0 + 1), gradientY(x0 + 1, y0 + 1));
115
117 if (world.getGrid().plane == grid::GridPlane::XZ) {
118 hit.worldX = planeX;
119 hit.worldY = sampledHeight;
120 hit.worldZ = planeY;
121 hit.normalX = -dx;
122 hit.normalY = 1.f;
123 hit.normalZ = -dy;
124 hit.tangentX = 1.f;
125 hit.tangentY = dx;
126 hit.tangentZ = 0.f;
127 } else {
128 hit.worldX = planeX;
129 hit.worldY = planeY;
130 hit.worldZ = sampledHeight;
131 hit.normalX = -dx;
132 hit.normalY = -dy;
133 hit.normalZ = 1.f;
134 hit.tangentX = 1.f;
135 hit.tangentY = 0.f;
136 hit.tangentZ = dx;
137 }
138 hit.surfaceId = config_.surfaceId;
139 hit.surfaceRevision = config_.surfaceRevision;
140 hit.primitiveId = static_cast<uint64_t>(y0) * static_cast<uint64_t>(config_.width - 1) +
141 static_cast<uint64_t>(x0);
142 hit.tags = config_.tags;
144}
145
146} // namespace eve::building
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
int subject
Definition AnimSmr.cpp:163
std::string message
DiagnosticCode code
float u
Definition Grass.cpp:233
Module-neutral grid topology and sizing (no building/map dependency).
float v
std::uint32_t height
std::uint32_t width
World3D * world
bool hit
float dy
float dx
std::size_t at
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 regularly sampled heightfield usable as an XY or XZ placement surface.
格子型建筑放置世界(脚本可直接操作)。
建筑放置模块入口:定义 / 放置世界 / 鬼影 / 变更事件的脚本绑定点。 设计文档:docs/dev/建筑放置系统设计.md
Definition Building.cpp:7
DiagnosticCode
Stable machine-readable diagnostic codes.
Definition Diagnostic.h:47
Owning construction data. Samples are row-major plane-axis heights.
One sampled point and orthonormal frame on a placement surface.