27 Config config, std::vector<float> samples) {
29 return heightfieldFailure<std::shared_ptr<const HeightfieldSurface>>(
31 "heightfield dimensions must both be at least two", config.
surfaceId);
33 if (!std::isfinite(config.
originX) || !std::isfinite(config.
originY) ||
37 return heightfieldFailure<std::shared_ptr<const HeightfieldSurface>>(
39 "heightfield transform requires finite values and positive spacing",
42 const size_t width =
static_cast<size_t>(config.
width);
43 const size_t height =
static_cast<size_t>(config.
height);
45 return heightfieldFailure<std::shared_ptr<const HeightfieldSurface>>(
47 "heightfield sample count must equal width times height", config.
surfaceId);
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);
57 std::shared_ptr<const HeightfieldSurface>(
63 if (!std::isfinite(planeX) || !std::isfinite(planeY)) {
64 return heightfieldFailure<PlacementSystem::PlacementHit>(
66 "heightfield sample coordinates must be finite", config_.surfaceId);
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);
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)];
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 +
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);
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);
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;
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));
117 if (
world.getGrid().plane == grid::GridPlane::XZ) {
119 hit.worldY = sampledHeight;
130 hit.worldZ = sampledHeight;
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;
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.