载入中...
搜索中...
未找到
HairCards.cpp
浏览该文件的文档.
2
3#include "common/Exception.h"
4#include "graphics/Graphics.h"
6#include "graphics/Material.h"
7#include "graphics/Mesh.h"
9#include "graphics/Shader.h"
10
11#include <cmath>
12
13namespace eve::graphics::hair {
14namespace {
15
16void pushVertex(CardMeshData &mesh, float x, float y, float z, float nx, float ny, float nz,
17 float u, float v) {
18 mesh.positions.push_back(x);
19 mesh.positions.push_back(y);
20 mesh.positions.push_back(z);
21 mesh.normals.push_back(nx);
22 mesh.normals.push_back(ny);
23 mesh.normals.push_back(nz);
24 mesh.uvs.push_back(u);
25 mesh.uvs.push_back(v);
26}
27
28void pushQuad(CardMeshData &mesh, float ax, float ay, float az, float bx, float by, float bz,
29 float cx, float cy, float cz, float dx, float dy, float dz, float nx, float ny,
30 float nz) {
31 const uint32_t base = static_cast<uint32_t>(mesh.positions.size() / 3);
32 pushVertex(mesh, ax, ay, az, nx, ny, nz, 0.f, 0.f);
33 pushVertex(mesh, bx, by, bz, nx, ny, nz, 1.f, 0.f);
34 pushVertex(mesh, cx, cy, cz, nx, ny, nz, 1.f, 1.f);
35 pushVertex(mesh, dx, dy, dz, nx, ny, nz, 0.f, 1.f);
36 mesh.indices.push_back(base + 0);
37 mesh.indices.push_back(base + 1);
38 mesh.indices.push_back(base + 2);
39 mesh.indices.push_back(base + 0);
40 mesh.indices.push_back(base + 2);
41 mesh.indices.push_back(base + 3);
42}
43
44void normalize3(float &x, float &y, float &z) {
45 const float len = std::sqrt(x * x + y * y + z * z);
46 if (len <= 1e-8f) {
47 x = 0.f;
48 y = 1.f;
49 z = 0.f;
50 return;
51 }
52 x /= len;
53 y /= len;
54 z /= len;
55}
56
57void cross3(float ax, float ay, float az, float bx, float by, float bz, float &ox, float &oy,
58 float &oz) {
59 ox = ay * bz - az * by;
60 oy = az * bx - ax * bz;
61 oz = ax * by - ay * bx;
62}
63
64} // namespace
65
68 const float w = width > 1e-4f ? width : 1e-4f;
69 const float h = height > 1e-4f ? height : 1e-4f;
70 const float hw = 0.5f * w;
71 pushQuad(mesh, -hw, 0.f, 0.f, hw, 0.f, 0.f, hw, h, 0.f, -hw, h, 0.f, 0.f, 0.f, 1.f);
72 return mesh;
73}
74
75CardMeshData buildCardsAlongPolyline(const float *pointsXYZ, int pointCount, float width) {
77 if (!pointsXYZ || pointCount < 2) return mesh;
78 const float w = width > 1e-4f ? width : 1e-4f;
79 const float hw = 0.5f * w;
80 for (int i = 0; i + 1 < pointCount; ++i) {
81 const float *a = pointsXYZ + i * 3;
82 const float *b = pointsXYZ + (i + 1) * 3;
83 float dx = b[0] - a[0], dy = b[1] - a[1], dz = b[2] - a[2];
84 normalize3(dx, dy, dz);
85 float sx = 0.f, sy = 0.f, sz = 0.f;
86 cross3(dx, dy, dz, 0.f, 1.f, 0.f, sx, sy, sz);
87 if (sx * sx + sy * sy + sz * sz < 1e-8f)
88 cross3(dx, dy, dz, 1.f, 0.f, 0.f, sx, sy, sz);
89 normalize3(sx, sy, sz);
90 float nx = 0.f, ny = 0.f, nz = 0.f;
91 cross3(sx, sy, sz, dx, dy, dz, nx, ny, nz);
92 normalize3(nx, ny, nz);
93 pushQuad(mesh, a[0] - sx * hw, a[1] - sy * hw, a[2] - sz * hw, a[0] + sx * hw,
94 a[1] + sy * hw, a[2] + sz * hw, b[0] + sx * hw, b[1] + sy * hw, b[2] + sz * hw,
95 b[0] - sx * hw, b[1] - sy * hw, b[2] - sz * hw, nx, ny, nz);
96 }
97 return mesh;
98}
99
100CardMeshData buildCardsFromRoots(const CardRoot *roots, int rootCount) {
102 if (!roots || rootCount <= 0) return mesh;
103 for (int i = 0; i < rootCount; ++i) {
104 const CardRoot &r = roots[i];
105 float dx = r.dirX, dy = r.dirY, dz = r.dirZ;
106 normalize3(dx, dy, dz);
107 const float len = r.length > 1e-4f ? r.length : 1e-4f;
108 const float tip[3] = {r.x + dx * len, r.y + dy * len, r.z + dz * len};
109 const float pts[6] = {r.x, r.y, r.z, tip[0], tip[1], tip[2]};
111 }
112 return mesh;
113}
114
116 const uint32_t base = static_cast<uint32_t>(dst.positions.size() / 3);
117 dst.positions.insert(dst.positions.end(), src.positions.begin(), src.positions.end());
118 dst.normals.insert(dst.normals.end(), src.normals.begin(), src.normals.end());
119 dst.uvs.insert(dst.uvs.end(), src.uvs.begin(), src.uvs.end());
120 for (uint32_t idx : src.indices) dst.indices.push_back(base + idx);
121}
122
124 if (!gfx) throw eve::Exception("hair::uploadCardMesh: null graphics");
125 if (data.positions.empty() || data.indices.empty()) return nullptr;
126 const int vertexCount = static_cast<int>(data.positions.size() / 3);
127 return gfx->newMeshFromArrays(data.positions.data(), data.normals.data(), data.uvs.data(),
128 vertexCount, data.indices.data(),
129 static_cast<int>(data.indices.size()));
130}
131
132Mesh *newCardMesh(Graphics *gfx, float width, float height) {
133 return uploadCardMesh(gfx, buildCard(width, height));
134}
135
137 mat.setHair(true);
138 mat.setShadingModel("hair");
139 mat.setSurfaceMode("transparent");
140 mat.setBlendMode("alpha");
141 mat.setDoubleSided(true);
142 mat.setDepthWrite(false);
143 mat.setCastShadow(false);
144 mat.setAlphaCutoff(0.15f);
145 if (mat.getSortPriority() == 0) mat.setSortPriority(10);
146}
147
149 if (!gfx) throw eve::Exception("hair::makeCardMaterial: null graphics");
150 auto *mat = new Material();
151 applyCardDefaults(*mat);
152 if (albedo) mat->setAlbedoTexture(albedo);
153 Shader *shader = hairShader ? hairShader : createShader(gfx);
154 mat->setShader(shader);
155 return mat;
156}
157
158void configureCardLod(Renderable3D *renderable, Mesh *nearCards, Mesh *farProxy,
159 float switchDistance) {
160 if (!renderable) throw eve::Exception("hair::configureCardLod: null renderable");
161 if (!nearCards) throw eve::Exception("hair::configureCardLod: null near mesh");
162 const float dist = switchDistance > 0.f ? switchDistance : 8.f;
163 renderable->setMeshLod(0, nearCards, 0.f);
164 if (farProxy) renderable->setMeshLod(1, farProxy, dist);
165 renderable->setHair(true);
166}
167
168} // namespace eve::graphics::hair
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
int tip
Definition AnimSmr.cpp:121
float cx
Definition CardTypes.cpp:33
float cy
Definition CardTypes.cpp:34
int bz
Definition CaveMesh.cpp:114
int ax
Definition CaveMesh.cpp:113
int ay
Definition CaveMesh.cpp:113
int bx
Definition CaveMesh.cpp:114
int az
Definition CaveMesh.cpp:113
int by
Definition CaveMesh.cpp:114
float nx
float nz
float ny
graphics::Texture * albedo
std::uint32_t vertexCount
float u
Definition Grass.cpp:233
double r
float v
int h
std::uint32_t height
std::uint32_t width
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
int idx
Mesh * mesh
Shader * shader
float dz
float dy
float dx
double oy
double ox
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
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)....
Packages shading method + surface parameters into one attachable asset.
Definition Material.h:35
void setCastShadow(bool cast)
Sets the cast shadow.
Definition Material.h:213
void setBlendMode(const std::string &mode)
Set "alpha", "premultiplied", "additive", or "multiply".
Definition Material.cpp:126
int getSortPriority() const
Returns the sort priority.
Definition Material.h:283
void setAlphaCutoff(float cutoff)
Sets the alpha cutoff.
Definition Material.cpp:124
void setSurfaceMode(const std::string &mode)
Set "opaque", "masked", or "transparent" surface classification.
Definition Material.cpp:109
void setShadingModel(const std::string &model)
"pbr" | "unlit" | "hair" | "custom" (unknown → pbr).
Definition Material.cpp:49
void setDepthWrite(bool enabled)
Sets the depth write.
Definition Material.h:269
void setSortPriority(int priority)
Sets the sort priority.
Definition Material.h:281
void setHair(bool hair)
Sets the hair.
Definition Material.cpp:95
void setDoubleSided(bool enabled)
Sets the double sided.
Definition Material.h:273
GPU mesh handle (+ optional CPU morph targets).
Definition Mesh.h:25
EVENGINE_API_BACKENDS public API.
void setMeshLod(int index, Mesh *mesh, float switchDistance=0.f)
Configure geometric LOD. index 0 = highest detail. For index > 0, switchDistance is the camera distan...
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
Procedural hair/fur card meshes + material/LOD helpers (no strand simulation).
Definition Graphics.h:58
CardMeshData buildCardsAlongPolyline(const float *pointsXYZ, int pointCount, float width)
Build one card per polyline segment, facing a camera-facing side approximated by world up.
Definition HairCards.cpp:75
CardMeshData buildCard(float width, float height)
Build a single upright card in local space (origin at root, height along +Y, face +Z).
Definition HairCards.cpp:66
CardMeshData buildCardsFromRoots(const CardRoot *roots, int rootCount)
Expand root samples into independent cards.
Mesh * newCardMesh(Graphics *gfx, float width, float height)
Convenience: single card mesh.
void applyCardDefaults(Material &mat)
Apply transparent hair-card surface defaults (double-sided, no depth write/shadows)....
Material * makeCardMaterial(Graphics *gfx, Texture *albedo, Shader *hairShader)
Create a Material configured for hair cards and optionally bind the hair shader + albedo.
Mesh * uploadCardMesh(Graphics *gfx, const CardMeshData &data)
Upload card arrays as a Graphics-owned Mesh.
void configureCardLod(Renderable3D *renderable, Mesh *nearCards, Mesh *farProxy, float switchDistance)
Wire near/far geometric LOD on a renderable (index 0 = cards, index 1 = proxy).
void appendCardMesh(CardMeshData &dst, const CardMeshData &src)
Append src into dst (vertex/index rebase).
Shader * createShader(Graphics *gfx)
Create the default hair shader (alpha-blended, anisotropic specular). Owned by Graphics.
CPU-side interleaved mesh arrays for upload via Graphics::newMeshFromArrays.
Definition HairCards.h:27
std::vector< uint32_t > indices
Definition HairCards.h:31
std::vector< float > normals
xyz per vertex
Definition HairCards.h:29
std::vector< float > positions
xyz per vertex
Definition HairCards.h:28
std::vector< float > uvs
uv per vertex
Definition HairCards.h:30
One root sample expanded into a card.
Definition HairCards.h:35