载入中...
搜索中...
未找到
TileSystem.cpp
浏览该文件的文档.
1#include "map/TileSystem.h"
2#include "map/TileLayer.h"
3#include "map/TileConfig.h"
5#include "graphics/Graphics.h"
7#include "graphics/Canvas.h"
9#include "common/Module.h"
10#include "common/Profile.h"
11
12#include <unordered_set>
13#include <vector>
14#include <algorithm>
15#include <cmath>
16
17namespace eve::map {
18namespace {
19
20int gLastVisibleTileCount = 0;
21int gLastCustomVisualCount = 0;
22int gLastAtlasCount = 0;
23int gLastVisitedChunkCount = 0;
24int gLastVisitedCellCount = 0;
25double gAnimationTimeMs = 0.0;
26
27struct ViewCam {
28 float x = 0.f;
29 float y = 0.f;
30 float zoom = 1.f;
31 bool valid = false;
32};
33
34ViewCam fromEntity(graphics::Camera2D *ent) {
35 ViewCam v;
36 if (!ent) return v;
37 auto d = ent->data();
38 v.valid = true;
39 v.x = d->x;
40 v.y = d->y;
41 v.zoom = d->zoom;
42 return v;
43}
44
46Color solidForGid(uint32_t gid, const Color &tint) {
47 const uint32_t h = gid * 2654435761u;
48 const float r = ((h >> 0) & 255u) / 255.f;
49 const float g = ((h >> 8) & 255u) / 255.f;
50 const float b = ((h >> 16) & 255u) / 255.f;
51 return Color{r * tint.r, g * tint.g, b * tint.b, tint.a};
52}
53
54const TileLayer::Tileset::Atlas *atlasForGid(const TileLayer::Tileset &ts, uint32_t gid) {
55 const TileLayer::Tileset::Atlas *best = nullptr;
56 for (const auto &atlas : ts.atlases) {
57 if (gid >= uint32_t(atlas.firstGid) && (!best || atlas.firstGid > best->firstGid)) best = &atlas;
58 }
59 return best;
60}
61
62bool atlasUV(const TileLayer::Tileset::Atlas &atlas, uint32_t gid, float &u0, float &v0, float &u1, float &v1) {
63 if (!atlas.texture || atlas.columns <= 0 || atlas.tileW <= 0 || atlas.tileH <= 0) return false;
64 if (gid < uint32_t(atlas.firstGid)) return false;
65 const int local = int(gid) - atlas.firstGid;
66 const int col = local % atlas.columns;
67 const int row = local / atlas.columns;
68 const float iw = float(atlas.texture->getWidth());
69 const float ih = float(atlas.texture->getHeight());
70 if (iw <= 0.f || ih <= 0.f) return false;
71
72 const float px = float(atlas.margin + col * (atlas.tileW + atlas.spacing));
73 const float py = float(atlas.margin + row * (atlas.tileH + atlas.spacing));
74 u0 = px / iw;
75 v0 = py / ih;
76 u1 = (px + float(atlas.tileW)) / iw;
77 v1 = (py + float(atlas.tileH)) / ih;
78 return true;
79}
80
81const TileLayer::Tileset::Visual *visualForGid(const TileLayer::Tileset &ts, uint32_t gid) {
82 for (const auto &visual : ts.visuals)
83 if (visual.gid == int(gid)) return &visual;
84 return nullptr;
85}
86
87bool visualUV(const TileLayer::Tileset::Atlas &atlas, const TileLayer::Tileset::Visual &visual, float &u0, float &v0,
88 float &u1, float &v1) {
89 if (!atlas.texture || visual.width <= 0 || visual.height <= 0) return false;
90 const float iw = float(atlas.texture->getWidth());
91 const float ih = float(atlas.texture->getHeight());
92 if (iw <= 0.f || ih <= 0.f) return false;
93 u0 = float(visual.x) / iw;
94 v0 = float(visual.y) / ih;
95 u1 = float(visual.x + visual.width) / iw;
96 v1 = float(visual.y + visual.height) / ih;
97 return true;
98}
99
100bool subtileUV(const TileLayer::Tileset::Atlas &atlas, const TileLayer::Tileset::Visual::Subtile &part, float &u0,
101 float &v0, float &u1, float &v1) {
102 if (!atlas.texture || part.width <= 0 || part.height <= 0) return false;
103 const float iw = float(atlas.texture->getWidth());
104 const float ih = float(atlas.texture->getHeight());
105 if (iw <= 0.f || ih <= 0.f) return false;
106 u0 = float(part.x) / iw;
107 v0 = float(part.y) / ih;
108 u1 = float(part.x + part.width) / iw;
109 v1 = float(part.y + part.height) / ih;
110 return true;
111}
112
113const TileLayer::Tileset::Visual::SubtileFrame *subtileFrame(const TileLayer::Tileset::Visual &visual) {
114 if (visual.subtileFrames.empty()) return nullptr;
115 int total = 0;
116 for (const auto &frame : visual.subtileFrames) total += std::max(1, frame.durationMs);
117 int cursor = total > 0 ? int(std::fmod(gAnimationTimeMs, double(total))) : 0;
118 for (const auto &frame : visual.subtileFrames) {
119 cursor -= std::max(1, frame.durationMs);
120 if (cursor < 0) return &frame;
121 }
122 return &visual.subtileFrames.back();
123}
124
125int64_t fileModtime(const std::string &path) {
126 auto *fs = eve::ModuleManager::getInstance<eve::filesystem::Filesystem>("Filesystem");
127 if (!fs) fs = eve::filesystem::Filesystem::create();
129 if (!fs->getInfo(path, info)) return -1;
130 return info.modtime;
131}
132
133uint32_t animatedGid(const TileLayer::Tileset &ts, uint32_t gid) {
134 for (const auto &animation : ts.animations) {
135 if (animation.gid != int(gid) || animation.frames.empty()) continue;
136 int total = 0;
137 for (const auto &frame : animation.frames) total += std::max(1, frame.durationMs);
138 if (total <= 0) return gid;
139 int cursor = int(std::fmod(gAnimationTimeMs, double(total)));
140 for (const auto &frame : animation.frames) {
141 cursor -= std::max(1, frame.durationMs);
142 if (cursor < 0) return uint32_t(frame.gid);
143 }
144 }
145 return gid;
146}
147
148void applyTiledTransform(graphics::DrawItem2D &item, uint32_t raw) {
149 const bool horizontal = (raw & 0x80000000u) != 0;
150 const bool vertical = (raw & 0x40000000u) != 0;
151 const bool diagonal = (raw & 0x20000000u) != 0;
152 if (!diagonal) {
153 item.flipX = horizontal;
154 item.flipY = vertical;
155 return;
156 }
157 item.rotation = horizontal ? 90.f : 270.f;
158 item.flipX = horizontal == vertical;
159 item.flipY = false;
160}
161
162bool chunkVisible(const TileLayer::Config &cfg, const TileLayer::Tileset &ts, const ViewCam &cam,
163 int viewWidth, int viewHeight, int x0, int y0, int x1, int y1, float scaleX,
164 float scaleY) {
165 if (!cam.valid || viewWidth <= 0 || viewHeight <= 0) return true;
166 float minX = 1e30f, minY = 1e30f, maxX = -1e30f, maxY = -1e30f;
167 const int xs[] = {x0, std::max(x0, x1 - 1)};
168 const int ys[] = {y0, std::max(y0, y1 - 1)};
169 for (int y : ys) {
170 for (int x : xs) {
171 float wx = 0.f, wy = 0.f;
172 tileToWorld(cfg, x, y, wx, wy);
173 minX = std::min(minX, wx);
174 minY = std::min(minY, wy);
175 maxX = std::max(maxX, wx + cfg.tileW * scaleX);
176 maxY = std::max(maxY, wy + cfg.tileH * scaleY);
177 }
178 }
179 float expandX = cfg.tileW * scaleX;
180 float expandY = cfg.tileH * scaleY;
181 for (const auto &visual : ts.visuals) {
182 expandX = std::max(expandX, float(visual.width) * scaleX + std::fabs(visual.pivotX) * scaleX);
183 expandY = std::max(expandY, float(visual.height) * scaleY + std::fabs(visual.pivotY) * scaleY);
184 }
185 minX -= expandX;
186 minY -= expandY;
187 maxX += expandX;
188 maxY += expandY;
189 const float zoom = cam.zoom > 0.f ? cam.zoom : 0.0001f;
190 const float left = cam.x - float(viewWidth) * 0.5f / zoom;
191 const float right = cam.x + float(viewWidth) * 0.5f / zoom;
192 const float top = cam.y - float(viewHeight) * 0.5f / zoom;
193 const float bottom = cam.y + float(viewHeight) * 0.5f / zoom;
194 return maxX >= left && minX <= right && maxY >= top && minY <= bottom;
195}
196
197} // namespace
198
199void TileRenderSystem::collect(std::vector<graphics::DrawItem2D> &out) { collect(out, 0, 0); }
200
201void TileRenderSystem::collect(std::vector<graphics::DrawItem2D> &out, int viewWidth,
202 int viewHeight) {
203 gLastVisibleTileCount = 0;
204 gLastCustomVisualCount = 0;
205 gLastAtlasCount = 0;
206 gLastVisitedChunkCount = 0;
207 gLastVisitedCellCount = 0;
208 if (ecs::current()->getManager<TileLayer>() == nullptr) return;
209
210 std::unordered_set<graphics::Texture *> atlases;
211
214 for (auto it = view.begin(); it != view.end(); ++it) {
215 auto [cfg, tiles, ts, draw] = *it;
216 if (!draw->visible || cfg->mapW <= 0 || cfg->mapH <= 0) continue;
217 if (tiles->gids.size() < size_t(cfg->mapW * cfg->mapH)) continue;
218
219 const Color &tint = draw->tint;
220 const ViewCam cam = fromEntity(draw->camera);
221 const float scaleX = draw->visualScaleX > 0.f ? draw->visualScaleX : 1.f;
222 const float scaleY = draw->visualScaleY > 0.f ? draw->visualScaleY : 1.f;
223
224 const int chunkColumns = std::max(1, tiles->chunkColumns);
225 const int chunkRows = std::max(1, tiles->chunkRows);
226 for (int cy = 0; cy < chunkRows; ++cy) {
227 for (int cx = 0; cx < chunkColumns; ++cx) {
228 const int chunkIndex = cy * chunkColumns + cx;
229 if (!tiles->chunks.empty() &&
230 (chunkIndex >= int(tiles->chunks.size()) ||
231 tiles->chunks[size_t(chunkIndex)].nonEmpty == 0))
232 continue;
233 const int x0 = cx * TileLayer::Tiles::kChunkSize;
234 const int y0 = cy * TileLayer::Tiles::kChunkSize;
235 const int x1 = std::min(cfg->mapW, x0 + TileLayer::Tiles::kChunkSize);
236 const int y1 = std::min(cfg->mapH, y0 + TileLayer::Tiles::kChunkSize);
237 if (!chunkVisible(*cfg, *ts, cam, viewWidth, viewHeight, x0, y0, x1, y1, scaleX,
238 scaleY))
239 continue;
240 ++gLastVisitedChunkCount;
241 for (int ty = y0; ty < y1; ++ty) {
242 for (int tx = x0; tx < x1; ++tx) {
243 ++gLastVisitedCellCount;
244 const uint32_t raw = tiles->gids[size_t(ty * cfg->mapW + tx)];
245 const uint32_t gid = animatedGid(*ts, tileGid(raw));
246 if (gid == 0) continue;
247
249 tileToWorld(*cfg, tx, ty, item.x, item.y);
250 item.w = cfg->tileW * scaleX;
251 item.h = cfg->tileH * scaleY;
252 item.depthY = tileToDepthY(*cfg, tx, ty);
253 item.layer = draw->layer;
254 item.canvas = draw->canvas;
255 item.camera = draw->camera;
256 item.camValid = cam.valid;
257 item.camX = cam.x;
258 item.camY = cam.y;
259 item.camZoom = cam.zoom;
260 item.receiveLight = draw->receiveLight;
261 item.litPath = false;
262 applyTiledTransform(item, raw);
263
264 float u0, v0, u1, v1;
265 const auto* visual = visualForGid(*ts, gid);
266 const auto *atlas = atlasForGid(*ts, gid);
267 const auto *subtiles = visual ? subtileFrame(*visual) : nullptr;
268 if (subtiles && atlas) {
269 for (const auto &part : subtiles->parts) {
270 graphics::DrawItem2D quarter = item;
271 if (!subtileUV(*atlas, part, u0, v0, u1, v1)) continue;
272 float centerX = (part.offsetX + float(part.width) * 0.5f) * scaleX
273 - cfg->tileW * scaleX * 0.5f;
274 float centerY = (part.offsetY + float(part.height) * 0.5f) * scaleY
275 - cfg->tileH * scaleY * 0.5f;
276 if (raw & 0x20000000u) std::swap(centerX, centerY);
277 if (raw & 0x80000000u) centerX = -centerX;
278 if (raw & 0x40000000u) centerY = -centerY;
279 quarter.x += cfg->tileW * scaleX * 0.5f + centerX
280 - float(part.width) * scaleX * 0.5f;
281 quarter.y += cfg->tileH * scaleY * 0.5f + centerY
282 - float(part.height) * scaleY * 0.5f;
283 quarter.w = float(part.width) * scaleX;
284 quarter.h = float(part.height) * scaleY;
285 quarter.texture = atlas->texture;
286 quarter.hasUV = true;
287 quarter.u0 = u0;
288 quarter.v0 = v0;
289 quarter.u1 = u1;
290 quarter.v1 = v1;
291 quarter.color = tint;
292 quarter.litPath = quarter.receiveLight && quarter.texture != nullptr;
293 out.push_back(quarter);
294 ++gLastCustomVisualCount;
295 }
296 ++gLastVisibleTileCount;
297 if (atlas->texture) atlases.insert(atlas->texture);
298 continue;
299 }
300 if (visual && atlas && visualUV(*atlas, *visual, u0, v0, u1, v1)) {
301 ++gLastCustomVisualCount;
302 item.x -= visual->pivotX * scaleX;
303 item.y -= visual->pivotY * scaleY;
304 item.w = float(visual->width) * scaleX;
305 item.h = float(visual->height) * scaleY;
306 item.depthY += visual->sortBias;
307 item.texture = atlas->texture;
308 item.hasUV = true;
309 item.u0 = u0;
310 item.v0 = v0;
311 item.u1 = u1;
312 item.v1 = v1;
313 item.color = tint;
314 } else if (atlas && atlasUV(*atlas, gid, u0, v0, u1, v1)) {
315 item.texture = atlas->texture;
316 item.hasUV = true;
317 item.u0 = u0;
318 item.v0 = v0;
319 item.u1 = u1;
320 item.v1 = v1;
321 item.color = tint;
322 } else {
323 item.texture = nullptr;
324 item.color = solidForGid(gid, tint);
325 }
326 item.litPath = item.receiveLight && item.texture != nullptr;
327 out.push_back(item);
328 ++gLastVisibleTileCount;
329 if (item.texture) atlases.insert(item.texture);
330 }
331 }
332 }
333 }
334 }
335 gLastAtlasCount = int(atlases.size());
336}
337
339 if (!gfx) return;
340 std::vector<graphics::DrawItem2D> items;
341 collect(items, gfx->getWidth(), gfx->getHeight());
342 graphics::RenderSystem::drawItems(*gfx, items, false);
343}
344
345int TileRenderSystem::lastVisibleTileCount() { return gLastVisibleTileCount; }
346int TileRenderSystem::lastCustomVisualCount() { return gLastCustomVisualCount; }
347int TileRenderSystem::lastAtlasCount() { return gLastAtlasCount; }
348int TileRenderSystem::lastVisitedChunkCount() { return gLastVisitedChunkCount; }
349int TileRenderSystem::lastVisitedCellCount() { return gLastVisitedCellCount; }
350
352 EV_PROFILE_MODULE("map", "TileRenderSystem::update");
353 if (dt > 0.f && std::isfinite(dt)) gAnimationTimeMs += double(dt) * 1000.0;
354}
355
357 if (ecs::current()->getManager<TileLayer>() == nullptr) return 0;
358
359 int reloaded = 0;
360 auto view = ecs::View<TileLayer, TileLayer::Config, TileLayer::Resource>();
361 for (auto it = view.begin(); it != view.end(); ++it) {
362 auto [cfg, res] = *it;
363 if (!res->autoReload || res->path.empty() || !cfg->entity) continue;
364
365 const int64_t mt = fileModtime(res->path);
366 bool changed = mt >= 0 && mt != res->modtime;
367 for (size_t index = 0; !changed && index < res->dependencyPaths.size(); ++index) {
368 const int64_t dependencyModtime = fileModtime(res->dependencyPaths[index]);
369 const int64_t known = index < res->dependencyModtimes.size() ? res->dependencyModtimes[index] : -1;
370 changed = dependencyModtime >= 0 && dependencyModtime != known;
371 }
372 if (!changed) continue;
373 if (reloadConfigFile(cfg->entity, nullptr)) ++reloaded;
374 }
375 return reloaded;
376}
377
378} // namespace eve::map
SQInteger top
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float cx
Definition CardTypes.cpp:33
float cy
Definition CardTypes.cpp:34
float py
tensor::Graph g
Definition GpuGraph.cpp:7
glm::vec4 tint
double r
float v
HexVec3 left
HexVec3 right
int h
std::vector< Colorf > px
std::string local
bool valid
MeleePoint3 b
Definition MeleeHit.cpp:41
MeshVfxBatchedDraw draw
bool diagonal
std::string path
Definition PlayHost.cpp:110
#define EV_PROFILE_MODULE(module, name)
Profile the enclosing scope, tagged with a module for grouping.
Definition Profile.h:140
float d
glm::mat4 view
float zoom
std::map< Cell, int > best
std::size_t cursor
std::vector< TileLayer::Tileset::Visual > visuals
std::vector< TileLayer::Tileset::Animation > animations
uint32_t index
std::vector< WfcTile > tiles
Definition WfcSimple.cpp:22
float bottom
float wx
float wy
int getWidth() const
Returns the width.
Definition Graphics.h:502
int getHeight() const
Returns the height.
Definition Graphics.h:504
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 ...
static int poll()
Polls .
ECS tile layer entity. Script mutates tile GIDs / tileset / draw; TileRenderSystem batch-draws atlas ...
Definition TileLayer.h:32
static int lastVisibleTileCount()
Number of non-empty visible tiles collected by the last pass.
static void collect(std::vector< graphics::DrawItem2D > &out)
Append non-empty visible tiles into a shared 2D draw queue.
static int lastVisitedCellCount()
Last visited cell count.
static int lastAtlasCount()
Number of distinct atlas textures referenced by the last pass.
static void render(graphics::Graphics *gfx)
Collect + draw tiles only (no present). Null gfx is a no-op.
static int lastCustomVisualCount()
Number of collected tiles using irregular per-GID metadata.
static void update(float dt)
Advance TileSet animation clocks once per map update.
static int lastVisitedChunkCount()
Spatial-index work counters for deterministic performance regressions.
放置世界:格子占用(多通道)+ 地形语义 + 已放置建筑实例。 行为由 PlacementSystem 提供;本类暴露便于脚本绑定的薄封装方法。 坐标换算统一走 eve::grid(支持 rectang...
bool reloadConfigFile(TileLayer *layer, std::string *error)
Reloads config file.
uint32_t tileGid(uint32_t raw)
Strip Tiled flip / rotate flags; keep low 28 bits.
Definition TileLayer.h:383
void tileToWorld(const TileLayer::Config &cfg, int tx, int ty, float &wx, float &wy)
Converts a tile index to world pixels using the layer's projection.
float tileToDepthY(const TileLayer::Config &cfg, int tx, int ty)
Painter's-algorithm depth for the tile under the layer's projection.
WidgetDesc row(std::vector< WidgetDesc > children, std::string id)
Horizontal elastic layout row.
Definition Widget.cpp:679
glm::vec4 Color
Render-neutral RGBA color shared by graphics-facing modules.
Definition RenderTypes.h:8
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:54
Config public API.
Definition TileLayer.h:41
static constexpr int kChunkSize
Definition TileLayer.h:63
glm::uvec4 info