载入中...
搜索中...
未找到
TerrainMaterialAtlas.cpp
浏览该文件的文档.
2
4#include "graphics/Shader.h"
5
6#include <algorithm>
7#include <bit>
8#include <cmath>
9
10namespace eve::asset_procgen {
11namespace {
12TerrainAtlasImage solid(std::array<std::uint8_t, 4> color) { return {1, 1, {color[0], color[1], color[2], color[3]}}; }
13
14std::array<std::uint8_t, 4> sample(const asset::DecodedEvpackImage& image, std::uint32_t x, std::uint32_t y,
15 std::uint32_t width, std::uint32_t height) {
16 const float sx = (float(x) + .5f) * float(image.width) / float(width) - .5f;
17 const float sy = (float(y) + .5f) * float(image.height) / float(height) - .5f;
18 const int x0 = std::clamp(int(std::floor(sx)), 0, int(image.width) - 1);
19 const int y0 = std::clamp(int(std::floor(sy)), 0, int(image.height) - 1);
20 const int x1 = std::min(x0 + 1, int(image.width) - 1);
21 const int y1 = std::min(y0 + 1, int(image.height) - 1);
22 const float fx = std::clamp(sx - std::floor(sx), 0.f, 1.f);
23 const float fy = std::clamp(sy - std::floor(sy), 0.f, 1.f);
24 std::array<std::uint8_t, 4> result{};
25 for (std::size_t channel = 0; channel < 4; ++channel) {
26 auto at = [&](int px, int py) {
27 return float(image.pixels[(std::size_t(py) * image.width + px) * 4 + channel]);
28 };
29 const float top = std::lerp(at(x0, y0), at(x1, y0), fx);
30 const float bottom = std::lerp(at(x0, y1), at(x1, y1), fx);
31 result[channel] = std::uint8_t(std::clamp(std::lround(std::lerp(top, bottom, fy)), 0l, 255l));
32 }
33 return result;
34}
35
36void place(TerrainAtlasImage& atlas, const asset::DecodedEvpackImage* source, std::uint32_t slot,
37 std::array<std::uint8_t, 4> fallback, const RuntimeTerrainLayer* layer, bool normal, bool mask) {
38 const std::uint32_t tileWidth = atlas.width / 2, tileHeight = atlas.height / 2;
39 const std::uint32_t ox = (slot & 1u) * tileWidth, oy = (slot >> 1u) * tileHeight;
40 for (std::uint32_t y = 0; y < tileHeight; ++y) {
41 for (std::uint32_t x = 0; x < tileWidth; ++x) {
42 auto value = source ? sample(*source, x, y, tileWidth, tileHeight) : fallback;
43 if (normal && layer && layer->normalConvention == "directx") value[1] = std::uint8_t(255 - value[1]);
44 if (mask && layer) {
45 for (std::size_t channel = 0; channel < 4; ++channel) {
46 const float unit = float(value[channel]) / 255.f;
47 const float remapped =
48 std::lerp(layer->maskRemapMinimum[channel], layer->maskRemapMaximum[channel], unit);
49 value[channel] = std::uint8_t(std::clamp(std::lround(remapped * 255.f), 0l, 255l));
50 }
51 }
52 const std::size_t destination = (std::size_t(oy + y) * atlas.width + ox + x) * 4;
53 std::copy(value.begin(), value.end(), atlas.pixels.begin() + std::ptrdiff_t(destination));
54 }
55 }
56}
57
58Result<std::optional<asset::DecodedEvpackImage>> decodeOptional(const asset::EvpackResourceReader& reader,
59 const std::optional<AssetRef>& reference,
60 const asset::EvpackCapabilities& capabilities,
61 const asset::EvpackImageDecodeLimits& limits) {
62 if (!reference) return Result<std::optional<asset::DecodedEvpackImage>>::success(std::nullopt);
63 auto decoded = asset::decodeEvpackImage(reader, *reference, capabilities, limits);
64 if (!decoded) return Result<std::optional<asset::DecodedEvpackImage>>::failure(decoded.status());
65 return Result<std::optional<asset::DecodedEvpackImage>>::success(std::move(decoded).takeValue());
66}
67
68std::array<std::uint8_t, 4> sampleImage(const TerrainAtlasImage& image, float u, float v) {
69 const auto x = std::min(std::uint32_t(u * float(image.width)), image.width - 1);
70 const auto y = std::min(std::uint32_t(v * float(image.height)), image.height - 1);
71 const auto offset = (std::size_t(y) * image.width + x) * 4;
72 return {image.pixels[offset], image.pixels[offset + 1], image.pixels[offset + 2], image.pixels[offset + 3]};
73}
74
75void writePixel(TerrainAtlasImage& image, std::uint32_t x, std::uint32_t y, const std::array<std::uint8_t, 4>& value) {
76 const auto offset = (std::size_t(y) * image.width + x) * 4;
77 std::copy(value.begin(), value.end(), image.pixels.begin() + std::ptrdiff_t(offset));
78}
79
80void writeFloat(TerrainAtlasImage& image, std::uint32_t x, std::uint32_t y, float value) {
81 const auto bits = std::bit_cast<std::uint32_t>(value);
82 writePixel(image, x, y,
83 {std::uint8_t(bits), std::uint8_t(bits >> 8), std::uint8_t(bits >> 16), std::uint8_t(bits >> 24)});
84}
85} // namespace
86
89 const asset::EvpackCapabilities& capabilities,
91 if (material.layers.empty() || material.layers.size() > limits.maximumLayers || limits.maximumLayers > 16)
93 "terrain atlas layer count is outside [1,16]",
94 {}, {}, "asset.procgen.terrain-atlas"));
96 auto holes = decodeOptional(reader, material.holesAsset, capabilities, limits.image);
97 if (!holes) return Result<TerrainMaterialAtlases>::failure(holes.status());
98 if (holes.value()) {
99 const auto& image = *holes.value();
100 result.holes = {image.width, image.height,
101 std::vector<std::uint8_t>(
102 image.pixels.begin(),
103 image.pixels.begin() + std::ptrdiff_t(std::uint64_t(image.width) * image.height * 4))};
104 } else {
105 result.holes = solid({255, 255, 255, 255});
106 }
107 const std::size_t groupCount = (material.layers.size() + 3) / 4;
108 result.groups.reserve(groupCount);
109 std::uint64_t outputBytes = result.holes.pixels.size();
110 for (std::size_t groupIndex = 0; groupIndex < groupCount; ++groupIndex) {
111 struct Images {
112 std::optional<asset::DecodedEvpackImage> albedo, normal, mask;
113 } images[4];
114 const std::size_t first = groupIndex * 4;
115 const std::size_t count = std::min<std::size_t>(4, material.layers.size() - first);
116 std::uint32_t tileWidth = 1, tileHeight = 1;
117 for (std::size_t slot = 0; slot < count; ++slot) {
118 const auto& layer = material.layers[first + slot];
119 auto albedo = decodeOptional(reader, layer.diffuseAsset, capabilities, limits.image);
120 auto normal = decodeOptional(reader, layer.normalAsset, capabilities, limits.image);
121 auto mask = decodeOptional(reader, layer.maskAsset, capabilities, limits.image);
122 if (!albedo || !normal || !mask)
124 : !normal ? normal.status()
125 : mask.status());
126 images[slot] = {std::move(albedo).takeValue(), std::move(normal).takeValue(), std::move(mask).takeValue()};
127 for (const auto* image : {&images[slot].albedo, &images[slot].normal, &images[slot].mask}) {
128 if (!*image) continue;
129 tileWidth = std::max(tileWidth, (*image)->width);
130 tileHeight = std::max(tileHeight, (*image)->height);
131 }
132 }
133 if (tileWidth > limits.maximumTileDimension || tileHeight > limits.maximumTileDimension)
135 Diagnostic::error(DiagnosticCode::InvalidArgument, "terrain atlas tile exceeds dimension budget",
136 std::to_string(groupIndex), {}, "asset.procgen.terrain-atlas"));
137 const std::uint64_t atlasBytes = std::uint64_t(tileWidth) * tileHeight * 16;
138 if (atlasBytes > limits.maximumOutputBytes || outputBytes > limits.maximumOutputBytes - atlasBytes * 3)
140 "terrain atlas output exceeds byte budget",
141 {}, {}, "asset.procgen.terrain-atlas"));
143 group.firstLayer = std::uint32_t(first);
144 group.layerCount = std::uint32_t(count);
145 group.tileWidth = tileWidth;
146 group.tileHeight = tileHeight;
147 for (auto* atlas : {&group.albedo, &group.normal, &group.mask}) {
148 atlas->width = tileWidth * 2;
149 atlas->height = tileHeight * 2;
150 atlas->pixels.resize(std::size_t(atlas->width) * atlas->height * 4);
151 }
152 for (std::size_t slot = 0; slot < 4; ++slot) {
153 const RuntimeTerrainLayer* layer = slot < count ? &material.layers[first + slot] : nullptr;
154 place(group.albedo, images[slot].albedo ? &*images[slot].albedo : nullptr, std::uint32_t(slot),
155 {255, 255, 255, 255}, layer, false, false);
156 place(group.normal, images[slot].normal ? &*images[slot].normal : nullptr, std::uint32_t(slot),
157 {128, 128, 255, 255}, layer, true, false);
158 place(group.mask, images[slot].mask ? &*images[slot].mask : nullptr, std::uint32_t(slot),
159 {255, 255, 255, 255}, layer, false, true);
160 }
161 auto control = decodeOptional(reader, material.controlAssets[groupIndex], capabilities, limits.image);
162 if (!control) return Result<TerrainMaterialAtlases>::failure(control.status());
163 if (control.value()) {
164 const auto& image = *control.value();
165 group.control = {image.width, image.height,
166 std::vector<std::uint8_t>(
167 image.pixels.begin(),
168 image.pixels.begin() + std::ptrdiff_t(std::uint64_t(image.width) * image.height * 4))};
169 } else if (groupIndex == 0) {
170 group.control = solid({255, 0, 0, 0});
171 } else {
173 Diagnostic::error(DiagnosticCode::NotFound, "terrain layer group has no control image",
174 std::to_string(groupIndex), {}, "asset.procgen.terrain-atlas"));
175 }
176 outputBytes += atlasBytes * 3 + group.control.pixels.size();
177 if (outputBytes > limits.maximumOutputBytes)
179 "terrain atlas output exceeds byte budget",
180 {}, {}, "asset.procgen.terrain-atlas"));
181 result.groups.push_back(std::move(group));
182 }
183 return Result<TerrainMaterialAtlases>::success(std::move(result));
184}
185
189 if (material.layers.empty() || material.layers.size() > 16 || material.layers.size() > limits.maximumLayers ||
190 atlases.groups.size() != (material.layers.size() + 3) / 4 || atlases.holes.width == 0 ||
191 atlases.holes.height == 0 ||
192 atlases.holes.pixels.size() != std::size_t(atlases.holes.width) * atlases.holes.height * 4)
194 DiagnosticCode::InvalidArgument, "terrain atlas set is incomplete", {}, {}, "asset.procgen.terrain-atlas"));
195 std::uint32_t tileWidth = 1, tileHeight = 1, controlWidth = atlases.holes.width,
196 controlHeight = atlases.holes.height;
197 for (const auto& group : atlases.groups) {
198 const auto groupIndex = std::size_t(&group - atlases.groups.data());
199 const auto expectedLayers = std::min<std::size_t>(4, material.layers.size() - groupIndex * 4);
200 if (group.tileWidth == 0 || group.tileHeight == 0 || group.control.width == 0 || group.control.height == 0 ||
201 group.firstLayer != groupIndex * 4 || group.layerCount != expectedLayers ||
202 group.control.pixels.size() != std::size_t(group.control.width) * group.control.height * 4)
204 Diagnostic::error(DiagnosticCode::InvalidArgument, "terrain atlas group is incomplete", {}, {},
205 "asset.procgen.terrain-atlas"));
206 for (const auto* image : {&group.albedo, &group.normal, &group.mask})
207 if (image->width != group.tileWidth * 2 || image->height != group.tileHeight * 2 ||
208 image->pixels.size() != std::size_t(image->width) * image->height * 4)
210 Diagnostic::error(DiagnosticCode::InvalidArgument, "terrain layer atlas dimensions are invalid", {},
211 {}, "asset.procgen.terrain-atlas"));
212 tileWidth = std::max(tileWidth, group.tileWidth);
213 tileHeight = std::max(tileHeight, group.tileHeight);
214 controlWidth = std::max(controlWidth, group.control.width);
215 controlHeight = std::max(controlHeight, group.control.height);
216 }
217 const std::uint64_t outputBytes = std::uint64_t(tileWidth) * tileHeight * 4 * 4 * 4 * 3 +
218 std::uint64_t(controlWidth) * controlHeight * 3 * 2 * 4 + 7 * 16 * 4;
219 if (tileWidth > limits.maximumTileDimension || tileHeight > limits.maximumTileDimension ||
220 controlWidth > limits.maximumTileDimension || controlHeight > limits.maximumTileDimension ||
221 outputBytes > limits.maximumOutputBytes)
223 Diagnostic::error(DiagnosticCode::InvalidArgument, "packed terrain atlas exceeds output limits", {}, {},
224 "asset.procgen.terrain-atlas"));
226 result.layerCount = std::uint32_t(material.layers.size());
227 result.hasHoles = material.holesAsset.has_value();
228 for (auto* image : {&result.albedo, &result.normal, &result.mask}) {
229 image->width = tileWidth * 4;
230 image->height = tileHeight * 4;
231 image->pixels.resize(std::size_t(image->width) * image->height * 4);
232 }
233 for (std::uint32_t layer = 0; layer < result.layerCount; ++layer) {
234 const auto& group = atlases.groups[layer / 4];
235 const auto sourceSlot = layer % 4;
236 for (std::uint32_t y = 0; y < tileHeight; ++y) {
237 for (std::uint32_t x = 0; x < tileWidth; ++x) {
238 const float sourceU = (float(sourceSlot & 1u) + (float(x) + .5f) / tileWidth) * .5f;
239 const float sourceV = (float(sourceSlot >> 1u) + (float(y) + .5f) / tileHeight) * .5f;
240 const auto destinationX = (layer & 3u) * tileWidth + x;
241 const auto destinationY = (layer >> 2u) * tileHeight + y;
242 writePixel(result.albedo, destinationX, destinationY, sampleImage(group.albedo, sourceU, sourceV));
243 writePixel(result.normal, destinationX, destinationY, sampleImage(group.normal, sourceU, sourceV));
244 writePixel(result.mask, destinationX, destinationY, sampleImage(group.mask, sourceU, sourceV));
245 }
246 }
247 }
248 result.controls = {controlWidth * 3, controlHeight * 2,
249 std::vector<std::uint8_t>(std::size_t(controlWidth) * controlHeight * 3 * 2 * 4)};
250 for (std::uint32_t cell = 0; cell < 5; ++cell) {
251 const TerrainAtlasImage& source = cell < atlases.groups.size() ? atlases.groups[cell].control : atlases.holes;
252 for (std::uint32_t y = 0; y < controlHeight; ++y)
253 for (std::uint32_t x = 0; x < controlWidth; ++x)
254 writePixel(result.controls, (cell % 3) * controlWidth + x, (cell / 3) * controlHeight + y,
255 sampleImage(source, (float(x) + .5f) / controlWidth, (float(y) + .5f) / controlHeight));
256 }
257 result.parameters = {7, 16, std::vector<std::uint8_t>(7 * 16 * 4)};
258 for (std::uint32_t layer = 0; layer < result.layerCount; ++layer) {
259 const auto& source = material.layers[layer];
260 if (!std::isfinite(source.tileScaleMeters[0]) || !std::isfinite(source.tileScaleMeters[1]) ||
261 source.tileScaleMeters[0] == 0.f || source.tileScaleMeters[1] == 0.f ||
262 !std::isfinite(source.tileOffsetMeters[0]) || !std::isfinite(source.tileOffsetMeters[1]) ||
263 !std::isfinite(source.metallic) || !std::isfinite(source.normalScale) || !std::isfinite(source.smoothness))
265 Diagnostic::error(DiagnosticCode::InvalidArgument, "terrain layer parameters are invalid",
266 std::to_string(layer), {}, "asset.procgen.terrain-atlas"));
267 const std::array<float, 7> values{1.f / source.tileScaleMeters[0],
268 1.f / source.tileScaleMeters[1],
269 source.tileOffsetMeters[0] / source.tileScaleMeters[0],
270 source.tileOffsetMeters[1] / source.tileScaleMeters[1],
271 source.metallic,
272 source.normalScale,
273 source.smoothness};
274 for (std::uint32_t parameter = 0; parameter < values.size(); ++parameter)
275 writeFloat(result.parameters, parameter, layer, values[parameter]);
276 }
277 return Result<PackedTerrainMaterialAtlases>::success(std::move(result));
278}
279
281 bool released = true;
282 for (auto& group : set.groups) {
283 for (auto* handle : {&group.albedo, &group.normal, &group.mask, &group.control}) {
284 if (*handle && !factory.releaseTexture(*handle)) released = false;
285 *handle = nullptr;
286 }
287 }
288 if (set.holes && !factory.releaseTexture(set.holes)) released = false;
289 set.holes = nullptr;
290 set.groups.clear();
291 if (!released)
292 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Failed, "terrain atlas backend release failed",
293 {}, {}, "asset.procgen.terrain-atlas"));
294 return Result<void>::success();
295}
296
298 const TerrainMaterialAtlases& atlases) {
300 auto upload = [&](const TerrainAtlasImage& image) {
301 return factory.newTexture(int(image.width), int(image.height), image.pixels.data(), false, false);
302 };
303 result.holes = upload(atlases.holes);
304 if (!result.holes)
306 DiagnosticCode::Failed, "terrain holes upload failed", {}, {}, "asset.procgen.terrain-atlas"));
307 result.groups.reserve(atlases.groups.size());
308 for (const auto& source : atlases.groups) {
310 group.albedo = upload(source.albedo);
311 group.normal = upload(source.normal);
312 group.mask = upload(source.mask);
313 group.control = upload(source.control);
314 result.groups.push_back(group);
315 if (!group.albedo || !group.normal || !group.mask || !group.control) {
316 auto cleanup = releaseTerrainMaterialAtlases(factory, result);
317 if (!cleanup) return Result<TerrainMaterialGpuSet>::failure(cleanup.status());
319 DiagnosticCode::Failed, "terrain atlas upload failed", {}, {}, "asset.procgen.terrain-atlas"));
320 }
321 }
322 return Result<TerrainMaterialGpuSet>::success(std::move(result));
323}
324
327 bool released = true;
328 for (auto* handle : {&set.albedo, &set.normal, &set.mask, &set.controls, &set.parameters}) {
329 if (*handle && !factory.releaseTexture(*handle)) released = false;
330 *handle = nullptr;
331 }
332 set.layerCount = 0;
333 set.hasHoles = false;
334 if (!released)
335 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Failed, "packed terrain backend release failed",
336 {}, {}, "asset.procgen.terrain-atlas"));
337 return Result<void>::success();
338}
339
341 const PackedTerrainMaterialAtlases& atlases) {
343 auto upload = [&](const TerrainAtlasImage& image) {
344 return factory.newTexture(int(image.width), int(image.height), image.pixels.data(), false, false);
345 };
346 result.albedo = upload(atlases.albedo);
347 result.normal = upload(atlases.normal);
348 result.mask = upload(atlases.mask);
349 result.controls = upload(atlases.controls);
350 result.parameters = upload(atlases.parameters);
351 result.layerCount = atlases.layerCount;
352 result.hasHoles = atlases.hasHoles;
353 if (!result.albedo || !result.normal || !result.mask || !result.controls || !result.parameters) {
354 auto cleanup = releasePackedTerrainMaterialAtlases(factory, result);
355 if (!cleanup) return Result<PackedTerrainMaterialGpuSet>::failure(cleanup.status());
357 DiagnosticCode::Failed, "packed terrain atlas upload failed", {}, {}, "asset.procgen.terrain-atlas"));
358 }
359 return Result<PackedTerrainMaterialGpuSet>::success(std::move(result));
360}
361
363 if (!gpu.albedo || !gpu.normal || !gpu.mask || !gpu.controls || !gpu.parameters || gpu.layerCount == 0 ||
364 gpu.layerCount > 16)
366 "packed terrain GPU set is incomplete", {}, {},
367 "asset.procgen.terrain-atlas"));
368 for (const auto& [slot, texture] : {std::pair<std::size_t, graphics::Texture*>{0, gpu.albedo},
369 {1, gpu.normal},
370 {2, gpu.mask},
371 {3, gpu.parameters}}) {
372 auto bound = shader.setMeshTexture(slot, texture);
373 if (!bound) return bound;
374 }
375 shader.sendVec4("terrainFeatures", 2.f, gpu.hasHoles ? 1.f : 0.f, float(gpu.layerCount), 0.f);
376 return Result<void>::success();
377}
378
380 const LoadedTerrainMaterial& material, std::size_t groupIndex) {
381 if (groupIndex >= gpu.groups.size() || groupIndex * 4 >= material.layers.size() || !gpu.holes)
383 "terrain atlas group index is invalid", {}, {},
384 "asset.procgen.terrain-atlas"));
385 const auto& group = gpu.groups[groupIndex];
386 for (const auto& [slot, texture] : {std::pair<std::size_t, graphics::Texture*>{0, group.albedo},
387 {1, group.normal},
388 {2, group.mask},
389 {3, gpu.holes}}) {
390 auto bound = shader.setMeshTexture(slot, texture);
391 if (!bound) return bound;
392 }
393 std::array<float, 4> metallic{}, normalScale{1, 1, 1, 1}, smoothness{};
394 for (std::size_t slot = 0; slot < 4; ++slot) {
395 const std::size_t layerIndex = groupIndex * 4 + slot;
396 std::array<float, 4> st{};
397 if (layerIndex < material.layers.size()) {
398 const auto& layer = material.layers[layerIndex];
399 st = {1.f / layer.tileScaleMeters[0], 1.f / layer.tileScaleMeters[1],
400 layer.tileOffsetMeters[0] / layer.tileScaleMeters[0],
401 layer.tileOffsetMeters[1] / layer.tileScaleMeters[1]};
402 metallic[slot] = layer.metallic;
403 normalScale[slot] = layer.normalScale;
404 smoothness[slot] = layer.smoothness;
405 }
406 shader.sendVec4("terrainLayer" + std::to_string(slot) + "ST", st[0], st[1], st[2], st[3]);
407 }
408 shader.sendVec4("terrainMetallic", metallic[0], metallic[1], metallic[2], metallic[3]);
409 shader.sendVec4("terrainNormalScale", normalScale[0], normalScale[1], normalScale[2], normalScale[3]);
410 shader.sendVec4("terrainSmoothness", smoothness[0], smoothness[1], smoothness[2], smoothness[3]);
411 shader.sendVec4("terrainFeatures", 1.f, material.holesAsset ? 1.f : 0.f, float(groupIndex),
412 float(std::min<std::size_t>(4, material.layers.size() - groupIndex * 4)));
413 return Result<void>::success();
414}
415} // namespace eve::asset_procgen
double value
SQInteger top
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
AuthorityStoreHandleRef reference
Definition Authority.cpp:24
int mask
building::EdgeCurveGroup group
float py
graphics::Texture * albedo
std::map< std::string, Var > values
vk::UniqueImage image
float u
Definition Grass.cpp:233
float v
std::int32_t first
std::vector< Colorf > px
std::uint32_t height
std::uint32_t width
size_t offset
Texture * normal
float metallic
TileLayer * layer
PrimitiveHandle handle
Shader * shader
Material * material
std::uint32_t count
Cell cell
TacticalUnit * unit
CPU assembly of grouped terrain material atlases.
const UnitySourceAsset & source
const AssetImportLimits & limits
std::size_t at
double oy
double ox
float bottom
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
T takeValue() &&
Move the value out and remove it from this Result.
Definition Result.h:339
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
Immutable reader retaining an admitted pack by shared ownership.
Texture / mesh / shader / canvas creation and release.
virtual Texture * newTexture(int width, int height, const uint8_t *rgba, bool repeatU=false, bool repeatV=false)=0
Creates a texture. @ownership Caller deletes unless documented otherwise.
virtual bool releaseTexture(Texture *texture)=0
Release texture.
Custom GPU program.
Definition Shader.h:39
Result< PackedTerrainMaterialAtlases > packTerrainMaterialAtlases(const TerrainMaterialAtlases &atlases, const LoadedTerrainMaterial &material, const TerrainMaterialAtlasLimits &limits)
Repack grouped CPU atlases and material parameters for one terrain draw.
Result< void > bindTerrainMaterialGroup(graphics::Shader &shader, const TerrainMaterialGpuSet &gpu, const LoadedTerrainMaterial &material, std::size_t groupIndex)
Bind one uploaded group and its four layer values to a terrain shader.
Result< void > bindPackedTerrainMaterial(graphics::Shader &shader, const PackedTerrainMaterialGpuSet &gpu)
Bind a packed terrain set for one draw and publish its feature counts.
Result< PackedTerrainMaterialGpuSet > uploadPackedTerrainMaterialAtlases(graphics::IResourceFactory &factory, const PackedTerrainMaterialAtlases &atlases)
Upload a packed terrain set transactionally.
Result< TerrainMaterialAtlases > buildTerrainMaterialAtlases(const asset::EvpackResourceReader &reader, const LoadedTerrainMaterial &material, const asset::EvpackCapabilities &capabilities, const TerrainMaterialAtlasLimits &limits)
Resolve canonical image references and assemble one 2x2 atlas per control map.
Result< void > releasePackedTerrainMaterialAtlases(graphics::IResourceFactory &factory, PackedTerrainMaterialGpuSet &set)
Release and clear a packed terrain GPU set.
Result< void > releaseTerrainMaterialAtlases(graphics::IResourceFactory &factory, TerrainMaterialGpuSet &set)
Release every texture in a GPU set and clear it even if one backend release fails.
Result< TerrainMaterialGpuSet > uploadTerrainMaterialAtlases(graphics::IResourceFactory &factory, const TerrainMaterialAtlases &atlases)
Upload every atlas transactionally; partial failure releases earlier textures.
Result< DecodedEvpackImage > decodeEvpackImage(const EvpackResourceReader &reader, const AssetRef &image, const EvpackCapabilities &capabilities, const EvpackImageDecodeLimits &limits)
Decode and validate canonical eve.image/3 data with eve.image/2 compatibility.
double sample(const Heightmap &map, double u, double v)
Sample.
Actual runtime device capabilities used for variant selection.
Definition Evpack.h:92
Owning capability-selected terrain material candidate.
Single-draw terrain textures covering up to sixteen layers.
Backend-owned texture set for a single draw of up to sixteen layers.
One owning canonical terrain layer independent of its source engine.
Owning base-level linear RGBA8 image ready for backend upload.
Four-layer atlas group driven by one RGBA control image.
Bounds for deterministic terrain atlas construction.
Complete terrain material image set; up to four groups cover sixteen layers.
std::vector< TerrainMaterialAtlasGroup > groups
Four backend-owned textures for one uploaded terrain group.
Backend-owned terrain textures released together on their graphics thread.
std::vector< TerrainMaterialGpuGroup > groups
glm::vec4 color