载入中...
搜索中...
未找到
Sprite3D.cpp
浏览该文件的文档.
1#include "hd2d/Hd2d.h"
2
3#include "common/Assert.h"
4#include "common/Exception.h"
5#include "graphics/Graphics.h"
6#include "graphics/Material.h"
7#include "graphics/Mesh.h"
9#include "graphics/Texture.h"
10
11#include <algorithm>
12#include <cmath>
13#include <cstdint>
14#include <glm/glm.hpp>
15#include <glm/gtx/euler_angles.hpp>
16#include <limits>
17#include <string>
18#include <vector>
19
20namespace eve::hd2d {
21
22namespace {
23
24// Unit quad in the XY plane, normal -Z, full 0..1 UV. Spans [-0.5,0.5] so the
25// Renderable3D scale maps it to the requested world-space size.
26const std::vector<float> kQuadPos = {-0.5f, -0.5f, 0.f, 0.5f, -0.5f, 0.f,
27 0.5f, 0.5f, 0.f, -0.5f, 0.5f, 0.f};
28const std::vector<float> kQuadNrm = {0.f, 0.f, -1.f, 0.f, 0.f, -1.f,
29 0.f, 0.f, -1.f, 0.f, 0.f, -1.f};
30const std::vector<uint32_t> kQuadIdx = {0, 2, 1, 0, 3, 2};
31const std::vector<float> kQuadUv = {0.f, 0.f, 1.f, 0.f, 1.f, 1.f, 0.f, 1.f};
32
33} // namespace
34
35// ---------------------------------------------------------------------------
36// Sprite3D
37// ---------------------------------------------------------------------------
38
39Sprite3D::Sprite3D() = default;
41 if (renderable_) renderable_->setVisible(false);
42}
43
44void Sprite3D::buildQuad(graphics::Graphics *gfx) {
45 if (!gfx) throw eve::Exception("Sprite3D: null graphics");
46 gfx_ = gfx;
47 quad_ = gfx->newMeshFromArrays(kQuadPos.data(), kQuadNrm.data(), kQuadUv.data(),
48 int(kQuadPos.size() / 3), kQuadIdx.data(),
49 int(kQuadIdx.size()));
50 if (!quad_) throw eve::Exception("Sprite3D: quad mesh creation failed");
51
52 renderable_ = graphics::Renderable3D::create();
53 if (!renderable_) throw eve::Exception("Sprite3D: Renderable3D create failed");
54 renderable_->setMesh(quad_);
55 auto *material = gfx->newMaterial();
56 material->setSurfaceMode("masked");
57 material->setReceiveLight(false);
58 material->setAlbedoTexture(texture_);
59 material->setTint(tintR_, tintG_, tintB_, tintA_);
60 material->setAlphaCutoff(alphaCutoff_);
61 material->setDepthWrite(depthWrite_);
62 material->setDoubleSided(doubleSided_);
63 renderable_->setMaterial(material);
64 // 2D sprites are self-lit (HD-2D characters), not shaded by 3D lights.
65 renderable_->setReceiveLight(false);
66 renderable_->setScale(width_, height_, 1.f);
67 renderable_->setPosition(x_, y_, z_);
68 renderable_->setTint(tintR_, tintG_, tintB_, tintA_);
69 renderable_->setVisible(visible_);
70 updateFrameUv();
71}
72
73void Sprite3D::syncMaterial() {
74 if (!renderable_ || !renderable_->getMaterial()) return;
75 auto *material = renderable_->getMaterial();
76 material->setSurfaceMode("masked");
77 material->setAlphaCutoff(alphaCutoff_);
78 material->setDepthWrite(depthWrite_);
79 material->setDoubleSided(doubleSided_);
80 material->setTint(tintR_, tintG_, tintB_, tintA_);
81 material->setAlbedoTexture(texture_);
82}
83
84void Sprite3D::updateFrameUv() {
85 if (!gfx_ || !quad_) return;
86 std::vector<float> uv(8);
87 for (int i = 0; i < 4; ++i) {
88 // The visible front is -Z, so local +X is the viewer's left.
89 float u = (i == 0 || i == 3) ? u1_ : u0_;
90 // Image row zero is at the top; the first two vertices are the feet.
91 float v = (i < 2) ? v1_ : v0_;
92 if (flipX_) u = (u0_ + u1_) - u;
93 if (flipY_) v = (v0_ + v1_) - v;
94 uv[size_t(i) * 2u] = u;
95 uv[size_t(i) * 2u + 1u] = v;
96 }
97 auto positions = kQuadPos;
98 for (int i = 0; i < 4; ++i) {
99 // Local +X projects left on the visible -Z face.
100 positions[size_t(i) * 3u] += pivotX_ - 0.5f;
101 positions[size_t(i) * 3u + 1u] += pivotY_ - 0.5f;
102 }
103 gfx_->updateMeshVertices(quad_, positions.data(), kQuadNrm.data(), uv.data(), int(kQuadPos.size() / 3), nullptr, 0);
104}
105
107 texture_ = texture;
108 if (renderable_) {
109 renderable_->setTexture(texture);
110 syncMaterial();
111 }
112}
113graphics::Texture *Sprite3D::getTexture() const { return texture_; }
114
115void Sprite3D::setFrame(float u0, float v0, float u1, float v1) {
116 u0_ = u0;
117 v0_ = v0;
118 u1_ = u1;
119 v1_ = v1;
120 updateFrameUv();
121}
122void Sprite3D::getFrame(float &u0, float &v0, float &u1, float &v1) const {
123 u0 = u0_;
124 v0 = v0_;
125 u1 = u1_;
126 v1 = v1_;
127}
128void Sprite3D::setFlipX(bool flip) {
129 flipX_ = flip;
130 updateFrameUv();
131}
132void Sprite3D::setFlipY(bool flip) {
133 flipY_ = flip;
134 updateFrameUv();
135}
136
138 if (columns <= 0 || rows <= 0 || columns > std::numeric_limits<int>::max() / rows)
139 throw eve::Exception("Sprite3D.setFrameGrid: columns and rows must be > 0");
140 gridCols_ = columns;
141 gridRows_ = rows;
142 stop();
143 frameIndex_ = std::min(frameIndex_, gridCols_ * gridRows_ - 1);
144 setFrameIndex(frameIndex_);
145}
146int Sprite3D::getFrameGridColumns() const { return gridCols_; }
147int Sprite3D::getFrameGridRows() const { return gridRows_; }
148int Sprite3D::getFrameCount() const { return gridCols_ * gridRows_; }
149
151 frameIndex_ = std::max(0, std::min(index, gridCols_ * gridRows_ - 1));
152 const int cx = frameIndex_ % gridCols_;
153 const int cy = frameIndex_ / gridCols_;
154 const float u0 = float(cx) / float(gridCols_);
155 const float u1 = float(cx + 1) / float(gridCols_);
156 const float v0 = float(cy) / float(gridRows_);
157 const float v1 = float(cy + 1) / float(gridRows_);
158 setFrame(u0, v0, u1, v1);
159}
160int Sprite3D::getFrameIndex() const { return frameIndex_; }
161
162void Sprite3D::play(int start, int end, float fps) {
163 if (start < 0 || end < start || end >= gridCols_ * gridRows_)
164 throw eve::Exception("Sprite3D.play: frame range [%d,%d] out of grid", start, end);
165 if (!std::isfinite(fps) || fps <= 0.f) throw eve::Exception("Sprite3D.play: fps must be finite and > 0");
166 anim_ = {start, end, fps, 0.f, true};
168}
169void Sprite3D::stop() { anim_.playing = false; }
170bool Sprite3D::isPlaying() const { return anim_.playing; }
171
172void Sprite3D::update(float dt) {
173 if (anim_.playing && std::isfinite(dt) && dt > 0.f) {
174 const int span = anim_.end - anim_.start + 1;
175 // Reduce in floating point before converting to int. A long suspension
176 // or a large finite dt must never overflow the integral frame counter.
177 const double frames = double(anim_.clock) + double(dt) * double(anim_.fps);
178 const double whole = std::floor(frames);
179 anim_.clock = float(frames - whole);
180 if (whole >= 1.0) {
181 const int steps = int(std::fmod(whole, double(span)));
182 const int offset = std::clamp(frameIndex_ - anim_.start, 0, span - 1);
183 setFrameIndex(anim_.start + int((int64_t(offset) + steps) % span));
184 }
185 }
186 orientToCamera();
187}
188
190 camera_ = camera;
191 orientToCamera();
192}
193
194void Sprite3D::orientToCamera() {
195 if (!renderable_ || !camera_) return;
196 const auto &cam = *camera_->data();
197 const glm::vec3 eye(cam.eyeX, cam.eyeY, cam.eyeZ);
198 glm::vec3 forward = glm::vec3(cam.targetX, cam.targetY, cam.targetZ) - eye;
199 const float length2 = glm::dot(forward, forward);
200 if (!std::isfinite(length2) || length2 < 1e-12f) return;
201 forward /= std::sqrt(length2);
202
203 glm::mat4 rotation(1.f);
204 if (yawBillboard_) {
205 // Cylindrical (Y-up) billboard: face the camera on XZ only.
206 glm::vec3 flat(-forward.x, 0.f, -forward.z);
207 const float flatLen2 = glm::dot(flat, flat);
208 if (!std::isfinite(flatLen2) || flatLen2 < 1e-12f) return;
209 flat /= std::sqrt(flatLen2);
210 const glm::vec3 right = glm::normalize(glm::cross(glm::vec3(0.f, 1.f, 0.f), flat));
211 rotation[0] = glm::vec4(-right, 0.f);
212 rotation[1] = glm::vec4(0.f, 1.f, 0.f, 0.f);
213 rotation[2] = glm::vec4(flat, 0.f);
214 } else {
215 glm::vec3 right = glm::cross(forward, glm::vec3(cam.upX, cam.upY, cam.upZ));
216 const float rightLength2 = glm::dot(right, right);
217 if (!std::isfinite(rightLength2) || rightLength2 < 1e-12f) return;
218 right /= std::sqrt(rightLength2);
219 // Use the camera basis, not eye-to-sprite: all sprites remain parallel to
220 // the image plane, including off-center sprites and rolled cameras.
221 rotation[0] = glm::vec4(-right, 0.f);
222 rotation[1] = glm::vec4(glm::cross(right, forward), 0.f);
223 rotation[2] = glm::vec4(forward, 0.f);
224 }
225 float yaw, pitch, roll;
226 glm::extractEulerAngleYXZ(rotation, yaw, pitch, roll);
227 renderable_->setRotation(yaw, pitch, roll);
228}
229
230void Sprite3D::setPivot(float x, float y) {
231 EV_PARAM_CHECK((std::isfinite(x) && std::isfinite(y)), "Sprite3D pivot must be finite");
232 pivotX_ = x;
233 pivotY_ = y;
234 updateFrameUv();
235}
236
237void Sprite3D::setPosition(float x, float y, float z) {
238 x_ = x;
239 y_ = y;
240 z_ = z;
241 if (renderable_) {
242 renderable_->setPosition(x, y, z);
243 orientToCamera();
244 }
245}
246float Sprite3D::getPositionX() const { return x_; }
247float Sprite3D::getPositionY() const { return y_; }
248float Sprite3D::getPositionZ() const { return z_; }
249
250void Sprite3D::setSize(float width, float height) {
251 if (width <= 0.f || height <= 0.f)
252 throw eve::Exception("Sprite3D.setSize: width and height must be > 0");
253 width_ = width;
254 height_ = height;
255 if (renderable_) renderable_->setScale(width, height, 1.f);
256}
257float Sprite3D::getWidth() const { return width_; }
258float Sprite3D::getHeight() const { return height_; }
259
260void Sprite3D::setTint(float r, float g, float b, float a) {
261 tintR_ = std::max(0.f, std::min(1.f, r));
262 tintG_ = std::max(0.f, std::min(1.f, g));
263 tintB_ = std::max(0.f, std::min(1.f, b));
264 tintA_ = std::max(0.f, std::min(1.f, a));
265 if (renderable_) {
266 renderable_->setTint(tintR_, tintG_, tintB_, tintA_);
267 syncMaterial();
268 }
269}
271 visible_ = visible;
272 if (renderable_) renderable_->setVisible(visible);
273}
274bool Sprite3D::getVisible() const { return visible_; }
275
276void Sprite3D::setAlphaCutoff(float cutoff) {
277 if (!std::isfinite(cutoff)) throw eve::Exception("Sprite3D.setAlphaCutoff: cutoff must be finite");
278 alphaCutoff_ = std::max(0.f, std::min(1.f, cutoff));
279 syncMaterial();
280}
281float Sprite3D::getAlphaCutoff() const { return alphaCutoff_; }
282
284 depthWrite_ = enabled;
285 syncMaterial();
286}
287bool Sprite3D::getDepthWrite() const { return depthWrite_; }
288
290 doubleSided_ = enabled;
291 syncMaterial();
292}
293bool Sprite3D::getDoubleSided() const { return doubleSided_; }
294
295void Sprite3D::setBillboardMode(const std::string &mode) {
296 if (mode == "yaw" || mode == "cylindrical" || mode == "y") {
297 yawBillboard_ = true;
298 } else if (mode == "screen" || mode == "camera" || mode.empty()) {
299 yawBillboard_ = false;
300 } else {
301 throw eve::Exception("Sprite3D.setBillboardMode: expected 'screen' or 'yaw'");
302 }
303 orientToCamera();
304}
305std::string Sprite3D::getBillboardMode() const { return yawBillboard_ ? "yaw" : "screen"; }
306
307} // namespace eve::hd2d
Duration start
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
EVEngine assertion entry point, backed by zeroerr.
#define EV_PARAM_CHECK(cond,...)
Validate a function parameter / public API precondition.
Definition Assert.h:30
float cx
Definition CardTypes.cpp:33
float cy
Definition CardTypes.cpp:34
float uv
int rows
tensor::Graph g
Definition GpuGraph.cpp:7
float camera[2]
float u
Definition Grass.cpp:233
double r
std::vector< float > positions
float v
HexVec3 right
std::uint32_t height
std::uint32_t width
size_t offset
std::array< float, 4 > rotation
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
int steps
glm::vec3 eye
Material * material
int columns
bool visible
uint32_t index
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
EVENGINE_API_BACKENDS public API.
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...
Material * newMaterial()
Create a Material asset (shading model + textures + PBR knobs). Caller owns Material*; not tracked by...
Definition Graphics.cpp:252
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)....
void setSurfaceMode(const std::string &mode)
Set "opaque", "masked", or "transparent" surface classification.
Definition Material.cpp:109
void setRotation(float yaw, float pitch, float roll)
void setTint(float r, float g, float b, float a=1.f)
void setTexture(Texture *texture)
void setReceiveLight(bool receive)
void setMaterial(Material *material)
Attach a Material that packages shading method + surface params.
void setPosition(float x, float y, float z)
void setScale(float sx, float sy, float sz)
void setVisible(bool visible)
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
Definition Texture.h:18
int getFrameGridColumns() const
Returns the frame grid columns.
Definition Sprite3D.cpp:146
bool getDoubleSided() const
Returns the double sided.
Definition Sprite3D.cpp:293
bool getDepthWrite() const
Returns the depth write.
Definition Sprite3D.cpp:287
bool isPlaying() const
True when playing.
Definition Sprite3D.cpp:170
float getHeight() const
Returns the height.
Definition Sprite3D.cpp:258
float getPositionY() const
Returns the position y.
Definition Sprite3D.cpp:247
void setFlipX(bool flip)
Flip the sampled frame horizontally / vertically.
Definition Sprite3D.cpp:128
void setPosition(float x, float y, float z)
World position of the pivot (center by default).
Definition Sprite3D.cpp:237
float getPositionZ() const
Returns the position z.
Definition Sprite3D.cpp:248
int getFrameIndex() const
Returns the frame index.
Definition Sprite3D.cpp:160
void update(float dt)
Advance the animation clock and re-orient the billboard toward the attached camera....
Definition Sprite3D.cpp:172
std::string getBillboardMode() const
Returns the billboard mode.
Definition Sprite3D.cpp:305
void play(int start, int end, float fps)
Run a frame animation.
Definition Sprite3D.cpp:162
void setCamera(graphics::Camera3D *camera)
Borrow the camera used for screen alignment, including pitch/roll.
Definition Sprite3D.cpp:189
~Sprite3D()
Sprite 3 d.
Definition Sprite3D.cpp:40
void setSize(float width, float height)
Billboard size in world units.
Definition Sprite3D.cpp:250
int getFrameCount() const
Total grid frames.
Definition Sprite3D.cpp:148
void getFrame(float &u0, float &v0, float &u1, float &v1) const
Returns the frame.
Definition Sprite3D.cpp:122
void stop()
Stops .
Definition Sprite3D.cpp:169
void setVisible(bool visible)
Sets the visible.
Definition Sprite3D.cpp:270
void setFrameIndex(int index)
Jump to a grid frame index, clamped within the grid.
Definition Sprite3D.cpp:150
float getPositionX() const
Returns the position x.
Definition Sprite3D.cpp:246
void setTint(float r, float g, float b, float a=1.f)
Color multiplier (alpha participates in cutout discards).
Definition Sprite3D.cpp:260
graphics::Texture * getTexture() const
Returns the texture.
Definition Sprite3D.cpp:113
float getWidth() const
Returns the width.
Definition Sprite3D.cpp:257
void setDepthWrite(bool enabled)
Whether opaque/cutout texels write depth (required for DOF focus). Default true — matches HD2DURP Dop...
Definition Sprite3D.cpp:283
void setBillboardMode(const std::string &mode)
Billboard orientation mode.
Definition Sprite3D.cpp:295
void setFrame(float u0, float v0, float u1, float v1)
Direct atlas sub-rect frame selection (0..1 UV).
Definition Sprite3D.cpp:115
void setFrameGrid(int columns, int rows)
Configure a sprite-sheet grid (frame 0 = top-left); stops the previous clip.
Definition Sprite3D.cpp:137
void setFlipY(bool flip)
Sets the flip y.
Definition Sprite3D.cpp:132
void setPivot(float x, float y)
Set the image-space pivot; (0,0) is visible top-left, (1,1) bottom-right.
Definition Sprite3D.cpp:230
void setTexture(graphics::Texture *texture)
Albedo texture (a sprite frame / sheet / animation image).
Definition Sprite3D.cpp:106
float getAlphaCutoff() const
Returns the alpha cutoff.
Definition Sprite3D.cpp:281
int getFrameGridRows() const
Returns the frame grid rows.
Definition Sprite3D.cpp:147
bool getVisible() const
Returns the visible.
Definition Sprite3D.cpp:274
void setDoubleSided(bool enabled)
Render both faces of the billboard quad (default true).
Definition Sprite3D.cpp:289
void setAlphaCutoff(float cutoff)
Alpha cutout threshold for the masked material (DopFix-style depth write).
Definition Sprite3D.cpp:276
Sprite3D()
Sprite 3 d.
bool enabled