8#include "zeroerr/assert.h"
13#include <unordered_map>
36 throw Exception(
"Sprite2D.setSize: width/height must be >= 0");
37 sprite()->width =
width;
64 else if (
blend ==
"premultiplied" ||
blend ==
"premultiplied_alpha")
68 "Sprite2D.setBlend: expected alpha|additive|opaque|premultiplied|multiply");
71 switch (sprite()->
blend) {
76 default:
return "alpha";
80 sprite()->anchorX =
x;
81 sprite()->anchorY =
y;
86 sprite()->flipX = horizontal;
87 sprite()->flipY = vertical;
92 setSize(
float(sourceW),
float(sourceH));
93 sprite()->trimW = trimW;
94 sprite()->trimH = trimH;
120 float z =
d->zoom <= 0.f ? 1e-4f :
d->zoom;
121 return (screenX - viewW * 0.5f) /
z +
d->x;
126 float z =
d->zoom <= 0.f ? 1e-4f :
d->zoom;
127 return (screenY - viewH * 0.5f) /
z +
d->y;
132 float z =
d->zoom <= 0.f ? 1e-4f :
d->zoom;
133 return (worldX -
d->x) *
z + viewW * 0.5f;
138 float z =
d->zoom <= 0.f ? 1e-4f :
d->zoom;
139 return (worldY -
d->y) *
z + viewH * 0.5f;
148float clampZoom(
float z) {
return z <= 0.f ? 1e-4f :
z; }
159ViewCam fromEntity(Camera2D *ent) {
162 auto d = ent->data();
168 v.ambientR =
d->ambientR;
169 v.ambientG =
d->ambientG;
170 v.ambientB =
d->ambientB;
174void applyCamera(
float wx,
float wy,
float ww,
float wh,
const ViewCam &cam,
int viewW,
int viewH,
175 float &
sx,
float &
sy,
float &sw,
float &sh) {
185 const float z = clampZoom(cam.zoom);
187 sx = (
wx - cam.x) *
z +
float(viewW) * 0.5f;
188 sy = (
wy - cam.y) *
z +
float(viewH) * 0.5f;
194 Light2D::Data *data =
nullptr;
198void collectLights(Canvas *canvasKey, std::vector<PackedLight> &out) {
200 if (ecs::current()->getManager<Light2D>() ==
nullptr)
return;
201 auto view = ecs::View<Light2D, Light2D::Data>();
202 for (
auto it =
view.begin(); it !=
view.end(); ++it) {
204 if (!
d->enabled)
continue;
205 if (
d->volumetricOnly)
continue;
206 if (
d->canvas != canvasKey)
continue;
209 pl.isPoint = (
d->type !=
"dir");
212 std::stable_sort(out.begin(), out.end(), [](
const PackedLight &
a,
const PackedLight &
b) {
213 if (a.isPoint != b.isPoint) return a.isPoint && !b.isPoint;
214 return a.data->intensity > b.data->intensity;
220Lighting2DUBO packLights(
const std::vector<PackedLight> &
lights,
const ViewCam &cam,
int viewW,
223 ubo.ambient = glm::vec4(cam.ambientR, cam.ambientG, cam.ambientB, 0.f);
224 ubo.meta = glm::vec4(
float(
lights.size()),
float(viewW),
float(viewH), 0.f);
225 for (
size_t i = 0; i <
lights.size(); ++i) {
226 const auto *
d =
lights[i].data;
227 Light2DGpu &
g = ubo.lights[i];
228 g.color = glm::vec4(
d->r *
d->intensity,
d->g *
d->intensity,
d->b *
d->intensity, 1.f);
229 g.spot = glm::vec4(0.f, 0.f, -2.f, -2.f);
231 float lx =
d->x, ly =
d->y;
232 float lr =
d->radius;
234 const float z = clampZoom(cam.zoom);
235 lx = (
d->x - cam.x) *
z +
float(viewW) * 0.5f;
236 ly = (
d->y - cam.y) *
z +
float(viewH) * 0.5f;
241 if (
d->type ==
"spot") {
242 float dx =
d->dx,
dy =
d->dy;
243 const float len = std::sqrt(
dx *
dx +
dy *
dy);
251 float cosOuter = 0.f, cosInner = 0.f;
253 g.spot = glm::vec4(
dx,
dy, cosOuter, cosInner);
255 g.posRadius = glm::vec4(lx, ly, 0.f, lr);
257 float dx =
d->dx,
dy =
d->dy;
258 const float len = std::sqrt(
dx *
dx +
dy *
dy);
266 g.posRadius = glm::vec4(
dx,
dy, 0.f, 0.f);
272Color modulateUnlit(
Color base,
float cx,
float cy,
bool receiveLight,
const Lighting2DUBO &ubo) {
273 if (!receiveLight)
return base;
274 glm::vec3 lit(ubo.ambient.r, ubo.ambient.g, ubo.ambient.b);
275 const int count = int(ubo.meta.x + 0.5f);
276 for (
int i = 0; i <
count; ++i) {
277 const auto &L = ubo.lights[i];
278 glm::vec3 col(L.color.r, L.color.g, L.color.b);
279 if (L.posRadius.w <= 0.f) {
281 if (L.spot.z <= -1.5f) {
286 const float dx = L.posRadius.x -
cx;
287 const float dy = L.posRadius.y -
cy;
288 const float dist = std::sqrt(
dx *
dx +
dy *
dy);
289 float atten = 1.f - dist / std::max(L.posRadius.w, 1.f);
290 if (atten < 0.f) atten = 0.f;
292 if (L.spot.z > -1.5f) {
293 const float fx =
cx - L.posRadius.x;
294 const float fy =
cy - L.posRadius.y;
295 const float fl = std::sqrt(fx * fx + fy * fy);
296 float spotAtten = 1.f;
298 const float beamLen =
299 std::sqrt(L.spot.x * L.spot.x + L.spot.y * L.spot.y);
300 if (beamLen > 1e-6f) {
301 const float cosTheta = (fx * L.spot.x + fy * L.spot.y) / (fl * beamLen);
302 const float t = (cosTheta - L.spot.z) / std::max(L.spot.w - L.spot.z, 1e-4f);
303 spotAtten = std::clamp(
t, 0.f, 1.f);
304 spotAtten = spotAtten * spotAtten * (3.f - 2.f * spotAtten);
314 return Color(base.r * lit.r, base.g * lit.g, base.b * lit.b, base.a);
320 std::unordered_map<Canvas *, Camera2D *> defaultCam;
321 if (ecs::current()->getManager<Camera2D>() !=
nullptr) {
322 auto camView = ecs::View<Camera2D, Camera2D::Data>();
323 for (
auto it = camView.begin(); it != camView.end(); ++it) {
325 if (!data->active)
continue;
326 ASSERT(data->entity !=
nullptr);
327 if (!data->entity)
continue;
329 if (defaultCam.find(
key) == defaultCam.end()) defaultCam[
key] = data->entity;
333 auto view = ecs::View<Renderable2D, Renderable2D::Transform2D, Renderable2D::Sprite>();
334 for (
auto it =
view.begin(); it !=
view.end(); ++it) {
336 if (!sp->visible)
continue;
340 auto found = defaultCam.find(sp->canvas);
341 camEnt = (
found != defaultCam.end()) ?
found->second :
nullptr;
344 ViewCam cam = fromEntity(camEnt);
346 item.
x = xf->x + float(sp->offsetX) * xf->sx;
347 item.
y = xf->y + float(sp->offsetY) * xf->sy;
348 item.
w = float(sp->trimW > 0 ? sp->trimW : sp->width) * xf->sx;
349 item.
h = float(sp->trimH > 0 ? sp->trimH : sp->height) * xf->sy;
350 item.
depthY = xf->y + sp->height * xf->sy;
353 ? (sp->anchorX * sp->width - float(sp->offsetX)) /
float(sp->trimW)
356 ? (sp->anchorY * sp->height - float(sp->offsetY)) /
float(sp->trimH)
358 item.
flipX = sp->flipX;
359 item.
flipY = sp->flipY;
360 item.
color =
Color(sp->r, sp->g, sp->b, sp->a);
361 item.
layer = sp->layer;
362 item.
blend = sp->blend;
364 item.
normal = sp->normalTexture;
365 item.
quad = sp->quad;
378 item.
litPath = sp->receiveLight && sp->normalTexture !=
nullptr && sp->texture !=
nullptr &&
379 sp->shader ==
nullptr;
393 std::unordered_map<Canvas *, Camera2D *> defaultCam;
394 if (ecs::current()->getManager<Camera2D>() !=
nullptr) {
395 auto camView = ecs::View<Camera2D, Camera2D::Data>();
396 for (
auto it = camView.begin(); it != camView.end(); ++it) {
398 if (!data->active)
continue;
399 if (!data->entity)
continue;
401 if (defaultCam.find(
key) == defaultCam.end()) defaultCam[
key] = data->entity;
407 for (
size_t i = 0; i < items.size(); ++i) {
408 Canvas *next = items[i].canvas;
409 if (i == 0 || next !=
current) {
412 auto defIt = defaultCam.find(next);
413 if (defIt != defaultCam.end()) {
414 groupCam = fromEntity(defIt->second);
415 clearCol = groupCam.clearColor;
416 }
else if (items[i].camValid) {
417 groupCam.valid =
true;
418 groupCam.x = items[i].camX;
419 groupCam.y = items[i].camY;
420 groupCam.zoom = items[i].camZoom;
421 groupCam.clearColor = items[i].camClear;
422 groupCam.ambientR = items[i].camAmbientR;
423 groupCam.ambientG = items[i].camAmbientG;
424 groupCam.ambientB = items[i].camAmbientB;
425 clearCol = groupCam.clearColor;
431 if (present) gfx.
clear(clearCol, std::nullopt, std::nullopt);
434 int viewW = next ? next->getWidth() : gfx.
getWidth();
435 int viewH = next ? next->getHeight() : gfx.
getHeight();
436 std::vector<PackedLight> packed;
437 collectLights(next, packed);
438 currentLights = packLights(packed, groupCam, viewW, viewH);
442 const auto &it = items[i];
448 cam.zoom = it.camZoom;
449 }
else if (it.camera) {
450 cam = fromEntity(it.camera);
452 int viewW = it.canvas ? it.canvas->getWidth() : gfx.
getWidth();
453 int viewH = it.canvas ? it.canvas->getHeight() : gfx.
getHeight();
454 float sx,
sy, sw, sh;
455 applyCamera(it.x, it.y, it.w, it.h, cam, viewW, viewH,
sx,
sy, sw, sh);
457 float u0 = it.hasUV ? it.u0 : 0.f;
458 float v0 = it.hasUV ? it.v0 : 0.f;
459 float u1 = it.hasUV ? it.u1 : 1.f;
460 float v1 = it.hasUV ? it.v1 : 1.f;
461 if (!it.hasUV && it.quad && it.texture)
462 it.quad->getUV(it.texture->getWidth(), it.texture->getHeight(), u0, v0, u1, v1);
463 if (it.flipX) std::swap(u0, u1);
464 if (it.flipY) std::swap(v0, v1);
466 const float pivotX =
sx + sw * it.anchorX;
467 const float pivotY =
sy + sh * it.anchorY;
468 const float radians = it.rotation * 3.14159265358979323846f / 180.f;
469 const float ox = sw * (0.5f - it.anchorX);
470 const float oy = sh * (0.5f - it.anchorY);
471 const float centerX = pivotX +
ox * std::cos(radians) -
oy * std::sin(radians);
472 const float centerY = pivotY +
ox * std::sin(radians) +
oy * std::cos(radians);
474 if (it.sceneColorDistortion) {
479 u0, v0, u1, v1, it.distortionStrength,
480 it.color.a, it.rotatedUV);
481 }
else if (it.litPath) {
484 if (it.rotation != 0.f) {
487 u1, v1, it.color, it.blend);
490 gfx.
drawTexturedRectLitUV(it.texture, it.normal,
sx,
sy, sw, sh, u0, v0, u1, v1, it.color, it.blend);
492 }
else if (it.texture) {
494 if (it.receiveLight && !it.normal)
495 c = modulateUnlit(
c,
sx + sw * 0.5f,
sy + sh * 0.5f,
true, currentLights);
498 if (it.rotation != 0.f) {
500 it.shader ?
"shader" :
"textured");
502 centerY, sw, sh, it.rotation, u0, v0, u1,
503 v1,
c, it.rotatedUV, it.blend);
506 gfx.
drawTexturedRectShaderUV(it.texture, it.shader,
sx,
sy, sw, sh, u0, v0, u1, v1,
c,
507 it.rotatedUV, it.blend);
512 c = modulateUnlit(
c,
sx + sw * 0.5f,
sy + sh * 0.5f,
true, currentLights);
514 if (it.rotation != 0.f)
532 std::vector<DrawItem2D> items;
std::vector< eve::ProcgenProbeDesc > lights
EVENGINE_API_FOUNDATION public API.
RAII pass scope for C++ call sites.
Declarative 2D camera (viewport center + zoom).
float screenToWorldY(float screenX, float screenY, float viewW, float viewH)
float worldToScreenY(float worldX, float worldY, float viewW, float viewH)
float worldToScreenX(float worldX, float worldY, float viewW, float viewH)
float screenToWorldX(float screenX, float screenY, float viewW, float viewH)
Convert screen pixel (origin top-left of viewport) to world coordinates. viewW/viewH are the current ...
void setAmbient(float r, float g, float b)
void setPosition(float x, float y)
World-space look-at center and zoom (1 = identity).
virtual void clear(std::optional< Color > color, std::optional< int > stencil, std::optional< double > depth)=0
Clears .
int getWidth() const
Returns the width.
virtual void drawTexturedRectShaderUV(Texture *texture, Shader *shader, float x, float y, float w, float h, float u0, float v0, float u1, float v1, const Color &color, bool rotatedUV=false, BlendMode blend=BlendMode::Alpha)=0
UV draw with an explicit Shader (nullptr = default textured pipeline).
virtual void drawTexturedRectLitUVRotated(Texture *albedo, Texture *normal, float cx, float cy, float w, float h, float degrees, float u0, float v0, float u1, float v1, const Color &color, BlendMode blend=BlendMode::Alpha)=0
Lit 2D draw rotated degrees clockwise (screen Y-down) around (cx, cy). Fragment tangent frame is rebu...
Color getBackgroundColor() const
Returns the background color.
virtual void setLighting2D(const Lighting2DUBO &ubo)=0
Upload per-frame / per-canvas 2D lighting constants for subsequent lit draws.
virtual SceneColorDistortionStatus drawSceneColorDistortionUVRotated(Texture *displacement, float cx, float cy, float w, float h, float degrees, float u0, float v0, float u1, float v1, float strengthPixels, float opacity, bool rotatedUV=false)
Draws scene color distortion uv rotated.
virtual void drawSolidRect(float x, float y, float w, float h, float r, float g, float b, float a=1.f)
RGBA-float overload matching the script-facing drawSolidRect name.
virtual void drawTexturedRectShaderUVRotated(Texture *texture, Shader *shader, float cx, float cy, float w, float h, float degrees, float u0, float v0, float u1, float v1, const Color &color, bool rotatedUV=false, BlendMode blend=BlendMode::Alpha)=0
UV draw rotated degrees clockwise (screen Y-down) around the rect center. texture may be null → solid...
virtual void drawSolidRectRotated(float cx, float cy, float w, float h, float degrees, const Color &color, BlendMode blend=BlendMode::Alpha)=0
Rotated solid quad degrees clockwise (screen Y-down) around (cx, cy).
virtual void setCanvas(Canvas *canvas)=0
nullptr or this → screen. Switching flushes pending draws to the previous target.
virtual void present()=0
Present.
virtual void drawTexturedRectLitUV(Texture *albedo, Texture *normal, float x, float y, float w, float h, float u0, float v0, float u1, float v1, const Color &color, BlendMode blend=BlendMode::Alpha)=0
Lit 2D draw (albedo + normal map). Uses Lighting2DUBO from setLighting2D. normal may be null → treate...
int getHeight() const
Returns the height.
Pixel-space sub-rectangle of a Texture (atlas cell / sprite frame). UV conversion uses the texture's ...
void getUV(int texW, int texH, float &u0, float &v0, float &u1, float &v1) const
Convert pixel rect to normalized UVs for a texture of size texW×texH.
static void render(Graphics &gfx)
Full sprite pass + present (existing tests / sprite-only scenes).
static void collectSprites(std::vector< DrawItem2D > &out)
Append visible Renderable2D sprites into a shared queue.
static void drawItems(Graphics &gfx, std::vector< DrawItem2D > &items, bool present)
Sort and draw items. If present=true, calls gfx.present() at the end. Map::render uses present=false ...
void setCastOcclusion(bool cast)
Enable or disable volumetric occlusion casting.
void setVisible(bool visible)
Include or exclude the sprite from collection.
float getScaleY()
Return Y scale.
float getRotation()
Return center rotation in degrees.
void setBlend(const std::string &blend)
Set blend mode (alpha or additive).
void setScale(float sx, float sy)
Set independent X/Y scale.
void setPosition(float x, float y)
Set world-space position in pixels.
Texture * getTexture()
Return the assigned texture.
float getX()
Return world-space X position.
bool getReceiveLight()
Return whether the sprite receives 2D lights.
bool getFlipX()
Return horizontal mirror state.
void release() override
Entity.
void setNormalTexture(Texture *texture)
Assign an optional tangent-space normal map for the GPU lit2d path.
void setFrameLayout(int sourceW, int sourceH, int trimW, int trimH, int offsetX, int offsetY)
Apply trimmed-frame layout while preserving the original canvas origin.
void setRotation(float degrees)
Set center rotation in degrees.
void setLayer(int layer)
Set integer painter-order layer.
void setColor(float r, float g, float b, float a=1.f)
Set RGBA tint.
Quad * getQuad()
Return the assigned UV quad.
void setTexture(Texture *texture)
Assign the borrowed sprite texture.
float getScaleX()
Return X scale.
float getWidth()
Return unscaled width.
void setFlip(bool horizontal, bool vertical)
Mirror atlas UVs without changing transform scale.
Texture * getNormalTexture()
Return the assigned normal map, or nullptr when unlit/flat. @ownership Returns a borrowed Texture*; t...
std::string getBlend()
Return blend mode name.
bool getVisible()
Return whether the sprite is visible.
bool getCastOcclusion()
Return whether the sprite casts volumetric occlusion.
void setQuad(Quad *quad)
Assign the borrowed UV quad.
int getLayer()
Return painter-order layer.
void setAnchor(float x, float y)
Set normalized transform pivot; (0,0) top-left, (0.5,0.5) center.
float getAnchorY()
Return normalized vertical pivot.
float getY()
Return world-space Y position.
float getAnchorX()
Return normalized horizontal pivot.
void setSize(float width, float height)
Set unscaled sprite dimensions in pixels.
float getHeight()
Return unscaled height.
void setReceiveLight(bool receive)
Enable or disable 2D light reception.
bool getFlipY()
Return vertical mirror state.
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
int getWidth() const
Returns the width.
int getHeight() const
Returns the height.
void rtBind(const char *kind, const char *name)
Rt bind.
void rtTarget(const char *name)
Rt target.
void rtPassBegin(const char *name)
Rt pass begin.
void rtFrameBegin()
Rt frame begin.
void rtFrameEnd()
Rt frame end.
void rtDraw(const char *api, const char *detail=nullptr)
Rt draw.
void rtPassEnd(const char *name)
Rt pass end.
卡牌游戏 UI 工具模块:工厂 + 脚本绑定入口。 功能参考 ycarowr/UiCard:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、 敌方手牌(背面/偷看)、费用不足置灰,以及可实时调节的布局...
void light2dSpotCosines(float angleDeg, float softness, float &cosOuter, float &cosInner)
Backward-compatible alias for lightSpotCosines.
eve::Color Color
RGBA color used by every graphics draw call. Lives inside eve::graphics so including a graphics heade...
void sortDrawItems2D(std::vector< DrawItem2D > &items)
Sort draw items 2 d.
Shared 2D draw queue item (sprites + tiles).
BlendMode blend
2D quad blending (alpha / additive / opaque).
float camX
Screen-space camera already resolved; if cameraEntity set, RenderSystem resolves.
float anchorX
Normalized transform pivot; (0,0) top-left, (0.5,0.5) center.
float rotation
Degrees, clockwise, around the rectangle center (screen Y-down).
Lighting2DUBO public API.
static constexpr int kMaxLights