载入中...
搜索中...
未找到
EvpackStaticPrefab.cpp
浏览该文件的文档.
7#include "graphics/Graphics.h"
8#include "graphics/Material.h"
9
10#include <algorithm>
11#include <cmath>
12#include <cstdio>
13#include <functional>
14#include <glm/gtc/matrix_transform.hpp>
15#include <glm/gtc/type_ptr.hpp>
16#include <map>
17#include <numeric>
18
19namespace eve::asset_graphics {
20namespace {
21using MaterialData = detail::CookedMaterial;
22} // namespace
23
27 // Factory ABI leases: only passed back to the same factories or synchronous draw calls.
28 std::map<std::string, graphics::Mesh*> meshLeases;
29 std::map<std::string, std::vector<uint32_t>> meshUvSets;
30 std::map<std::string, graphics::Texture*> imageLeases;
31 struct Draw {
32 glm::mat4 transform;
35 MaterialData material;
36 bool castShadows = true;
37 bool receiveShadows = true;
38 };
39 std::vector<Draw> draws;
40 std::vector<std::unique_ptr<graphics::Material>> gpuMaterials;
42 bool released = false;
43};
44EvpackStaticPrefab::EvpackStaticPrefab(std::unique_ptr<Impl> impl) : impl_(std::move(impl)) {}
46 auto result = release();
47 if (!result) std::fprintf(stderr, "asset prefab release failed: %s\n", result.error()->message().c_str());
48}
49std::size_t EvpackStaticPrefab::drawCount() const noexcept { return impl_->draws.size(); }
51 impl_->released = true;
52 if (impl_->gpuGraphics) {
53 for (const auto& material : impl_->gpuMaterials) {
54 auto released = impl_->gpuGraphics->gpuDrivenReleaseMaterialRecord(material.get());
55 if (!released) return released;
56 }
57 impl_->gpuGraphics = nullptr;
58 }
59 impl_->gpuMaterials.clear();
60 impl_->draws.clear();
61 Status failure;
62 for (auto it = impl_->meshLeases.begin(); it != impl_->meshLeases.end();) {
63 auto result = impl_->meshes.releaseMesh(it->second);
64 if (result)
65 it = impl_->meshLeases.erase(it);
66 else {
67 failure = result.status();
68 ++it;
69 }
70 }
71 for (auto it = impl_->imageLeases.begin(); it != impl_->imageLeases.end();) {
72 auto result = impl_->images.releaseImage(it->second);
73 if (result)
74 it = impl_->imageLeases.erase(it);
75 else {
76 failure = result.status();
77 ++it;
78 }
79 }
80 if (failure.isFailure()) return Result<void>::failure(failure);
81 return Result<void>::success();
82}
84 if (impl_->released)
86 DiagnosticCode::InvalidArgument, "prefab resources have been released", {}, {}, "asset.graphics.prefab"));
87 if (!impl_->gpuMaterials.empty())
89 Diagnostic::error(DiagnosticCode::InvalidArgument, "prefab GPU-driven parts are already registered", {}, {},
90 "asset.graphics.prefab"));
91 std::vector<std::unique_ptr<graphics::Material>> materials;
92 std::vector<StaticPrefabGpuPart> parts;
93 materials.reserve(impl_->draws.size());
94 parts.reserve(impl_->draws.size());
95 for (const auto& draw : impl_->draws) {
96 if (draw.material.transparent)
98 DiagnosticCode::InvalidArgument, "transparent prefab Detail cannot use opaque GPU submission", {}, {},
99 "asset.graphics.prefab"));
100 bool unsupportedImage = false;
101 for (std::size_t role = 1; role < draw.material.images.size(); ++role) {
102 if (role != std::size_t(graphics::PbrTextureSlot::Normal) && draw.material.images[role])
103 unsupportedImage = true;
104 }
105 const auto conventionalBinding = [](const graphics::PbrTextureBinding& binding) {
106 return binding.texcoord == 0 && binding.offset == std::array<float, 2>{0.f, 0.f} &&
107 binding.scale == std::array<float, 2>{1.f, 1.f} && binding.rotation == 0.f;
108 };
109 if (unsupportedImage ||
110 std::any_of(draw.material.detailImages.begin(), draw.material.detailImages.end(),
111 [](const auto& image) { return image.has_value(); }) ||
112 draw.material.vegetationSurface || draw.material.surface.normalScale != 1.f ||
113 draw.material.surface.normalMode != graphics::PbrNormalMode::TangentXYZ ||
114 !conventionalBinding(draw.material.surface.textures[0]) ||
115 !conventionalBinding(draw.material.surface.textures[std::size_t(graphics::PbrTextureSlot::Normal)]))
116 return Result<std::vector<StaticPrefabGpuPart>>::failure(Diagnostic::error(
117 DiagnosticCode::InvalidArgument, "extended prefab material requires a GPU-driven PBR surface record",
118 {}, {}, "asset.graphics.prefab"));
119 auto material = std::make_unique<graphics::Material>();
120 material->setTint(draw.material.color.r, draw.material.color.g, draw.material.color.b, draw.material.color.a);
121 material->setMetallic(draw.material.metallic);
122 material->setRoughness(draw.material.roughness);
123 material->setSurfaceMode(draw.material.masked ? "masked" : "opaque");
124 material->setAlphaCutoff(draw.material.alphaCutoff);
125 material->setDoubleSided(draw.material.doubleSided);
126 material->setAlbedoTexture(draw.image);
127 material->setNormalTexture(
128 draw.material.surface.textures[std::size_t(graphics::PbrTextureSlot::Normal)].texture);
129 if (!gfx.gpuDrivenMaterialUsable(material.get()))
130 return Result<std::vector<StaticPrefabGpuPart>>::failure(Diagnostic::error(
131 DiagnosticCode::InvalidArgument, "prefab material is unavailable to GPU-driven rendering", {}, {},
132 "asset.graphics.prefab"));
133 materials.push_back(std::move(material));
134 }
135 const auto releaseRegistered = [&gfx](const std::vector<std::unique_ptr<graphics::Material>>& owned,
136 std::size_t count) {
137 for (std::size_t i = 0; i < count; ++i) {
138 auto released = gfx.gpuDrivenReleaseMaterialRecord(owned[i].get());
139 if (!released) return released;
140 }
141 return Result<void>::success();
142 };
143 for (std::size_t drawIndex = 0; drawIndex < impl_->draws.size(); ++drawIndex) {
144 const auto& draw = impl_->draws[drawIndex];
145 auto& material = materials[drawIndex];
147 std::copy(glm::value_ptr(draw.transform), glm::value_ptr(draw.transform) + 16, part.localTransform.begin());
148 part.meshId = gfx.gpuDrivenMeshRecord(draw.mesh);
151 auto cleanup = releaseRegistered(materials, drawIndex + 1);
152 if (!cleanup) return Result<std::vector<StaticPrefabGpuPart>>::failure(cleanup.status());
154 Diagnostic::error(DiagnosticCode::InvalidArgument, "prefab GPU table registration failed", {}, {},
155 "asset.graphics.prefab"));
156 }
157 parts.push_back(part);
158 }
159 if (parts.empty())
161 DiagnosticCode::InvalidArgument, "prefab has no enabled renderer", {}, {}, "asset.graphics.prefab"));
162 impl_->gpuMaterials = std::move(materials);
163 impl_->gpuGraphics = &gfx;
164 return Result<std::vector<StaticPrefabGpuPart>>::success(std::move(parts));
165}
169 const AssetRef& ref,
170 const asset::EvpackCapabilities& caps) {
172 if (!scene) return Result<std::unique_ptr<EvpackStaticPrefab>>::failure(scene.status());
173 auto out =
174 std::unique_ptr<EvpackStaticPrefab>(new EvpackStaticPrefab(std::make_unique<Impl>(Impl{meshes, images})));
175 std::map<SceneObjectId, glm::mat4> transforms;
176 std::function<void(const scene::NodeDesc&, glm::mat4)> walk = [&](const scene::NodeDesc& node, glm::mat4 parent) {
177 if (!node.visible) return;
178 auto local = glm::translate(glm::mat4(1), glm::vec3(node.x, node.y, node.z));
179 local = glm::rotate(local, glm::radians(node.yaw), glm::vec3(0, 1, 0));
180 local = glm::rotate(local, glm::radians(node.pitch), glm::vec3(1, 0, 0));
181 local = glm::rotate(local, glm::radians(node.roll), glm::vec3(0, 0, 1));
182 local = glm::scale(local, glm::vec3(node.sx, node.sy, node.sz));
183 auto world = parent * local;
184 transforms.emplace(node.persistentId, world);
185 for (const auto& child : node.children) walk(child, world);
186 };
187 walk(scene.value().root, glm::mat4(1));
188 EvpackGraphicsLoader meshLoader(reader, meshes);
189 EvpackImageLoader imageLoader(reader, images);
190 for (const auto& binding : scene.value().renderers) {
191 if (!binding.enabled || !transforms.contains(binding.object)) continue;
192 auto parameters = detail::readCookedMaterial(reader, binding.material, caps);
193 if (!parameters) return Result<std::unique_ptr<EvpackStaticPrefab>>::failure(parameters.status());
194 const auto meshKey = binding.mesh.format();
195 if (!out->impl_->meshLeases.contains(meshKey)) {
196 auto loaded = meshLoader.loadMesh(binding.mesh, caps, {}, 0, true);
197 if (!loaded) return Result<std::unique_ptr<EvpackStaticPrefab>>::failure(loaded.status());
198 out->impl_->meshLeases.emplace(meshKey, loaded.value().mesh);
199 out->impl_->meshUvSets.emplace(meshKey, loaded.value().availableTexcoordSets);
200 }
201 graphics::Texture* image = nullptr;
202 for (size_t role = 0; role < 11; role++) {
203 if (!parameters.value().images[role]) continue;
204 const auto& sets = out->impl_->meshUvSets.at(meshKey);
205 auto selected = parameters.value().surface.textures[role].texcoord;
206 if (std::find(sets.begin(), sets.end(), selected) == sets.end())
208 Diagnostic::error(DiagnosticCode::InvalidArgument, "material references missing mesh UV channel",
209 {}, {}, "asset.graphics.prefab"));
210 const auto& ref = *parameters.value().images[role];
211 const auto imageKey = ref.format();
212 if (!out->impl_->imageLeases.contains(imageKey)) {
213 auto loaded = imageLoader.load(ref, caps);
214 if (!loaded) return Result<std::unique_ptr<EvpackStaticPrefab>>::failure(loaded.status());
215 out->impl_->imageLeases.emplace(imageKey, loaded.value().texture);
216 }
217 parameters.value().surface.textures[role].texture = out->impl_->imageLeases.at(imageKey);
218 }
219 for (size_t role = 0; role < 3; ++role) {
220 if (!parameters.value().detailImages[role]) continue;
221 const auto& ref = *parameters.value().detailImages[role];
222 const auto imageKey = ref.format();
223 if (!out->impl_->imageLeases.contains(imageKey)) {
224 auto loaded = imageLoader.load(ref, caps);
225 if (!loaded) return Result<std::unique_ptr<EvpackStaticPrefab>>::failure(loaded.status());
226 out->impl_->imageLeases.emplace(imageKey, loaded.value().texture);
227 }
228 parameters.value().surface.vegetationDetail.textures[role].texture = out->impl_->imageLeases.at(imageKey);
229 }
230 auto validatedSurface = graphics::validatePbrSurface(parameters.value().surface);
231 if (!validatedSurface) return Result<std::unique_ptr<EvpackStaticPrefab>>::failure(validatedSurface.status());
232 image = parameters.value().surface.textures[0].texture;
233 out->impl_->draws.push_back({transforms.at(binding.object), out->impl_->meshLeases.at(meshKey), image,
234 std::move(parameters).takeValue(), binding.castShadows,
235 binding.receiveShadows});
236 }
237 return Result<std::unique_ptr<EvpackStaticPrefab>>::success(std::move(out));
238}
239Result<void> EvpackStaticPrefab::draw(graphics::Graphics& gfx, const std::array<float, 16>& transform,
240 const std::array<float, 16>& cameraView) const {
241 if (impl_->released)
243 DiagnosticCode::InvalidArgument, "prefab resources have been released", {}, {}, "asset.graphics.prefab"));
244 for (float value : transform)
245 if (!std::isfinite(value))
247 DiagnosticCode::InvalidArgument, "nonfinite instance transform", {}, {}, "asset.graphics.prefab"));
248 for (float value : cameraView)
249 if (!std::isfinite(value))
251 {}, "asset.graphics.prefab"));
252 const auto instance = glm::make_mat4(transform.data());
253 const auto view = glm::make_mat4(cameraView.data());
254 std::vector<std::size_t> order(impl_->draws.size());
255 std::iota(order.begin(), order.end(), 0);
256 std::stable_sort(order.begin(), order.end(), [&](std::size_t a, std::size_t b) {
257 const auto& left = impl_->draws[a];
258 const auto& right = impl_->draws[b];
259 if (left.material.transparent != right.material.transparent) return !left.material.transparent;
260 return left.material.transparent &&
261 (view * instance * left.transform)[3].z < (view * instance * right.transform)[3].z;
262 });
263 for (auto index : order) {
264 const auto& draw = impl_->draws[index];
266 : draw.material.masked ? graphics::SurfaceMode::Masked
268 draw.material.blend, !draw.material.transparent, draw.material.doubleSided,
269 draw.material.alphaCutoff, "cutoff");
270 auto bound = gfx.setMesh3DPbrSurface(&draw.material.surface);
271 if (!bound) return bound;
272 gfx.setMesh3DMaterial(draw.material.metallic, draw.material.roughness);
273 gfx.setMesh3DNormalTexture(nullptr);
274 gfx.setMesh3DHeightTexture(nullptr);
275 gfx.setMesh3DVirtualTexture(false, 1, 1, 1, 1, 0.f);
276 gfx.setMesh3DTexCellBomb(1.f, 0.f, 0.f);
277 gfx.setMesh3DParallax(0.f);
278 gfx.setMesh3DShadowReceive(draw.receiveShadows);
279 auto color = draw.material.color;
280 gfx.drawMesh(draw.mesh, instance * draw.transform, draw.image, color);
281 }
282 return gfx.setMesh3DPbrSurface(nullptr);
283}
284
286 const std::array<float, 16>& transform,
287 const std::array<float, 16>& lightViewProjection) const {
288 if (impl_->released)
290 DiagnosticCode::InvalidArgument, "prefab resources have been released", {}, {}, "asset.graphics.prefab"));
291 for (float value : transform)
292 if (!std::isfinite(value))
294 DiagnosticCode::InvalidArgument, "nonfinite instance transform", {}, {}, "asset.graphics.prefab"));
295 for (float value : lightViewProjection)
296 if (!std::isfinite(value))
298 DiagnosticCode::InvalidArgument, "nonfinite shadow view projection", {}, {}, "asset.graphics.prefab"));
299 const glm::mat4 instance = glm::make_mat4(transform.data());
300 const glm::mat4 lightVP = glm::make_mat4(lightViewProjection.data());
301 for (const auto& draw : impl_->draws) {
302 if (!draw.castShadows || draw.material.transparent) continue;
303 const glm::mat4 lightMvp = lightVP * instance * draw.transform;
304 const bool doubleSided = draw.material.surface.cullMode == graphics::PbrCullMode::None ||
305 (draw.material.surface.cullMode == graphics::PbrCullMode::Inherit &&
306 draw.material.doubleSided);
307 if (draw.material.masked)
308 gfx.drawMeshShadowAlpha(draw.mesh, lightMvp, draw.image, doubleSided);
309 else
310 gfx.drawMeshShadow(draw.mesh, lightMvp, doubleSided);
311 }
312 return Result<void>::success();
313}
314} // namespace eve::asset_graphics
double value
std::vector< eve::artifact::PartView > parts
ActiveSource owned
void * impl
Transactional graphics consumers for canonical runtime assets.
Transactional EVIMG runtime texture loading.
Runtime decoding of canonical scene templates.
vk::UniqueImage image
std::string local
std::int32_t parent
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
std::vector< std::int32_t > order
MeshVfxBatchedDraw draw
bool doubleSided
eve::action::ActionVfxBinding binding
World3D * world
std::weak_ptr< PrimitiveScene > scene
glm::mat4 view
Material * material
const RoadNode * node
JSON-free versioned runtime metadata codec.
glm::mat4 lightVP
std::uint32_t count
int caps
Definition TreeMesh.cpp:162
uint32_t index
Stable asset identity backed by PersistentId.
Definition ResourceRef.h:53
std::string format() const
Return canonical asset://<uuid> text.
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
Structured status and zero or more diagnostics for an operation.
Definition Status.h:68
bool isFailure() const noexcept
Whether the operation has a failure outcome.
Definition Status.h:118
Immutable reader retaining an admitted pack by shared ownership.
Capability-aware adapter from admitted .evpack assets to graphics resources.
Result< LoadedGraphicsMesh > loadMesh(const AssetRef &asset, const asset::EvpackCapabilities &capabilities, const GraphicsAssetLoadLimits &limits={}, std::uint32_t texcoordSet=0, bool preserveAllTexcoords=false) const
Validate and upload one canonical mesh v3 (or compatibility v1/v2) atomically.
Capability-aware adapter from admitted EVIMG assets to backend textures.
Result< LoadedGraphicsImage > load(const AssetRef &image, const asset::EvpackCapabilities &capabilities, const ImageAssetLoadLimits &limits={}) const
Validate and upload image/3 mip assets, retaining image/2 single-level compatibility.
Owning static-prefab GPU leases and immutable draw transforms; no ECS or Scene mutation....
static Result< std::unique_ptr< EvpackStaticPrefab > > load(const asset::EvpackResourceReader &reader, graphics::IMeshResourceFactory &meshes, graphics::IImageResourceFactory &images, const AssetRef &asset, const asset::EvpackCapabilities &capabilities)
Validate a template and stage its mesh, material and image dependencies; failures release staged uplo...
std::size_t drawCount() const noexcept
Number of enabled static renderer bindings prepared for drawing.
Result< std::vector< StaticPrefabGpuPart > > prepareGpuDriven(graphics::Graphics &graphics)
Register every opaque or masked prefab draw in GPU-driven tables transactionally.
Result< void > release()
Invalidate draw packets and release all leases; failed releases remain owned for retry.
Result< void > draw(graphics::Graphics &graphics, const std::array< float, 16 > &transform, const std::array< float, 16 > &cameraView) const
Draw in an already-open 3D pass using its camera/light state and a column-major instance transform.
EvpackStaticPrefab(const EvpackStaticPrefab &)=delete
Result< void > drawShadow(graphics::Graphics &graphics, const std::array< float, 16 > &transform, const std::array< float, 16 > &lightViewProjection) const
Submit enabled source shadow casters inside an active cascade shadow pass.
Capability-aware scene-template/3 decoder with explicit scene-template/2 compatibility.
Result< LoadedSceneTemplate > load(const AssetRef &sceneTemplate, const asset::EvpackCapabilities &capabilities, const SceneTemplateLoadLimits &limits={}) const
Validate identities, hierarchy and finite TRS values before building a tree.
virtual void setMesh3DMaterial(float metallic, float roughness)=0
Metallic (0..1) and roughness (0..1) for the next default mesh draw.
virtual void drawMeshShadow(Mesh *mesh, const glm::mat4 &lightMVP, bool doubleSided=true)=0
Queue an opaque shadow caster.
virtual void drawMesh(Mesh *mesh, const glm::mat4 &model, Texture *texture, const Color &tint)=0
Draw one mesh with model matrix. Requires begin3DFrame() (or an open swapchain pass).
virtual void drawMeshShadowAlpha(Mesh *mesh, const glm::mat4 &lightMVP, Texture *albedo=nullptr, bool doubleSided=true, float lodWeight=1.f, bool lodFadeReverse=false, bool lodDither=false)=0
Shadow pass draw with alpha-cutout discard (card/billboard geometry such as sprite-stack slices): tra...
virtual Result< void > setMesh3DPbrSurface(const PbrSurface *surface)
Copy a validated extended surface for subsequent draws; null resets to legacy shading.
Definition Graphics.h:1192
virtual uint32_t gpuDrivenMeshRecord(Mesh *mesh)
GPU mesh-table slot for a mesh (kInvalidGpuDrivenSlot when not uploaded).
Definition Graphics.h:274
virtual bool gpuDrivenMaterialUsable(Material *material)
Whether a material can be shaded by the GPU-driven opaque path. Backends/drivers with descriptor-inde...
Definition Graphics.h:304
virtual void setMesh3DTexCellBomb(float cellScale, float strength, float rotAmount=1.f)=0
Texture cell bombing for the next default mesh draw (breaks tiling). cellScale: cells per UV unit (ty...
virtual void setMesh3DParallax(float scale, float minLayers=8.f, float maxLayers=32.f)=0
Parallax occlusion mapping for the next default mesh draw. scale: UV displacement strength (0=off)....
virtual void setMesh3DVirtualTexture(bool enabled, int pageCountX, int pageCountY, int atlasSlotsX, int atlasSlotsY, float borderFraction)=0
Configure page-table sampling for the next mesh draw.
virtual void setMesh3DSurface(SurfaceMode mode, BlendMode blend, bool depthWrite, bool doubleSided, float alphaCutoff, const std::string &alphaTechnique="cutoff")=0
Select pipeline state for subsequent mesh draws.
virtual uint32_t gpuDrivenMaterialRecord(Material *material)
GPU material-table slot for a material (lazily created).
Definition Graphics.h:280
virtual void setMesh3DShadowReceive(bool receive)=0
Per-draw: when false, shadow sampling is forced off for the next mesh draw.
virtual Result< void > gpuDrivenReleaseMaterialRecord(Material *material)
Release a lazily-created GPU material-table record.
Definition Graphics.h:292
virtual void setMesh3DNormalTexture(Texture *normal)=0
Optional normal map for the next drawMesh / drawMeshShader (nullptr = flat).
virtual void setMesh3DHeightTexture(Texture *height)=0
Optional height map for parallax (R channel; nullptr = flat / off).
Backend-owned RGBA8 texture upload and release operations.
Backend-owned mesh upload and release operations.
GPU mesh handle (+ optional CPU morph targets).
Definition Mesh.h:25
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
Definition Texture.h:18
Result< CookedMaterial > readCookedMaterial(const asset::EvpackResourceReader &reader, const AssetRef &ref, const asset::EvpackCapabilities &caps)
Decode material schemas 5 through 9 without allocating graphics resources. Unknown additive fields ar...
Result< void > validatePbrSurface(const PbrSurface &s)
Validate finite material factors and sampler enums without changing the input.
Definition PbrSurface.cpp:4
constexpr uint32_t kInvalidGpuDrivenSlot
GPU-driven rendering shared constants + std430 GPU layouts.
Actual runtime device capabilities used for variant selection.
Definition Evpack.h:92
std::map< std::string, graphics::Texture * > imageLeases
std::map< std::string, graphics::Mesh * > meshLeases
std::vector< std::unique_ptr< graphics::Material > > gpuMaterials
std::map< std::string, std::vector< uint32_t > > meshUvSets
One prefab renderer registered in backend GPU-driven tables.
One borrowed texture plus independently owned UV/sampler parameters. Texture is graphics-factory-owne...
Definition PbrSurface.h:39
Declarative scene-node description (build once / on dirty → flatten into SceneHost::Tree)....
Definition NodeDesc.h:19
glm::vec4 color