载入中...
搜索中...
未找到
VegetationAsset.cpp
浏览该文件的文档.
2#include <cmath>
3#include <glm/geometric.hpp>
4#include <new>
7#include "graphics/Graphics.h"
8#include "graphics/Mesh.h"
12
13namespace eve::asset_graphics {
15 std::vector<graphics::VegetationVertex> vertices;
16 std::vector<float> uv;
17 std::vector<uint32_t> indices;
18};
19namespace {} // namespace
20VegetationAsset::VegetationAsset(std::unique_ptr<Impl> impl) : impl_(std::move(impl)) {}
22uint32_t VegetationAsset::vertexCount() const noexcept { return uint32_t(impl_->vertices.size()); }
24 using Output = std::unique_ptr<VegetationAsset>;
25 const auto count = mesh.positions.size() / 3;
26 if (!count || count > 4'000'000 || mesh.positions.size() % 3 || mesh.normals.size() != count * 3 ||
27 mesh.indices.empty() || mesh.indices.size() > 12'000'000 || mesh.indices.size() % 3)
29 DiagnosticCode::InvalidArgument, "invalid vegetation mesh geometry", {}, {}, "asset.graphics.vegetation"));
30 auto stream = [&](const char* name) -> const asset::CanonicalMeshAttribute* {
31 auto found = mesh.attributes.find(name);
32 return found != mesh.attributes.end() && found->second.components == 4 &&
33 found->second.values.size() == count * 4
34 ? &found->second
35 : nullptr;
36 };
37 const auto* color = stream("COLOR_0");
38 const auto* uv0 = stream("_UNITY_UV0");
39 const auto* uv1 = stream("_UNITY_UV1");
40 const auto* uv3 = stream("_UNITY_UV3");
41 const auto* tangent = stream("TANGENT");
42 if (mesh.attributes.contains("TANGENT") && !tangent)
44 "malformed vegetation tangent stream", {}, {},
45 "asset.graphics.vegetation"));
46 if (!color || !uv0 || !uv1 || !uv3)
48 "TVE requires float4 color and complete Unity UV0/UV1/UV4", {},
49 {}, "asset.graphics.vegetation"));
50 const auto renderUv = mesh.texcoords.find(0);
51 if (renderUv == mesh.texcoords.end() || renderUv->second.size() != count * 2)
53 "vegetation render UV0 is absent or malformed", {}, {},
54 "asset.graphics.vegetation"));
55 for (auto f : renderUv->second)
56 if (!std::isfinite(f))
58 "nonfinite vegetation render UV", {}, {},
59 "asset.graphics.vegetation"));
60 for (auto index : mesh.indices)
61 if (index >= count)
63 DiagnosticCode::InvalidArgument, "vegetation index out of range", {}, {}, "asset.graphics.vegetation"));
64 try {
65 std::vector<graphics::TvePackedVertex> packed(count);
66 auto four = [](const auto& a, size_t at) { return glm::vec4(a[at], a[at + 1], a[at + 2], a[at + 3]); };
67 for (size_t v = 0; v < count; ++v) {
68 auto& p = packed[v];
69 p.position = {mesh.positions[v * 3], mesh.positions[v * 3 + 1], mesh.positions[v * 3 + 2]};
70 p.normal = {mesh.normals[v * 3], mesh.normals[v * 3 + 1], mesh.normals[v * 3 + 2]};
71 const auto length = glm::length(p.normal);
72 if (!std::isfinite(length) || length < 1e-6f)
74 DiagnosticCode::InvalidArgument, "invalid vegetation normal", {}, {}, "asset.graphics.vegetation"));
75 p.color = four(color->values, v * 4);
76 p.texcoord0 = four(uv0->values, v * 4);
77 p.texcoord1 = four(uv1->values, v * 4);
78 p.texcoord3 = four(uv3->values, v * 4);
79 }
80 auto decoded = graphics::decodeTveVegetationVertices(packed);
81 if (!decoded) return Result<Output>::failure(decoded.status());
82 auto impl = std::make_unique<Impl>();
83 impl->vertices = std::move(decoded).takeValue();
84 for (size_t v = 0; v < count; ++v) {
85 auto& vertex = impl->vertices[v];
86 vertex.pivot.z = -vertex.pivot.z;
87 vertex.texcoord = {renderUv->second[v * 2], renderUv->second[v * 2 + 1]};
88 vertex.secondaryTexcoord.y = 1.f - vertex.secondaryTexcoord.y;
89 if (tangent) {
90 vertex.tangent = four(tangent->values, v * 4);
91 const auto t = glm::vec3(*vertex.tangent);
92 if (!std::isfinite(t.x) || !std::isfinite(t.y) || !std::isfinite(t.z) ||
93 (vertex.tangent->w != 1.f && vertex.tangent->w != -1.f) ||
94 glm::length(glm::cross(vertex.normal, t)) < 1e-6f)
96 "invalid vegetation tangent frame", {}, {},
97 "asset.graphics.vegetation"));
98 }
99 }
100 impl->uv = renderUv->second;
101 impl->indices = mesh.indices;
102 return Result<Output>::success(Output(new VegetationAsset(std::move(impl))));
103 } catch (const std::bad_alloc&) {
104 return Result<Output>::failure(Diagnostic::error(DiagnosticCode::Failed, "vegetation asset allocation failed",
105 {}, {}, "asset.graphics.vegetation"));
106 }
107}
109 const AssetRef& ref,
110 const asset::EvpackCapabilities& capabilities,
112 using Output = std::unique_ptr<VegetationAsset>;
113 auto payload = reader.read(ref, "eve.mesh/3", capabilities, limits.maximumDecodedBytes);
114 if (!payload) return Result<Output>::failure(payload.status());
115 const asset::RuntimeAssetChunk* bulk = nullptr;
116 for (const auto& chunk : payload.value().chunks) {
117 if (chunk.kind != asset::EvpackChunkKind::Bulk) continue;
118 if (bulk)
120 "vegetation mesh has multiple bulk chunks", {}, {},
121 "asset.graphics.vegetation"));
122 bulk = &chunk;
123 }
124 if (!bulk)
125 return Result<Output>::failure(Diagnostic::error(DiagnosticCode::NotFound, "vegetation mesh bulk is missing",
126 {}, {}, "asset.graphics.vegetation"));
127 auto decoded = asset::decodeCanonicalMesh(bulk->bytes, limits);
128 if (!decoded) return Result<Output>::failure(decoded.status());
129 return fromCanonical(decoded.value());
130}
132 const graphics::VegetationMotion& motion) const {
133 return graphics::deformVegetation(field, impl_->vertices, motion);
134}
136 const graphics::VegetationField& field,
137 const graphics::VegetationMotion& motion) const {
138 auto geometry = evaluate(field, motion);
139 if (!geometry) return Result<graphics::Mesh*>::failure(geometry.status());
140 auto* mesh =
141 graphics.newMeshFromArrays(geometry.value().positions.data(), geometry.value().normals.data(), impl_->uv.data(),
142 int(impl_->vertices.size()), impl_->indices.data(), int(impl_->indices.size()));
143 if (!mesh)
145 DiagnosticCode::Failed, "vegetation backend mesh creation failed", {}, {}, "asset.graphics.vegetation"));
146 auto attached =
147 mesh->adoptTangentFrame(std::move(geometry.value().tangents), std::move(geometry.value().bitangents));
148 if (!attached) {
149 if (!graphics.releaseMesh(mesh))
151 DiagnosticCode::Failed, "vegetation mesh rollback failed", {}, {}, "asset.graphics.vegetation"));
152 return Result<graphics::Mesh*>::failure(attached.status());
153 }
154 auto highlights = mesh->adoptMotionHighlights(std::move(geometry.value().motionHighlights));
155 if (!highlights) {
156 if (!graphics.releaseMesh(mesh))
158 DiagnosticCode::Failed, "vegetation highlight rollback failed", {}, {}, "asset.graphics.vegetation"));
159 return Result<graphics::Mesh*>::failure(highlights.status());
160 }
161 auto factors = mesh->adoptVegetationFactors(std::move(geometry.value().vegetationFactors));
162 if (!factors) {
163 if (!graphics.releaseMesh(mesh))
165 DiagnosticCode::Failed, "vegetation factor rollback failed", {}, {}, "asset.graphics.vegetation"));
166 return Result<graphics::Mesh*>::failure(factors.status());
167 }
169}
171 try {
172 std::vector<float> positions, normals, tangents, bitangents, shading, deformation;
173 positions.reserve(impl_->vertices.size() * 3);
174 normals.reserve(impl_->vertices.size() * 3);
175 shading.reserve(impl_->vertices.size() * 5);
176 deformation.reserve(impl_->vertices.size() * 9);
177 const bool authored = !impl_->vertices.empty() && impl_->vertices.front().tangent.has_value();
178 if (authored) {
179 tangents.reserve(impl_->vertices.size() * 3);
180 bitangents.reserve(impl_->vertices.size() * 3);
181 }
182 for (const auto& vertex : impl_->vertices) {
183 positions.insert(positions.end(), {vertex.position.x, vertex.position.y, vertex.position.z});
184 normals.insert(normals.end(), {vertex.normal.x, vertex.normal.y, vertex.normal.z});
185 shading.insert(shading.end(), {vertex.variation, vertex.occlusion, vertex.detail, vertex.detailCoord.x,
186 vertex.detailCoord.y});
187 deformation.insert(deformation.end(),
188 {vertex.pivot.x, vertex.pivot.y, vertex.pivot.z, vertex.bending, vertex.branch,
189 vertex.flutter, vertex.variation, vertex.boundsHeight, vertex.boundsRadius});
190 if (authored) {
191 const auto normal = glm::normalize(vertex.normal);
192 const auto tangent = glm::normalize(glm::vec3(*vertex.tangent) -
193 normal * glm::dot(normal, glm::vec3(*vertex.tangent)));
194 const auto bitangent = glm::cross(normal, tangent) * vertex.tangent->w;
195 tangents.insert(tangents.end(), {tangent.x, tangent.y, tangent.z});
196 bitangents.insert(bitangents.end(), {bitangent.x, bitangent.y, bitangent.z});
197 }
198 }
199 auto* mesh = graphics.newMeshFromArrays(positions.data(), normals.data(), impl_->uv.data(),
200 int(impl_->vertices.size()), impl_->indices.data(),
201 int(impl_->indices.size()));
202 if (!mesh)
204 DiagnosticCode::Failed, "vegetation rest mesh upload failed", {}, {}, "asset.graphics.vegetation"));
205 auto rollback = [&](Result<void> status) -> Result<graphics::Mesh*> {
206 if (!graphics.releaseMesh(mesh))
208 "vegetation rest mesh rollback failed", {},
209 {}, "asset.graphics.vegetation"));
211 };
212 auto attached = mesh->adoptTangentFrame(std::move(tangents), std::move(bitangents));
213 if (!attached) return rollback(std::move(attached));
214 auto shadingAttached = mesh->adoptVegetationFactors(std::move(shading));
215 if (!shadingAttached) return rollback(std::move(shadingAttached));
216 auto deformationAttached = mesh->adoptVegetationDeformationFactors(std::move(deformation));
217 if (!deformationAttached) return rollback(std::move(deformationAttached));
219 } catch (const std::bad_alloc&) {
221 DiagnosticCode::Failed, "vegetation rest mesh allocation failed", {}, {}, "asset.graphics.vegetation"));
222 }
223}
225 const graphics::VegetationField& field,
226 const graphics::VegetationMotion& motion) const {
227 return graphics::updateVegetationMesh(graphics, mesh, field, impl_->vertices, motion, impl_->uv, impl_->indices);
228}
229} // namespace eve::asset_graphics
Value::Object payload
void * impl
Vec3 tangent
Definition CaveMesh.cpp:80
float length
Definition CaveMesh.cpp:94
glm::vec4 p[6]
Typed capability-aware runtime access to admitted packs.
wgpu::PopErrorScopeStatus status
std::vector< float > normals
std::vector< float > positions
float v
std::string name
MeleePoint3 a
Definition MeleeHit.cpp:40
Texture * normal
float f
float t
Mesh * mesh
bool found
std::uint32_t count
uint32_t index
const AssetImportLimits & limits
std::size_t at
Stable asset identity backed by PersistentId.
Definition ResourceRef.h:53
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 reader retaining an admitted pack by shared ownership.
Result< RuntimeAssetPayload > read(const AssetRef &asset, std::string_view expectedType, const EvpackCapabilities &capabilities, std::uint64_t maximumDecodedBytes) const
Resolve, type-check, variant-select and decode a canonical runtime asset.
Owning decoded TVE rest geometry, independent of archive and GPU resource lifetimes....
Result< graphics::VegetationGeometry > evaluate(const graphics::VegetationField &field, const graphics::VegetationMotion &motion) const
Evaluate owning world-space geometry with explicit field/time/transform inputs. Worker-safe with immu...
std::uint32_t vertexCount() const noexcept
Return the immutable rest vertex count.
Result< graphics::Mesh * > createMesh(graphics::Graphics &graphics, const graphics::VegetationField &field, const graphics::VegetationMotion &motion) const
Create a backend-owned animated mesh; caller must release it through graphics. Graphics-thread only....
VegetationAsset(const VegetationAsset &)=delete
Result< graphics::Mesh * > createGpuFieldMesh(graphics::Graphics &graphics) const
Create an object-space rest mesh with owning GPU-deformation factors. Graphics-thread only....
Result< void > updateMesh(graphics::Graphics &graphics, graphics::Mesh &mesh, const graphics::VegetationField &field, const graphics::VegetationMotion &motion) const
Update an existing mesh from immutable rest data, without accumulating deformation....
static Result< std::unique_ptr< VegetationAsset > > load(const asset::EvpackResourceReader &reader, const AssetRef &ref, const asset::EvpackCapabilities &capabilities, const asset::CanonicalMeshLimits &limits)
Read one canonical runtime mesh and decode its TVE authoring streams. Reader, identity,...
static Result< std::unique_ptr< VegetationAsset > > fromCanonical(const asset::CanonicalMeshData &mesh)
Admit explicit TVE-packed Unity canonical streams as native rest geometry.
virtual bool releaseMesh(Mesh *mesh)
Eagerly releases a mesh created by this Graphics.
Definition Graphics.h:927
virtual Mesh * newMeshFromArrays(const float *posXYZ, const float *nrmXYZ, const float *uvST, int vertexCount, const uint32_t *indices, int indexCount)=0
Upload a triangle mesh from packed CPU arrays. Owned by Graphics. posXYZ required (vertexCount*3)....
GPU mesh handle (+ optional CPU morph targets).
Definition Mesh.h:25
Backend-independent, owning vegetation influence field. Single writer; concurrent const evaluation is...
Result< CanonicalMeshData > decodeCanonicalMesh(std::span< const std::uint8_t > bytes, const CanonicalMeshLimits &limits)
Decode canonical EVMESH binary v1, v2 or v3 into independently owned arrays.
Result< VegetationGeometry > deformVegetation(const VegetationField &field, std::span< const VegetationVertex > vertices, const VegetationMotion &motion)
Evaluate plant bending, squash, rolling, flutter, interaction and perspective. Worker-safe with immut...
Result< void > updateVegetationMesh(Graphics &graphics, Mesh &mesh, const VegetationField &field, std::span< const VegetationVertex > vertices, const VegetationMotion &motion, std::span< const float > texcoords, std::span< const uint32_t > indices)
Deform rest geometry then update an existing mesh through the active Graphics backend....
Result< std::vector< VegetationVertex > > decodeTveVegetationVertices(std::span< const TvePackedVertex > vertices)
Decode TVE 12.x color/motion/bounds/detail/pivot packing to native rest vertices. Borrows input only ...
Owning float vertex attribute; one to four components per vertex.
Owning CPU mesh snapshot; UV arrays are packed ST pairs indexed by source set number....
Allocation limits checked before decoding any vertex arrays. UV count is byte-budget bounded.
Actual runtime device capabilities used for variant selection.
Definition Evpack.h:92
One decoded runtime chunk with its admitted semantic metadata.
std::vector< std::uint8_t > bytes
std::vector< graphics::VegetationVertex > vertices
Source-equivalent plant motion parameters with explicit time and texture inputs. All data is owned....
glm::vec4 color