载入中...
搜索中...
未找到
RibbonBuilder.cpp
浏览该文件的文档.
2
3#include "common/Diagnostic.h"
4
5#include <algorithm>
6#include <cmath>
7
8#include <glm/glm.hpp>
9
10namespace eve::graphics::hair {
11namespace {
12
13glm::vec3 safeNormalize(const glm::vec3 &v, const glm::vec3 &fallback) {
14 const float len2 = glm::dot(v, v);
15 if (len2 < 1e-12f) return fallback;
16 return v * (1.f / std::sqrt(len2));
17}
18
19} // namespace
20
22 auto validated = strands.validate();
23 if (!validated.ok()) return Result<RibbonMesh>::failure(validated.status());
24
25 if (params.widthScale <= 0.f || !std::isfinite(params.widthScale)) {
27 DiagnosticCode::InvalidArgument, "buildRibbons: widthScale must be positive",
28 "hair.ribbon.widthScale"));
29 }
30
31 const glm::vec3 sideHint = safeNormalize(params.sideHint, glm::vec3(1.f, 0.f, 0.f));
32
34 const size_t curveN = strands.curveCount();
35 mesh.posXYZ.reserve(curveN * 8u * 3u);
36 mesh.nrmXYZ.reserve(curveN * 8u * 3u);
37 mesh.uvST.reserve(curveN * 8u * 2u);
38 mesh.indices.reserve(curveN * 7u * 6u);
39
40 for (size_t ci = 0; ci < curveN; ++ci) {
41 const auto pts = strands.curvePoints(ci);
42 if (pts.size() < 2) continue;
43
44 const uint32_t baseVert = uint32_t(mesh.posXYZ.size() / 3u);
45 for (size_t pi = 0; pi < pts.size(); ++pi) {
46 const StrandPoint &p = pts[pi];
47 glm::vec3 tangent(0.f, 1.f, 0.f);
48 if (pi + 1 < pts.size()) {
49 tangent = pts[pi + 1].position - p.position;
50 } else {
51 tangent = p.position - pts[pi - 1].position;
52 }
53 tangent = safeNormalize(tangent, glm::vec3(0.f, 1.f, 0.f));
54
55 glm::vec3 side = glm::cross(tangent, sideHint);
56 if (glm::dot(side, side) < 1e-10f) {
57 side = glm::cross(tangent, glm::vec3(0.f, 0.f, 1.f));
58 }
59 side = safeNormalize(side, glm::vec3(1.f, 0.f, 0.f));
60
61 const float halfW = std::max(p.radius * params.widthScale, params.minWidth);
62 const glm::vec3 left = p.position - side * halfW;
63 const glm::vec3 right = p.position + side * halfW;
64
65 auto pushVert = [&](const glm::vec3 &pos, float vSide) {
66 mesh.posXYZ.push_back(pos.x);
67 mesh.posXYZ.push_back(pos.y);
68 mesh.posXYZ.push_back(pos.z);
69 mesh.nrmXYZ.push_back(tangent.x);
70 mesh.nrmXYZ.push_back(tangent.y);
71 mesh.nrmXYZ.push_back(tangent.z);
72 mesh.uvST.push_back(vSide);
73 mesh.uvST.push_back(p.u);
74 };
75 pushVert(left, 0.f);
76 pushVert(right, 1.f);
77 }
78
79 const uint32_t segs = uint32_t(pts.size() - 1u);
80 for (uint32_t s = 0; s < segs; ++s) {
81 const uint32_t i0 = baseVert + s * 2u;
82 const uint32_t i1 = i0 + 1u;
83 const uint32_t i2 = i0 + 2u;
84 const uint32_t i3 = i0 + 3u;
85 mesh.indices.push_back(i0);
86 mesh.indices.push_back(i2);
87 mesh.indices.push_back(i1);
88 mesh.indices.push_back(i1);
89 mesh.indices.push_back(i2);
90 mesh.indices.push_back(i3);
91 }
92 }
93
94 if (mesh.indices.empty()) {
96 DiagnosticCode::Failed, "buildRibbons: produced empty mesh", "hair.ribbon"));
97 }
98 return Result<RibbonMesh>::success(std::move(mesh));
99}
100
101void appendRibbonMesh(RibbonMesh &dst, const RibbonMesh &src) {
102 const uint32_t base = uint32_t(dst.vertexCount());
103 dst.posXYZ.insert(dst.posXYZ.end(), src.posXYZ.begin(), src.posXYZ.end());
104 dst.nrmXYZ.insert(dst.nrmXYZ.end(), src.nrmXYZ.begin(), src.nrmXYZ.end());
105 dst.uvST.insert(dst.uvST.end(), src.uvST.begin(), src.uvST.end());
106 dst.indices.reserve(dst.indices.size() + src.indices.size());
107 for (uint32_t idx : src.indices) dst.indices.push_back(base + idx);
108}
109
110} // namespace eve::graphics::hair
const std::string & s
float halfW
int strands
Vec3 tangent
Definition CaveMesh.cpp:80
glm::vec4 p[6]
Stable, structured diagnostics shared by engine modules.
uint32_t i1
Definition Grass.cpp:61
uint32_t i2
Definition Grass.cpp:61
uint32_t i0
Definition Grass.cpp:61
float v
HexVec3 left
HexVec3 right
int idx
Mesh * mesh
ecs::EntityHandle side
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
CPU-authoritative strand buffer (UE FHairStrandsDatas analogue).
std::vector< ParamSpec > params
Procedural hair/fur card meshes + material/LOD helpers (no strand simulation).
Definition Graphics.h:58
Result< RibbonMesh > buildRibbons(const StrandsDatas &strands, const RibbonParams &params)
Expand validated strands into stable (non-view-facing) ribbons.
void appendRibbonMesh(RibbonMesh &dst, const RibbonMesh &src)
Append src into dst, rebasing triangle indices by dst vertex count. @ownership Both meshes remain cal...
Triangle-list ribbon mesh expanded from strand curves.
std::vector< float > posXYZ
std::vector< float > nrmXYZ
int vertexCount() const
Vertex count.
std::vector< uint32_t > indices
RibbonParams public API.
One control point on a hair curve (UE FHairStrandsDatas point analogue). @ownership Value type owned ...