载入中...
搜索中...
未找到
BeamEffect.cpp
浏览该文件的文档.
2
3#include <glm/geometric.hpp>
4#include <glm/gtc/constants.hpp>
5
6#include <algorithm>
7#include <cmath>
8#include <vector>
9
10namespace eve::stylize {
11namespace {
12
13bool finite(glm::vec3 value) {
14 return std::isfinite(value.x) && std::isfinite(value.y) && std::isfinite(value.z);
15}
16
17struct RandomStream {
18 std::uint32_t state = 1;
19
20 float signedUnit() {
21 state ^= state << 13u;
22 state ^= state >> 17u;
23 state ^= state << 5u;
24 return static_cast<float>(state >> 8u) * (2.f / 16777216.f) - 1.f;
25 }
26};
27
28glm::vec3 safeNormal(glm::vec3 value, glm::vec3 fallback) {
29 const float length = glm::length(value);
30 return length > 1e-6f && std::isfinite(length) ? value / length : fallback;
31}
32
33void appendRibbon(TrailMeshSnapshot& mesh, const std::vector<glm::vec3>& centers,
34 const glm::vec3& cameraForward, float widthStart, float widthEnd,
35 float alphaStart, float alphaEnd) {
36 if (centers.size() < 2) return;
37 const std::uint32_t base = static_cast<std::uint32_t>(mesh.vertices.size());
38 for (std::size_t i = 0; i < centers.size(); ++i) {
39 const float t = static_cast<float>(i) / static_cast<float>(centers.size() - 1u);
40 const glm::vec3 previous = centers[i == 0 ? 0 : i - 1u];
41 const glm::vec3 next = centers[std::min(i + 1u, centers.size() - 1u)];
42 const glm::vec3 tangent = safeNormal(next - previous, glm::vec3(0.f, 1.f, 0.f));
43 glm::vec3 side = glm::cross(cameraForward, tangent);
44 if (glm::length(side) <= 1e-6f) side = glm::cross(glm::vec3(0.f, 1.f, 0.f), tangent);
45 if (glm::length(side) <= 1e-6f) side = glm::vec3(1.f, 0.f, 0.f);
46 side = glm::normalize(side);
47 const float halfWidth = 0.5f * (widthStart + (widthEnd - widthStart) * t);
48 const float alpha = alphaStart + (alphaEnd - alphaStart) * t;
49 mesh.vertices.push_back({centers[i] - side * halfWidth, {t, 0.f}, alpha});
50 mesh.vertices.push_back({centers[i] + side * halfWidth, {t, 1.f}, alpha});
51 if (i == 0) continue;
52 const std::uint32_t left0 = base + static_cast<std::uint32_t>((i - 1u) * 2u);
53 const std::uint32_t right0 = left0 + 1u;
54 const std::uint32_t left1 = base + static_cast<std::uint32_t>(i * 2u);
55 const std::uint32_t right1 = left1 + 1u;
56 mesh.indices.insert(mesh.indices.end(), {left0, right0, left1, left1, right0, right1});
57 }
58}
59
60std::vector<glm::vec3> makeCenters(const glm::vec3& start, const glm::vec3& end,
61 std::uint32_t segments, float amplitude, float cycles,
62 const glm::vec3& cameraForward, RandomStream& random) {
63 std::vector<glm::vec3> centers;
64 centers.reserve(static_cast<std::size_t>(segments) + 1u);
65 const glm::vec3 direction = safeNormal(end - start, glm::vec3(0.f, 1.f, 0.f));
66 glm::vec3 side = safeNormal(glm::cross(cameraForward, direction), glm::vec3(1.f, 0.f, 0.f));
67 glm::vec3 up = safeNormal(glm::cross(direction, side), glm::vec3(0.f, 1.f, 0.f));
68 for (std::uint32_t i = 0; i <= segments; ++i) {
69 const float t = static_cast<float>(i) / static_cast<float>(segments);
70 const float envelope = std::sin(glm::pi<float>() * t);
71 const float wave = std::sin(glm::two_pi<float>() * cycles * t + random.signedUnit());
72 const glm::vec3 noise =
73 (side * (wave + random.signedUnit() * 0.5f) + up * random.signedUnit()) *
74 (amplitude * envelope);
75 centers.push_back(start + (end - start) * t + noise);
76 }
77 centers.front() = start;
78 centers.back() = end;
79 return centers;
80}
81
82} // namespace
83
84BeamBuildResult buildBeamEffect(const glm::vec3& start, const glm::vec3& end,
85 const glm::vec3& cameraForward,
86 const BeamEffectConfig& config) {
87 BeamBuildResult result;
88 if (!finite(start) || !finite(end) || !finite(cameraForward) || config.segments == 0 ||
89 config.branchSegments == 0 || !std::isfinite(config.widthStart) ||
90 !std::isfinite(config.widthEnd) || config.widthStart <= 0.f || config.widthEnd < 0.f ||
91 !std::isfinite(config.noiseAmplitude) || config.noiseAmplitude < 0.f ||
92 !std::isfinite(config.branchLength) || config.branchLength < 0.f) {
94 return result;
95 }
96 const glm::vec3 delta = end - start;
97 const float length = glm::length(delta);
98 if (!std::isfinite(length) || length <= 1e-6f) {
100 return result;
101 }
102
103 RandomStream random{config.randomSeed ? config.randomSeed : 1u};
104 const glm::vec3 view = safeNormal(cameraForward, glm::vec3(0.f, 0.f, -1.f));
105 auto centers = makeCenters(start, end, config.segments, config.noiseAmplitude,
106 config.noiseCycles, view, random);
107 appendRibbon(result.mesh, centers, view, config.widthStart, config.widthEnd,
108 config.alphaStart, config.alphaEnd);
109
110 const glm::vec3 direction = delta / length;
111 glm::vec3 side = safeNormal(glm::cross(view, direction), glm::vec3(1.f, 0.f, 0.f));
112 glm::vec3 up = safeNormal(glm::cross(direction, side), glm::vec3(0.f, 1.f, 0.f));
113 for (std::uint32_t branch = 0; branch < config.branchCount; ++branch) {
114 const float t = static_cast<float>(branch + 1u) /
115 static_cast<float>(config.branchCount + 1u);
116 const std::size_t parentIndex = std::min(
117 static_cast<std::size_t>(std::round(t * config.segments)), centers.size() - 1u);
118 const glm::vec3 branchDirection = safeNormal(
119 direction * 0.35f + side * random.signedUnit() + up * random.signedUnit(), side);
120 const glm::vec3 branchStart = centers[parentIndex];
121 const glm::vec3 branchEnd =
122 branchStart + branchDirection * (length * config.branchLength);
123 auto branchCenters = makeCenters(branchStart, branchEnd, config.branchSegments,
124 config.noiseAmplitude * 0.5f, config.noiseCycles,
125 view, random);
126 appendRibbon(result.mesh, branchCenters, view,
127 config.widthStart * config.branchWidthScale, 0.f,
128 config.alphaStart, 0.f);
129 }
131 return result;
132}
133
134} // namespace eve::stylize
double value
Duration start
Vec3 tangent
Definition CaveMesh.cpp:80
float length
Definition CaveMesh.cpp:94
HexVec3 up
graphics::Canvas * previous
bool finite
float halfWidth
float t
Mesh * mesh
glm::mat4 view
RoadLaneDirection direction
Battle::Random random
ecs::EntityHandle side
constexpr HexDirection next(HexDirection d) noexcept
The next direction clockwise (NW wraps to NE).
Definition HexMetrics.h:76
BeamBuildResult buildBeamEffect(const glm::vec3 &start, const glm::vec3 &end, const glm::vec3 &cameraForward, const BeamEffectConfig &config)
Generate a camera-facing beam or branched lightning ribbon.
Owning beam generation result suitable for MeshEffectRenderer::submitTrail.
Definition BeamEffect.h:33
Shape and deterministic noise controls for one 3D beam or lightning arc.
Definition BeamEffect.h:14