载入中...
搜索中...
未找到
RenderSystem.cpp
浏览该文件的文档.
2#include "graphics/Graphics.h"
3#include "graphics/Canvas.h"
4#include "graphics/Light.h"
5#include "graphics/Quad.h"
7#include "zeroerr/assert.h"
8
9#include <algorithm>
10#include <cmath>
11#include <cstdint>
12#include <unordered_map>
13#include <vector>
14
15namespace eve::graphics {
16
17void Camera2D::setAmbient(float r, float g, float b) {
18 auto d = data();
19 d->ambientR = r;
20 d->ambientG = g;
21 d->ambientB = b;
22}
23
24void Camera2D::setPosition(float x, float y) {
25 auto d = data();
26 d->x = x;
27 d->y = y;
28}
29
30float Camera2D::getX() { return data()->x; }
31float Camera2D::getY() { return data()->y; }
32
33void Camera2D::setZoom(float zoom) { data()->zoom = zoom; }
34float Camera2D::getZoom() { return data()->zoom; }
35
36float Camera2D::screenToWorldX(float screenX, float /*screenY*/, float viewW, float /*viewH*/) {
37 auto d = data();
38 float z = d->zoom <= 0.f ? 1e-4f : d->zoom;
39 return (screenX - viewW * 0.5f) / z + d->x;
40}
41
42float Camera2D::screenToWorldY(float /*screenX*/, float screenY, float /*viewW*/, float viewH) {
43 auto d = data();
44 float z = d->zoom <= 0.f ? 1e-4f : d->zoom;
45 return (screenY - viewH * 0.5f) / z + d->y;
46}
47
48float Camera2D::worldToScreenX(float worldX, float /*worldY*/, float viewW, float /*viewH*/) {
49 auto d = data();
50 float z = d->zoom <= 0.f ? 1e-4f : d->zoom;
51 return (worldX - d->x) * z + viewW * 0.5f;
52}
53
54float Camera2D::worldToScreenY(float /*worldX*/, float worldY, float /*viewW*/, float viewH) {
55 auto d = data();
56 float z = d->zoom <= 0.f ? 1e-4f : d->zoom;
57 return (worldY - d->y) * z + viewH * 0.5f;
58}
59
60void Renderable2D::setCastOcclusion(bool cast) { sprite()->castOcclusion = cast; }
61
62bool Renderable2D::getCastOcclusion() { return sprite()->castOcclusion; }
63
64namespace {
65
66float clampZoom(float z) { return z <= 0.f ? 1e-4f : z; }
67
68struct ViewCam {
69 float x = 0.f;
70 float y = 0.f;
71 float zoom = 1.f;
72 bool valid = false;
73 Color clearColor{0.1f, 0.1f, 0.12f, 1.f};
74 float ambientR = 0.15f, ambientG = 0.15f, ambientB = 0.18f;
75};
76
77ViewCam fromEntity(Camera2D *ent) {
78 ViewCam v;
79 if (!ent) return v;
80 auto d = ent->data();
81 v.valid = true;
82 v.x = d->x;
83 v.y = d->y;
84 v.zoom = d->zoom;
85 v.clearColor = Color(d->r, d->g, d->b, d->a);
86 v.ambientR = d->ambientR;
87 v.ambientG = d->ambientG;
88 v.ambientB = d->ambientB;
89 return v;
90}
91
92void applyCamera(float wx, float wy, float ww, float wh, const ViewCam &cam, int viewW, int viewH,
93 float &sx, float &sy, float &sw, float &sh) {
94 if (!cam.valid) {
95 sx = wx;
96 sy = wy;
97 sw = ww;
98 sh = wh;
99 return;
100 }
101 ASSERT_GT(viewW, 0);
102 ASSERT_GT(viewH, 0);
103 const float z = clampZoom(cam.zoom);
104 ASSERT_GT(z, 0.f);
105 sx = (wx - cam.x) * z + float(viewW) * 0.5f;
106 sy = (wy - cam.y) * z + float(viewH) * 0.5f;
107 sw = ww * z;
108 sh = wh * z;
109}
110
111struct PackedLight {
112 Light2D::Data *data = nullptr;
113 bool isPoint = true;
114};
115
116void collectLights(Canvas *canvasKey, std::vector<PackedLight> &out) {
117 out.clear();
118 if (ecs::current()->getManager<Light2D>() == nullptr) return;
119 auto view = ecs::View<Light2D, Light2D::Data>();
120 for (auto it = view.begin(); it != view.end(); ++it) {
121 auto [d] = *it;
122 if (!d->enabled) continue;
123 if (d->canvas != canvasKey) continue;
124 PackedLight pl;
125 pl.data = d;
126 pl.isPoint = (d->type != "dir");
127 out.push_back(pl);
128 }
129 std::stable_sort(out.begin(), out.end(), [](const PackedLight &a, const PackedLight &b) {
130 if (a.isPoint != b.isPoint) return a.isPoint && !b.isPoint;
131 return a.data->intensity > b.data->intensity;
132 });
133 if (out.size() > size_t(Lighting2DUBO::kMaxLights))
134 out.resize(size_t(Lighting2DUBO::kMaxLights));
135}
136
137Lighting2DUBO packLights(const std::vector<PackedLight> &lights, const ViewCam &cam, int viewW,
138 int viewH) {
139 Lighting2DUBO ubo{};
140 ubo.ambient = glm::vec4(cam.ambientR, cam.ambientG, cam.ambientB, 0.f);
141 ubo.meta = glm::vec4(float(lights.size()), float(viewW), float(viewH), 0.f);
142 for (size_t i = 0; i < lights.size(); ++i) {
143 const auto *d = lights[i].data;
144 Light2DGpu &g = ubo.lights[i];
145 g.color = glm::vec4(d->r * d->intensity, d->g * d->intensity, d->b * d->intensity, 1.f);
146 if (lights[i].isPoint) {
147 float lx = d->x, ly = d->y;
148 float lr = d->radius;
149 if (cam.valid) {
150 const float z = clampZoom(cam.zoom);
151 lx = (d->x - cam.x) * z + float(viewW) * 0.5f;
152 ly = (d->y - cam.y) * z + float(viewH) * 0.5f;
153 lr = d->radius * z;
154 }
155 g.posRadius = glm::vec4(lx, ly, 0.f, lr);
156 } else {
157 float dx = d->dx, dy = d->dy;
158 const float len = std::sqrt(dx * dx + dy * dy);
159 if (len > 1e-6f) {
160 dx /= len;
161 dy /= len;
162 } else {
163 dx = 0.f;
164 dy = -1.f;
165 }
166 g.posRadius = glm::vec4(dx, dy, 0.f, 0.f);
167 }
168 }
169 return ubo;
170}
171
172Color modulateUnlit(Color base, float cx, float cy, bool receiveLight, const Lighting2DUBO &ubo) {
173 if (!receiveLight) return base;
174 glm::vec3 lit(ubo.ambient.r, ubo.ambient.g, ubo.ambient.b);
175 const int count = int(ubo.meta.x + 0.5f);
176 for (int i = 0; i < count; ++i) {
177 const auto &L = ubo.lights[i];
178 glm::vec3 col(L.color.r, L.color.g, L.color.b);
179 if (L.posRadius.w <= 0.f) {
180 // Directional: approximate as constant contribution for unlit sprites.
181 lit += col * 0.65f;
182 } else {
183 const float dx = L.posRadius.x - cx;
184 const float dy = L.posRadius.y - cy;
185 const float dist = std::sqrt(dx * dx + dy * dy);
186 float atten = 1.f - dist / std::max(L.posRadius.w, 1.f);
187 if (atten < 0.f) atten = 0.f;
188 atten *= atten;
189 lit += col * atten;
190 }
191 }
192 return Color(base.r * lit.r, base.g * lit.g, base.b * lit.b, base.a);
193}
194
195} // namespace
196
197void RenderSystem::collectSprites(std::vector<DrawItem2D> &out) {
198 std::unordered_map<Canvas *, Camera2D *> defaultCam;
199 if (ecs::current()->getManager<Camera2D>() != nullptr) {
200 auto camView = ecs::View<Camera2D, Camera2D::Data>();
201 for (auto it = camView.begin(); it != camView.end(); ++it) {
202 auto [data] = *it;
203 if (!data->active) continue;
204 ASSERT(data->entity != nullptr);
205 if (!data->entity) continue;
206 Canvas *key = data->canvas;
207 if (defaultCam.find(key) == defaultCam.end()) defaultCam[key] = data->entity;
208 }
209 }
210
211 auto view = ecs::View<Renderable2D, Renderable2D::Transform2D, Renderable2D::Sprite>();
212 for (auto it = view.begin(); it != view.end(); ++it) {
213 auto [xf, sp] = *it;
214 if (!sp->visible) continue;
215
216 Camera2D *camEnt = sp->camera;
217 if (!camEnt) {
218 auto found = defaultCam.find(sp->canvas);
219 camEnt = (found != defaultCam.end()) ? found->second : nullptr;
220 }
221
222 ViewCam cam = fromEntity(camEnt);
223 DrawItem2D item;
224 item.x = xf->x;
225 item.y = xf->y;
226 item.w = sp->width * xf->sx;
227 item.h = sp->height * xf->sy;
228 item.depthY = xf->y + sp->height * xf->sy;
229 item.color = Color(sp->r, sp->g, sp->b, sp->a);
230 item.layer = sp->layer;
231 item.texture = sp->texture;
232 item.normal = sp->normalTexture;
233 item.quad = sp->quad;
234 item.shader = sp->shader;
235 item.canvas = sp->canvas;
236 item.camera = camEnt;
237 item.camValid = cam.valid;
238 item.camX = cam.x;
239 item.camY = cam.y;
240 item.camZoom = cam.zoom;
241 item.camClear = cam.clearColor;
242 item.camAmbientR = cam.ambientR;
243 item.camAmbientG = cam.ambientG;
244 item.camAmbientB = cam.ambientB;
245 item.receiveLight = sp->receiveLight;
246 item.litPath = sp->receiveLight && sp->normalTexture != nullptr && sp->texture != nullptr &&
247 sp->shader == nullptr;
248 if (item.quad && item.texture) {
249 item.hasUV = true;
250 item.quad->getUV(item.texture->getWidth(), item.texture->getHeight(), item.u0, item.v0,
251 item.u1, item.v1);
252 }
253 out.push_back(item);
254 }
255}
256
257void RenderSystem::drawItems(Graphics &gfx, std::vector<DrawItem2D> &items, bool present) {
258 eve::debug::RenderPassScope pass("RenderSystem2D");
259 sortDrawItems2D(items);
260
261 std::unordered_map<Canvas *, Camera2D *> defaultCam;
262 if (ecs::current()->getManager<Camera2D>() != nullptr) {
263 auto camView = ecs::View<Camera2D, Camera2D::Data>();
264 for (auto it = camView.begin(); it != camView.end(); ++it) {
265 auto [data] = *it;
266 if (!data->active) continue;
267 if (!data->entity) continue;
268 Canvas *key = data->canvas;
269 if (defaultCam.find(key) == defaultCam.end()) defaultCam[key] = data->entity;
270 }
271 }
272
273 Canvas *current = reinterpret_cast<Canvas *>(static_cast<uintptr_t>(1));
274 Lighting2DUBO currentLights{};
275 for (size_t i = 0; i < items.size(); ++i) {
276 Canvas *next = items[i].canvas;
277 if (i == 0 || next != current) {
278 Color clearCol = gfx.getBackgroundColor();
279 ViewCam groupCam{};
280 auto defIt = defaultCam.find(next);
281 if (defIt != defaultCam.end()) {
282 groupCam = fromEntity(defIt->second);
283 clearCol = groupCam.clearColor;
284 } else if (items[i].camValid) {
285 groupCam.valid = true;
286 groupCam.x = items[i].camX;
287 groupCam.y = items[i].camY;
288 groupCam.zoom = items[i].camZoom;
289 groupCam.clearColor = items[i].camClear;
290 groupCam.ambientR = items[i].camAmbientR;
291 groupCam.ambientG = items[i].camAmbientG;
292 groupCam.ambientB = items[i].camAmbientB;
293 clearCol = groupCam.clearColor;
294 }
295
296 gfx.setCanvas(next);
297 eve::debug::rtTarget(next ? "canvas" : "screen");
298 // Only clear when presenting a full sprite pass; map-unified draws must not wipe.
299 if (present) gfx.clear(clearCol, std::nullopt, std::nullopt);
300 current = next;
301
302 int viewW = next ? next->getWidth() : gfx.getWidth();
303 int viewH = next ? next->getHeight() : gfx.getHeight();
304 std::vector<PackedLight> packed;
305 collectLights(next, packed);
306 currentLights = packLights(packed, groupCam, viewW, viewH);
307 gfx.setLighting2D(currentLights);
308 }
309
310 const auto &it = items[i];
311 ViewCam cam{};
312 if (it.camValid) {
313 cam.valid = true;
314 cam.x = it.camX;
315 cam.y = it.camY;
316 cam.zoom = it.camZoom;
317 } else if (it.camera) {
318 cam = fromEntity(it.camera);
319 }
320 int viewW = it.canvas ? it.canvas->getWidth() : gfx.getWidth();
321 int viewH = it.canvas ? it.canvas->getHeight() : gfx.getHeight();
322 float sx, sy, sw, sh;
323 applyCamera(it.x, it.y, it.w, it.h, cam, viewW, viewH, sx, sy, sw, sh);
324
325 float u0 = it.hasUV ? it.u0 : 0.f;
326 float v0 = it.hasUV ? it.v0 : 0.f;
327 float u1 = it.hasUV ? it.u1 : 1.f;
328 float v1 = it.hasUV ? it.v1 : 1.f;
329 if (!it.hasUV && it.quad && it.texture)
330 it.quad->getUV(it.texture->getWidth(), it.texture->getHeight(), u0, v0, u1, v1);
331
332 if (it.litPath) {
333 eve::debug::rtBind("texture", "albedo");
334 eve::debug::rtBind("texture", "normal");
335 eve::debug::rtDraw("drawTexturedRectLitUV", "lit2d");
336 gfx.drawTexturedRectLitUV(it.texture, it.normal, sx, sy, sw, sh, u0, v0, u1, v1,
337 it.color);
338 } else if (it.texture) {
339 Color c = it.color;
340 if (it.receiveLight && !it.normal)
341 c = modulateUnlit(c, sx + sw * 0.5f, sy + sh * 0.5f, true, currentLights);
342 eve::debug::rtBind("texture", "sprite");
343 if (it.shader) eve::debug::rtBind("shader", "custom");
344 if (it.rotation != 0.f) {
345 eve::debug::rtDraw("drawTexturedRectShaderUVRotated",
346 it.shader ? "shader" : "textured");
347 gfx.drawTexturedRectShaderUVRotated(it.texture, it.shader, sx + sw * 0.5f,
348 sy + sh * 0.5f, sw, sh, it.rotation, u0, v0, u1,
349 v1, c, it.rotatedUV, it.blend);
350 } else {
351 eve::debug::rtDraw("drawTexturedRectShaderUV", it.shader ? "shader" : "textured");
352 gfx.drawTexturedRectShaderUV(it.texture, it.shader, sx, sy, sw, sh, u0, v0, u1, v1, c,
353 it.rotatedUV, it.blend);
354 }
355 } else {
356 Color c = it.color;
357 if (it.receiveLight)
358 c = modulateUnlit(c, sx + sw * 0.5f, sy + sh * 0.5f, true, currentLights);
359 eve::debug::rtDraw("drawSolidRect", "solid");
360 if (it.rotation != 0.f)
361 gfx.drawSolidRectRotated(sx + sw * 0.5f, sy + sh * 0.5f, sw, sh, it.rotation, c,
362 it.blend);
363 else
364 gfx.drawSolidRect(sx, sy, sw, sh, c, it.blend);
365 }
366 }
367
368 if (present) {
369 gfx.setCanvas();
370 eve::debug::rtPassBegin("Present");
371 gfx.present();
372 eve::debug::rtPassEnd("Present");
373 }
374}
375
378 std::vector<DrawItem2D> items;
379 collectSprites(items);
380 drawItems(gfx, items, true);
382}
383
384} // namespace eve::graphics
float cx
Definition CardTypes.cpp:31
float cy
Definition CardTypes.cpp:32
int y
Definition Grass.cpp:135
int z
Definition Grass.cpp:135
int x
Definition Grass.cpp:135
uint32_t a
uint32_t b
uint32_t c
glm::mat4 view
Light2D::Data * data
float zoom
bool valid
Color clearColor
bool isPoint
float ambientG
float ambientR
float ambientB
int d
int v
RAII pass scope for C++ call sites.
Definition RenderTrace.h:56
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 setZoom(float zoom)
void setAmbient(float r, float g, float b)
void setPosition(float x, float y)
World-space look-at center and zoom (1 = identity).
virtual int getWidth() const =0
virtual void clear(std::optional< Color > color, std::optional< int > stencil, std::optional< double > depth)=0
virtual int getHeight() const =0
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).
Color getBackgroundColor() const
Definition Graphics.h:720
virtual void setLighting2D(const Lighting2DUBO &ubo)=0
Upload per-frame / per-canvas 2D lighting constants for subsequent lit draws.
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
virtual void drawSolidRect(float x, float y, float w, float h, const Color &color, BlendMode blend=BlendMode::Alpha)=0
Internal immediate-mode helper used by RenderSystem / Batcher.
int getHeight() const
Definition Graphics.h:254
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)=0
Lit 2D draw (albedo + normal map). Uses Lighting2DUBO from setLighting2D. normal may be null → treate...
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.
Definition Quad.cpp:18
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)
int getWidth() const
Definition Texture.h:26
int getHeight() const
Definition Texture.h:27
void rtBind(const char *kind, const char *name)
Definition RenderTrace.h:45
void rtTarget(const char *name)
Definition RenderTrace.h:42
void rtPassBegin(const char *name)
Definition RenderTrace.h:36
void rtFrameBegin()
Definition RenderTrace.h:30
void rtFrameEnd()
Definition RenderTrace.h:33
void rtDraw(const char *api, const char *detail=nullptr)
Definition RenderTrace.h:48
void rtPassEnd(const char *name)
Definition RenderTrace.h:39
卡牌游戏 UI 工具模块:工厂 + 脚本绑定入口。 功能参考 ycarowr/UiCard:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、 敌方手牌(背面/偷看)、费用不足置灰,以及可实时调节的布局...
Definition AnimTrail.h:6
glm::vec4 Color
RGBA color used by every graphics draw call. Lives inside eve::graphics so including a graphics heade...
Definition Color.h:13
void sortDrawItems2D(std::vector< DrawItem2D > &items)
Definition DrawItem2D.h:53
Shared 2D draw queue item (sprites + tiles).
Definition DrawItem2D.h:18
float camX
Screen-space camera already resolved; if cameraEntity set, RenderSystem resolves.
Definition DrawItem2D.h:47
static constexpr int kMaxLights
Definition Light.h:21