载入中...
搜索中...
未找到
MeshEffectRenderer.cpp
浏览该文件的文档.
2
3#include "common/Diagnostic.h"
5#include "graphics/Graphics.h"
6#include "graphics/Mesh.h"
7#include "graphics/Shader.h"
9
10#include <glm/geometric.hpp>
11
12#include <cmath>
13#include <exception>
14
15namespace eve::stylize {
16namespace {
17
18bool finite(glm::vec3 value) {
19 return std::isfinite(value.x) && std::isfinite(value.y) && std::isfinite(value.z);
20}
21
22} // namespace
23
25 std::span<const MeshParticleInstance> instances, MeshEffectInstance* effect,
27 std::uint64_t emitterStableId, const glm::vec3& cameraPosition,
28 float projectionScalePixels, float maximumDistance, std::int32_t priority) {
30 result.lodCandidates.reserve(instances.size());
31 result.renderItems.reserve(instances.size());
32 result.commands.reserve(instances.size());
33 const float safeProjection = std::max(0.f, projectionScalePixels);
34 const float safeMaximumDistance = std::max(0.f, maximumDistance);
35 for (const auto& instance : instances) {
36 const glm::vec3 position(instance.model[3]);
37 const float distance = glm::length(position - cameraPosition);
38 const float scale = glm::length(glm::vec3(instance.model[0]));
39 const float projectedRadius = safeProjection * scale / std::max(distance, 1e-4f);
40 const std::uint64_t stableId =
41 (emitterStableId * 0x9e3779b97f4a7c15ull) ^ instance.stableId;
42 result.lodCandidates.push_back(
43 {stableId, distance, projectedRadius, priority,
44 std::isfinite(distance) && distance <= safeMaximumDistance});
45 result.renderItems.push_back({stableId, key, distance});
47 command.stableInstanceId = stableId;
49 command.effect = effect;
50 command.particle = {mesh, albedo, instance};
51 result.commands.push_back(std::move(command));
52 }
53 return result;
54}
55
57 TrailUploadData upload;
58 upload.positions.reserve(snapshot.vertices.size() * 3u);
59 upload.normals.assign(snapshot.vertices.size() * 3u, 0.f);
60 upload.uvs.reserve(snapshot.vertices.size() * 2u);
61 upload.indices = snapshot.indices;
62
63 for (const TrailVertex& vertex : snapshot.vertices) {
64 if (!finite(vertex.position) || !std::isfinite(vertex.uv.x) || !std::isfinite(vertex.uv.y))
66 eve::DiagnosticCode::InvalidArgument, "trail snapshot contains a non-finite vertex", {}, {},
67 "stylize.mesh-effect-renderer"));
68 upload.positions.insert(upload.positions.end(),
69 {vertex.position.x, vertex.position.y, vertex.position.z});
70 upload.uvs.insert(upload.uvs.end(), {vertex.uv.x, vertex.uv.y});
71 }
72 if (upload.indices.size() % 3u != 0u)
74 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "trail snapshot index count is not triangular",
75 {}, {}, "stylize.mesh-effect-renderer"));
76
77 for (std::size_t i = 0; i < upload.indices.size(); i += 3u) {
78 const std::uint32_t ia = upload.indices[i];
79 const std::uint32_t ib = upload.indices[i + 1u];
80 const std::uint32_t ic = upload.indices[i + 2u];
81 if (ia >= snapshot.vertices.size() || ib >= snapshot.vertices.size() || ic >= snapshot.vertices.size())
83 eve::DiagnosticCode::InvalidArgument, "trail snapshot contains an out-of-range index", {}, {},
84 "stylize.mesh-effect-renderer"));
85 const glm::vec3 a = snapshot.vertices[ia].position;
86 const glm::vec3 b = snapshot.vertices[ib].position;
87 const glm::vec3 c = snapshot.vertices[ic].position;
88 glm::vec3 normal = glm::cross(b - a, c - a);
89 const float length = glm::length(normal);
90 if (length > 1e-6f) normal /= length;
91 for (const std::uint32_t index : {ia, ib, ic}) {
92 upload.normals[index * 3u] += normal.x;
93 upload.normals[index * 3u + 1u] += normal.y;
94 upload.normals[index * 3u + 2u] += normal.z;
95 }
96 }
97 for (std::size_t i = 0; i < snapshot.vertices.size(); ++i) {
98 glm::vec3 normal(upload.normals[i * 3u], upload.normals[i * 3u + 1u],
99 upload.normals[i * 3u + 2u]);
100 const float length = glm::length(normal);
101 normal = length > 1e-6f ? normal / length : glm::vec3(0.f, 0.f, 1.f);
102 upload.normals[i * 3u] = normal.x;
103 upload.normals[i * 3u + 1u] = normal.y;
104 upload.normals[i * 3u + 2u] = normal.z;
105 }
106 return eve::Result<TrailUploadData>::success(std::move(upload));
107}
108
109MeshEffectRenderer::MeshEffectRenderer(graphics::Graphics& graphics) noexcept : graphics_(graphics) {}
110
111graphics::Shader* MeshEffectRenderer::shaderFor(MeshEffectInstance& effect) {
112 const std::string style = effect.style().getStyle();
113 auto found = shaders_.find(style);
114 if (found == shaders_.end()) {
115 graphics::Shader* shader = effect.newMeshShader(&graphics_);
116 found = shaders_.emplace(style, shader).first;
117 } else {
118 effect.style().applyToShader(found->second);
119 }
120 return found->second;
121}
122
123namespace {
124
125void requestSceneColorForRefraction(graphics::Graphics& graphics, MeshEffectInstance& effect) {
126 if (!effect.style().hasParam("refractionStrength") ||
127 effect.style().getFloat("refractionStrength") <= 0.f)
128 return;
129 const auto capture = graphics.captureMesh3DSceneColor();
130 switch (capture) {
136 break;
137 }
138}
139
140} // namespace
141
144 if (!effect.isBound())
146 eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, "mesh effect has no bound target", {},
147 {}, "stylize.mesh-effect-renderer"));
148 if (source.target != effect.target())
150 eve::DiagnosticCode::StaleHandle, "resolved mesh source does not match the effect target", {}, {},
151 "stylize.mesh-effect-renderer"));
152 if (!source.mesh)
154 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "resolved mesh source has no mesh", {}, {},
155 "stylize.mesh-effect-renderer"));
156 if (effect.intensity() <= 0.f)
158
159 try {
161 tint.a *= effect.intensity();
162 if (!batchActive_)
163 graphics_.setMesh3DSurface(graphics::SurfaceMode::Transparent, graphics::BlendMode::Additive,
164 false, true, 0.f);
165 requestSceneColorForRefraction(graphics_, effect);
166 graphics_.drawMeshShader(source.mesh, source.model, source.albedo, tint, shaderFor(effect));
167 } catch (const std::exception& error) {
169 eve::DiagnosticCode::Failed, std::string("mesh overlay submission failed: ") + error.what(), {}, {},
170 "stylize.mesh-effect-renderer"));
171 }
173}
174
176 MeshEffectInstance& effect, const TrailMeshSnapshot& snapshot, const glm::mat4& model,
177 const graphics::Color& tint) {
178 if (effect.intensity() <= 0.f)
180 if (snapshot.vertices.empty() || snapshot.indices.empty())
182
183 auto prepared = prepareTrailUpload(snapshot);
184 if (!prepared.ok())
185 return eve::Result<MeshEffectSubmitStatus>::failure(prepared.status());
186 TrailUploadData upload = std::move(prepared).takeValue();
187
188 try {
189 if (!trailMesh_) {
190 trailMesh_ = graphics_.newMeshFromArrays(
191 upload.positions.data(), upload.normals.data(), upload.uvs.data(),
192 static_cast<int>(upload.positions.size() / 3u), upload.indices.data(),
193 static_cast<int>(upload.indices.size()));
194 if (!trailMesh_)
196 eve::Diagnostic::error(eve::DiagnosticCode::Failed, "graphics failed to create the trail mesh", {},
197 {}, "stylize.mesh-effect-renderer"));
198 } else if (!graphics_.updateMeshVertices(
199 trailMesh_, upload.positions.data(), upload.normals.data(), upload.uvs.data(),
200 static_cast<int>(upload.positions.size() / 3u), upload.indices.data(),
201 static_cast<int>(upload.indices.size()))) {
203 eve::DiagnosticCode::Unsupported, "graphics backend rejected the dynamic trail upload", {}, {},
204 "stylize.mesh-effect-renderer"));
205 }
206 graphics::Color drawTint = tint;
207 drawTint.a *= effect.intensity();
208 if (!batchActive_)
209 graphics_.setMesh3DSurface(graphics::SurfaceMode::Transparent, graphics::BlendMode::Additive,
210 false, true, 0.f);
211 requestSceneColorForRefraction(graphics_, effect);
212 graphics_.drawMeshShader(trailMesh_, model, nullptr, drawTint, shaderFor(effect));
213 } catch (const std::exception& error) {
215 eve::Diagnostic::error(eve::DiagnosticCode::Failed, std::string("trail submission failed: ") + error.what(),
216 {}, {}, "stylize.mesh-effect-renderer"));
217 }
219}
220
223 if (!source.mesh)
225 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "mesh particle source has no mesh", {}, {},
226 "stylize.mesh-effect-renderer"));
227 if (effect.intensity() <= 0.f)
229 try {
230 graphics::Color tint = source.instance.color;
231 tint.a *= effect.intensity();
232 if (!batchActive_)
234 graphics::BlendMode::Additive, false, true, 0.f);
235 requestSceneColorForRefraction(graphics_, effect);
236 graphics_.drawMeshShader(source.mesh, source.instance.model, source.albedo, tint,
237 shaderFor(effect));
238 } catch (const std::exception& error) {
240 eve::DiagnosticCode::Failed, std::string("mesh particle submission failed: ") + error.what(), {}, {},
241 "stylize.mesh-effect-renderer"));
242 }
244}
245
247 const MeshVfxRenderQueue& queue, std::span<const MeshVfxRendererCommand> commands) {
248 if (batchActive_ || !activeCommands_.empty())
250 eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, "mesh VFX queue submission is reentrant",
251 {}, {}, "stylize.mesh-effect-renderer"));
252 for (const auto& command : commands) {
253 if (!command.effect)
255 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "mesh VFX renderer command has no effect",
256 {}, {}, "stylize.mesh-effect-renderer"));
257 if (!activeCommands_.emplace(command.stableInstanceId, &command).second) {
258 activeCommands_.clear();
260 eve::DiagnosticCode::InvalidArgument, "mesh VFX renderer commands contain a duplicate stable ID", {},
261 {}, "stylize.mesh-effect-renderer"));
262 }
263 }
264
265 MeshVfxSubmissionReport report = MeshVfxBatchExecutor{}.submit(queue, *this);
266 activeCommands_.clear();
267 batchActive_ = false;
268 return eve::Result<MeshVfxSubmissionReport>::success(std::move(report));
269}
270
272 if (batchActive_)
274 try {
275 const auto blend = key.blend == MeshVfxBatchBlend::Alpha ? graphics::BlendMode::Alpha
276 : graphics::BlendMode::Additive;
277 graphics_.setMesh3DSurface(graphics::SurfaceMode::Transparent, blend, false, true, 0.f);
278 batchActive_ = true;
280 } catch (const std::exception&) {
282 }
283}
284
286 if (!batchActive_)
288 const auto found = activeCommands_.find(draw.stableInstanceId);
289 if (found == activeCommands_.end())
291 const auto& command = *found->second;
294 ? submitTrail(*command.effect, command.trail, command.trailModel, command.trailTint)
296 ? submitParticle(*command.effect, command.particle)
297 : submitOverlay(*command.effect, command.overlay);
298 if (!submitted.ok())
302}
303
305 if (!batchActive_)
307 batchActive_ = false;
309}
310
311} // namespace eve::stylize
double value
std::vector< BuildingInstanceSnapshot > instances
int priority
float length
Definition CaveMesh.cpp:94
graphics::Texture * albedo
Stable, structured diagnostics shared by engine modules.
std::uint32_t key
glm::vec4 tint
float u
Definition Grass.cpp:233
std::int32_t c
float blend
std::array< float, 3 > position
std::array< float, 3 > scale
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float distance
MeshVfxBatchedDraw draw
Texture * normal
bool finite
std::string error
Definition Package.cpp:60
std::unique_ptr< ParticleEffect > effect
Mesh * mesh
Shader * shader
glm::mat4 model
bool found
uint32_t index
const UnitySourceAsset & source
std::vector< VegetationPresetCommand > commands
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
const T & value() const &
Borrow the value from a const lvalue after checking success.
Definition Result.h:308
bool ok() const noexcept
Whether this result represents a non-failure outcome.
Definition Result.h:255
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
virtual bool updateMeshVertices(Mesh *mesh, const float *posXYZ, const float *nrmXYZ, const float *uvST, int vertexCount, const uint32_t *indices, int indexCount)=0
In-place update of a mesh's vertex/index data (CPU -> host-visible VBO). Mirrors bakeMeshMorph: the u...
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 void drawMeshShader(Mesh *mesh, const glm::mat4 &model, Texture *texture, const Color &tint, Shader *shader)=0
Draw mesh with an explicit Mesh3D Shader (nullptr = default PBR pipeline).
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
Custom GPU program.
Definition Shader.h:39
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
Definition Texture.h:18
Runtime state for a shader effect attached to a renderable mesh.
Definition MeshEffect.h:40
MeshVfxSubmitStatus endBatch() override
End the active GPU-state batch.
eve::Result< MeshVfxSubmissionReport > submitQueue(const MeshVfxRenderQueue &queue, std::span< const MeshVfxRendererCommand > commands)
Resolve and submit a complete LOD-planned render queue.
eve::Result< MeshEffectSubmitStatus > submitTrail(MeshEffectInstance &effect, const TrailMeshSnapshot &snapshot, const glm::mat4 &model=glm::mat4(1.f), const graphics::Color &tint=graphics::Color(1.f))
Upload and draw one dynamic ribbon using an active mesh effect.
MeshVfxSubmitStatus submitDraw(const MeshVfxBatchedDraw &draw) override
Submit one resolved draw in the active batch.
MeshEffectRenderer(graphics::Graphics &graphics) noexcept
Bind this adapter to one Graphics owner for its entire lifetime.
eve::Result< MeshEffectSubmitStatus > submitParticle(MeshEffectInstance &effect, const MeshParticleDrawSource &source)
Draw one simulated mesh particle through an active mesh-effect style.
eve::Result< MeshEffectSubmitStatus > submitOverlay(MeshEffectInstance &effect, const MeshEffectDrawSource &source)
Draw a second-pass effect over a resolved source mesh.
MeshVfxSubmitStatus beginBatch(const MeshVfxBatchKey &key) override
Begin one GPU-state batch; intended for MeshVfxBatchExecutor.
Executes a planned render queue against one immediate backend sink.
MeshVfxSubmissionReport submit(const MeshVfxRenderQueue &queue, IMeshVfxBatchSink &sink) const
Submits all batches in order until completion or device loss.
eve::Color Color
RGBA color used by every graphics draw call. Lives inside eve::graphics so including a graphics heade...
Definition Color.h:13
eve::Result< TrailUploadData > prepareTrailUpload(const TrailMeshSnapshot &snapshot)
Validate and convert a ribbon snapshot into packed graphics arrays.
MeshVfxSubmitStatus
Result of one synchronous backend submission operation.
MeshParticleRenderInputs buildMeshParticleRenderInputs(std::span< const MeshParticleInstance > instances, MeshEffectInstance *effect, graphics::Mesh *mesh, graphics::Texture *albedo, const MeshVfxBatchKey &key, std::uint64_t emitterStableId, const glm::vec3 &cameraPosition, float projectionScalePixels, float maximumDistance, std::int32_t priority)
Convert stable particle snapshots into the shared LOD/batch/render pipeline.
Immediate borrowed inputs for drawing an effect over an existing mesh.
Borrowed mesh/material plus owning transform state for one mesh particle draw.
Complete planner and renderer inputs generated from one particle snapshot.
std::vector< MeshVfxRendererCommand > commands
std::vector< MeshVfxLodCandidate > lodCandidates
std::vector< MeshVfxRenderItem > renderItems
Immutable GPU-state identity used to combine compatible draw items.
One ordered draw reference inside a render batch.
Complete deterministic queue consumed by a graphics backend.
Concrete draw payload resolved for one batched stable instance ID.
Counters and terminal state produced by queue execution.
Owning CPU snapshot suitable for upload by a graphics adapter.
Definition TrailEffect.h:42
std::vector< std::uint32_t > indices
Definition TrailEffect.h:44
std::vector< TrailVertex > vertices
Definition TrailEffect.h:43
Packed owning arrays accepted by Graphics dynamic mesh upload APIs.
std::vector< std::uint32_t > indices
Backend-neutral vertex emitted by TrailEmitter.
Definition TrailEffect.h:35