载入中...
搜索中...
未找到
TerrainVegetationGpu.cpp
浏览该文件的文档.
2
3#include "graphics/Graphics.h"
5
6#include <glm/gtc/quaternion.hpp>
7#include <glm/gtc/type_ptr.hpp>
8
9#include <map>
10#include <algorithm>
11#include <cmath>
12
13namespace eve::asset_procgen {
14namespace {
15
16} // namespace
17
20 const TerrainVegetationGpuFrame& frame) {
21 if (!std::isfinite(frame.timeSeconds) || std::abs(frame.timeSeconds) > 1.0e12)
23 Diagnostic::error(DiagnosticCode::InvalidArgument, "terrain vegetation frame time is invalid", {}, {},
24 "asset.procgen.terrainVegetationGpu"));
26 result.instances.reserve(realization.instances.size());
27 std::map<std::string_view, const RuntimeInstancePrototype*, std::less<>> prototypeMetadata;
28 for (const auto& prototype : realization.prototypes)
29 if (prototype.prototype.empty() || !prototypeMetadata.emplace(prototype.prototype, &prototype).second)
31 Diagnostic::error(DiagnosticCode::Conflict, "terrain vegetation prototype metadata is invalid",
32 prototype.prototype, {}, "asset.procgen.terrainVegetationGpu"));
33 std::size_t expectedFirst = 0;
34 for (const auto& bucket : realization.buckets) {
35 if (bucket.prototype.empty() || bucket.firstInstance != expectedFirst || bucket.instanceCount == 0 ||
36 std::uint64_t(bucket.firstInstance) + bucket.instanceCount > realization.instances.size())
38 Diagnostic::error(DiagnosticCode::InvalidArgument, "terrain vegetation bucket layout is invalid",
39 bucket.prototype, {}, "asset.procgen.terrainVegetationGpu"));
40 const auto metadata = prototypeMetadata.find(bucket.prototype);
41 auto prototype =
42 resolver.resolve({bucket.prototype, metadata == prototypeMetadata.end() ? nullptr : metadata->second});
43 if (!prototype) return Result<TerrainVegetationGpuPlan>::failure(prototype.status());
44 if (prototype.value().parts.empty() &&
45 (prototype.value().meshId == graphics::kInvalidGpuDrivenSlot ||
46 prototype.value().materialId == graphics::kInvalidGpuDrivenSlot))
48 Diagnostic::error(DiagnosticCode::NotFound, "vegetation prototype has no GPU mesh or material",
49 bucket.prototype, {}, "asset.procgen.terrainVegetationGpu"));
50 for (const auto& part : prototype.value().parts)
51 if (part.meshId == graphics::kInvalidGpuDrivenSlot || part.materialId == graphics::kInvalidGpuDrivenSlot)
53 Diagnostic::error(DiagnosticCode::NotFound, "vegetation prefab part has no GPU mesh or material",
54 bucket.prototype, {}, "asset.procgen.terrainVegetationGpu"));
55 for (std::uint32_t offset = 0; offset < bucket.instanceCount; ++offset) {
56 const auto& source = realization.instances[bucket.firstInstance + offset];
57 if (source.prototype != bucket.prototype)
59 Diagnostic::error(DiagnosticCode::Conflict, "vegetation bucket does not match instance prototype",
60 bucket.prototype, {}, "asset.procgen.terrainVegetationGpu"));
61 const glm::quat rotation(source.rotation[3], source.rotation[0], source.rotation[1], source.rotation[2]);
62 const glm::mat4 model =
63 glm::translate(glm::mat4(1.f), {source.position[0], source.position[1], source.position[2]}) *
64 glm::mat4_cast(rotation) *
65 glm::scale(glm::mat4(1.f), {source.scale[0], source.scale[1], source.scale[2]});
66 glm::vec4 instanceColor(1.f);
67 if (metadata != prototypeMetadata.end() && !metadata->second->useInstancing) {
68 const auto& detail = *metadata->second;
69 const float span = detail.maxHeight - detail.minHeight;
70 const float healthy = span > 0.f ? std::clamp((source.scale[1] - detail.minHeight) / span, 0.f, 1.f) : 1.f;
71 for (glm::length_t component = 0; component < 4; ++component)
72 instanceColor[component] =
73 detail.dryColor[component] + (detail.healthyColor[component] - detail.dryColor[component]) * healthy;
74 }
75 glm::vec4 terrainWave{};
76 glm::vec4 terrainWaveTint(1.f);
77 if (metadata != prototypeMetadata.end() && !metadata->second->usePrototypeMesh) {
78 terrainWave = {static_cast<float>(frame.timeSeconds * realization.wavingGrassSpeed),
79 realization.wavingGrassAmount, realization.wavingGrassStrength, 1.f};
80 terrainWaveTint = glm::make_vec4(realization.wavingGrassTint.data());
81 }
82 if (prototype.value().parts.empty()) {
83 graphics::GpuInstance instance{};
84 instance.model = model;
85 instance.meshId = prototype.value().meshId;
86 instance.materialId = prototype.value().materialId;
87 instance.flags = prototype.value().flags;
88 instance.lodGroupId = prototype.value().lodGroupId;
89 instance.color = instanceColor;
90 instance.terrainWave = terrainWave;
91 instance.terrainWaveTint = terrainWaveTint;
92 result.instances.push_back(instance);
93 } else {
94 for (const auto& part : prototype.value().parts) {
95 graphics::GpuInstance instance{};
96 instance.model = model * glm::make_mat4(part.localTransform.data());
97 instance.meshId = part.meshId;
98 instance.materialId = part.materialId;
99 instance.flags = part.flags;
100 instance.lodGroupId = part.lodGroupId;
101 instance.color = instanceColor;
102 instance.terrainWave = terrainWave;
103 instance.terrainWaveTint = terrainWaveTint;
104 result.instances.push_back(instance);
105 }
106 }
107 }
108 expectedFirst += bucket.instanceCount;
109 }
110 if (expectedFirst != realization.instances.size())
112 Diagnostic::error(DiagnosticCode::InvalidArgument, "terrain vegetation buckets do not cover every instance",
113 {}, {}, "asset.procgen.terrainVegetationGpu"));
114 return Result<TerrainVegetationGpuPlan>::success(std::move(result));
115}
116
119 const TerrainVegetationGpuFrame& frame) {
120 auto plan = buildTerrainVegetationGpuPlan(realization, resolver, frame);
121 if (!plan) return Result<void>::failure(plan.status());
122 if (plan.value().instances.empty()) return Result<void>::success();
123 if (!graphics.gpuDrivenSubmitOpaque(plan.value().instances.data(),
124 static_cast<std::uint32_t>(plan.value().instances.size())))
126 "GPU-driven vegetation submission failed", {}, {},
127 "asset.procgen.terrainVegetationGpu"));
128 return Result<void>::success();
129}
130
139
142 : state_(std::make_unique<State>()) {
143 state_->realization = std::move(realization);
144 state_->resolver = &resolver;
146 [this](graphics::Graphics&, const graphics::Camera3D::Data&, const glm::mat4&, float,
147 std::vector<graphics::GpuInstance>& instances) {
148 auto plan = buildTerrainVegetationGpuPlan(state_->realization, *state_->resolver, state_->frame);
149 if (plan) {
150 state_->gpuError.reset();
151 auto& vegetation = plan.value().instances;
152 instances.insert(instances.end(), vegetation.begin(), vegetation.end());
153 } else {
154 state_->gpuError = std::make_unique<Diagnostic>(*plan.error());
155 }
156 if (!state_->shadowFailedThisFrame) state_->shadowError.reset();
157 state_->shadowFailedThisFrame = false;
158 });
160 [this](graphics::Graphics&, const glm::mat4& lightViewProjection, const graphics::Camera3D::Data&) {
161 std::map<std::string_view, const RuntimeInstancePrototype*, std::less<>> metadata;
162 for (const auto& prototype : state_->realization.prototypes)
163 metadata.emplace(prototype.prototype, &prototype);
164 for (const auto& instance : state_->realization.instances) {
165 const auto found = metadata.find(instance.prototype);
166 const glm::quat rotation(instance.rotation[3], instance.rotation[0], instance.rotation[1],
167 instance.rotation[2]);
168 const glm::mat4 model =
169 glm::translate(glm::mat4(1.f), {instance.position[0], instance.position[1], instance.position[2]}) *
170 glm::mat4_cast(rotation) *
171 glm::scale(glm::mat4(1.f), {instance.scale[0], instance.scale[1], instance.scale[2]});
172 std::array<float, 16> modelValues{}, lightValues{};
173 std::copy(glm::value_ptr(model), glm::value_ptr(model) + 16, modelValues.begin());
174 std::copy(glm::value_ptr(lightViewProjection), glm::value_ptr(lightViewProjection) + 16,
175 lightValues.begin());
176 auto drawn = state_->resolver->drawShadow(
177 {instance.prototype, found == metadata.end() ? nullptr : found->second}, modelValues, lightValues);
178 if (!drawn) {
179 state_->shadowFailedThisFrame = true;
180 state_->shadowError = std::make_unique<Diagnostic>(*drawn.error());
181 return;
182 }
183 }
184 });
185}
186
191
193 if (!std::isfinite(frame.timeSeconds) || std::abs(frame.timeSeconds) > 1.0e12)
195 "terrain vegetation frame time is invalid", {}, {},
196 "asset.procgen.terrainVegetationGpu"));
197 state_->frame = frame;
198 return Result<void>::success();
199}
200
202 return state_->shadowError ? state_->shadowError.get() : state_->gpuError.get();
203}
204
205} // namespace eve::asset_procgen
std::vector< BuildingInstanceSnapshot > instances
size_t offset
std::array< float, 4 > rotation
int detail
glm::mat4 model
bool found
GPU-driven adapter for realized terrain vegetation.
const UnitySourceAsset & source
A structured explanation of a failed, degraded, or noteworthy result.
Definition Diagnostic.h:94
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
Provider boundary from stable prototype names to backend table slots.
virtual Result< TerrainVegetationGpuPrototype > resolve(const TerrainVegetationGpuPrototypeRequest &prototype)=0
Resolve one prototype and optional terrain Detail metadata without mutating the realization.
TerrainVegetationRenderer(TerrainVegetationRealization realization, ITerrainVegetationGpuResolver &resolver)
Terrain vegetation renderer.
Result< void > setFrame(TerrainVegetationGpuFrame frame)
Replace the explicit animation clock used by the next render frame.
const Diagnostic * lastSubmissionError() const
Return the most recent asynchronous submission failure, or null when healthy.
virtual bool gpuDrivenSubmitOpaque(const GpuInstance *instances, uint32_t instanceCount)
Upload + record GPU-driven opaque draws (call inside the open 3D frame). The backend sorts instances ...
Definition Graphics.h:365
static void removeShadowExtraDrawer(uint64_t token)
Remove one shadow callback before destroying its captured state.
static uint64_t addShadowExtraDrawer(ShadowExtraDrawer drawer)
Adds shadow extra drawer.
static void removeGpuOpaqueCollector(uint64_t token)
Unregister a GPU-driven opaque contributor.
static uint64_t addGpuOpaqueCollector(GpuOpaqueCollector collector)
Register a GPU-driven opaque contributor.
Result< void > submitTerrainVegetation(graphics::Graphics &graphics, const TerrainVegetationRealization &realization, ITerrainVegetationGpuResolver &resolver, const TerrainVegetationGpuFrame &frame)
Build and submit one terrain vegetation realization to the current GPU-driven frame.
Result< TerrainVegetationGpuPlan > buildTerrainVegetationGpuPlan(const TerrainVegetationRealization &realization, ITerrainVegetationGpuResolver &resolver, const TerrainVegetationGpuFrame &frame)
Resolve every prototype and build GPU records transactionally.
constexpr uint32_t kInvalidGpuDrivenSlot
GPU-driven rendering shared constants + std430 GPU layouts.
Explicit frame inputs for deterministic terrain Detail animation.
Detached CPU upload plan accepted by both Vulkan and WebGPU GPU-driven paths.
std::vector< graphics::GpuInstance > instances
Owning, prototype-sorted result plus scalability evidence.
std::vector< RuntimeInstancePrototype > prototypes
std::vector< TerrainVegetationInstance > instances
Per-instance GPU record (std430). Mirrors GLSL GpuInstance.
uint32_t bucket
glm::vec4 terrainWaveTint
glm::vec4 terrainWave