载入中...
搜索中...
未找到
TileMap3D.cpp
浏览该文件的文档.
1#include "hd2d/Hd2d.h"
2
3#include "common/Exception.h"
4#include "graphics/Graphics.h"
5#include "graphics/Mesh.h"
7#include "map/TileLayer.h"
8
9#include <algorithm>
10#include <cmath>
11#include <cstdint>
12#include <glm/glm.hpp>
13#include <vector>
14
15namespace eve::hd2d {
16namespace {
17
18constexpr float kPi = 3.14159265358979323846f;
19
25void atlasUvForGid(const map::TileLayer::Tileset &set, uint32_t gid, float &u0, float &v0,
26 float &u1, float &v1) {
27 const int cols = std::max(1, set.columns);
28 const int tileW = std::max(1, set.tileW);
29 const int tileH = std::max(1, set.tileH);
30 const int margin = std::max(0, set.margin);
31 const int spacing = std::max(0, set.spacing);
32 const int cell = int(gid) - set.firstGid;
33 if (cell < 0) {
34 u0 = 0.f;
35 v0 = 0.f;
36 u1 = 1.f;
37 v1 = 1.f;
38 return;
39 }
40 const int cx = cell % cols;
41 const int cy = cell / cols;
42 const float texW = set.texture ? float(std::max(1, set.texture->getWidth())) : float(tileW);
43 const float texH = set.texture ? float(std::max(1, set.texture->getHeight())) : float(tileH);
44 const float px0 = float(margin + cx * (tileW + spacing));
45 const float py0 = float(margin + cy * (tileH + spacing));
46 u0 = px0 / texW;
47 v0 = py0 / texH;
48 u1 = (px0 + float(tileW)) / texW;
49 v1 = (py0 + float(tileH)) / texH;
50}
51
52} // namespace
53
54// ---------------------------------------------------------------------------
55// TileMap3D
56// ---------------------------------------------------------------------------
57
59 if (depth < 0.f) throw eve::Exception("TileMap3D.setSideDepth: depth must be >= 0");
60 sideDepth_ = depth;
61}
62float TileMap3D::getSideDepth() const { return sideDepth_; }
64 if (scale < 0.f) throw eve::Exception("TileMap3D.setHeightScale: scale must be >= 0");
65 heightScale_ = scale;
66}
67float TileMap3D::getHeightScale() const { return heightScale_; }
68void TileMap3D::setWallUV(float u0, float v0, float u1, float v1) {
69 wallU0_ = u0;
70 wallV0_ = v0;
71 wallU1_ = u1;
72 wallV1_ = v1;
73}
74void TileMap3D::setTint(float r, float g, float b, float a) {
75 tintR_ = std::max(0.f, std::min(1.f, r));
76 tintG_ = std::max(0.f, std::min(1.f, g));
77 tintB_ = std::max(0.f, std::min(1.f, b));
78 tintA_ = std::max(0.f, std::min(1.f, a));
79}
80
82 if (!gfx) throw eve::Exception("TileMap3D.buildMesh: null graphics");
83 if (!layer) throw eve::Exception("TileMap3D.buildMesh: null tile layer");
84
85 const int mapW = layer->getMapWidth();
86 const int mapH = layer->getMapHeight();
87 if (mapW <= 0 || mapH <= 0) throw eve::Exception("TileMap3D.buildMesh: empty tile layer");
88
89 const float tileW = std::max(1.f, layer->getTileWidth());
90 const float tileH = std::max(1.f, layer->getTileHeight());
91 const float hx = tileW * 0.5f;
92 const float hz = tileH * 0.5f;
93
94 auto set = layer->tileset(); // ecs::ComponentRef<Tileset>; always valid
95 const bool haveSet = set->texture != nullptr && set->columns > 0;
96
97 std::vector<float> pos;
98 std::vector<float> nrm;
99 std::vector<float> uv;
100 std::vector<uint32_t> idx;
101
102 // Unit-cube face topology (RH Y-up, outward CCW): faces = -Z,+Z,-X,+X,-Y,+Y.
103 // Face 5 (+Y) is the textured top; faces 0..4 are extruded side walls.
104 static const int kQuads[6][4] = {
105 {0, 1, 2, 3}, {5, 4, 7, 6}, {4, 0, 3, 7},
106 {1, 5, 6, 2}, {4, 5, 1, 0}, {3, 2, 6, 7},
107 };
108 static const glm::vec3 kFaces[6] = {
109 {0.f, 0.f, -1.f}, {0.f, 0.f, 1.f}, {-1.f, 0.f, 0.f},
110 {1.f, 0.f, 0.f}, {0.f, -1.f, 0.f}, {0.f, 1.f, 0.f},
111 };
112
113 const auto pushBoxTile = [&](float cx, float cz, float topY, float botY, float tu0, float tv0, float tu1, float tv1,
114 uint32_t flags) {
115 // Corners (index matches kQuads[kFace]).
116 const glm::vec3 corners[8] = {
117 {cx - hx, botY, cz - hz}, {cx + hx, botY, cz - hz}, {cx + hx, topY, cz - hz},
118 {cx - hx, topY, cz - hz}, {cx - hx, botY, cz + hz}, {cx + hx, botY, cz + hz},
119 {cx + hx, topY, cz + hz}, {cx - hx, topY, cz + hz},
120 };
121 const float sideU0 = wallU0_, sideV0 = wallV0_, sideU1 = wallU1_, sideV1 = wallV1_;
122 for (int f = 0; f < 6; ++f) {
123 if (sideDepth_ == 0.f && f != 5) continue;
124 const uint32_t base = uint32_t(pos.size() / 3);
125 const bool top = (f == 5);
126 const float su0 = top ? tu0 : sideU0;
127 const float sv0 = top ? tv0 : sideV0;
128 const float su1 = top ? tu1 : sideU1;
129 const float sv1 = top ? tv1 : sideV1;
130 const float us[4] = {su0, su1, su1, su0};
131 const float vs[4] = {sv0, sv0, sv1, sv1};
132 const glm::vec3 &n = kFaces[f];
133 for (int k = 0; k < 4; ++k) {
134 const glm::vec3 &p = corners[kQuads[f][k]];
135 pos.insert(pos.end(), {p.x, p.y, p.z});
136 nrm.insert(nrm.end(), {n.x, n.y, n.z});
137 float u = us[k], v = vs[k];
138 if (top) {
139 // Invert Tiled's diagonal-then-horizontal/vertical transform
140 // to map a destination corner back into the source atlas.
141 float s = (k == 1 || k == 2) ? 1.f : 0.f;
142 float t = k >= 2 ? 1.f : 0.f;
143 if (flags & 0x80000000u) s = 1.f - s;
144 if (flags & 0x40000000u) t = 1.f - t;
145 if (flags & 0x20000000u) std::swap(s, t);
146 u = tu0 + s * (tu1 - tu0);
147 v = tv0 + t * (tv1 - tv0);
148 }
149 uv.insert(uv.end(), {u, v});
150 }
151 idx.insert(idx.end(), {base, base + 2, base + 1, base, base + 3, base + 2});
152 }
153 };
154
155 tileCount_ = 0;
156
157 for (int ty = 0; ty < mapH; ++ty) {
158 for (int tx = 0; tx < mapW; ++tx) {
159 const uint32_t raw = uint32_t(layer->getTile(tx, ty));
160 const uint32_t gid = eve::map::tileGid(raw);
161 if (gid == 0) continue;
162
163 // World X/Z from the layer's 2D projection; elevation from metadata.
164 const float wx = layer->tileToWorldX(tx, ty) + hx;
165 const float wz = layer->tileToWorldY(tx, ty) + hz;
166 const float elev = layer->getTileDataNumber(int(gid), "height") * heightScale_;
167 const float topY = elev;
168 const float botY = elev - sideDepth_;
169
170 float tu0 = 0.f, tv0 = 0.f, tu1 = 1.f, tv1 = 1.f;
171 if (haveSet) atlasUvForGid(*set, gid, tu0, tv0, tu1, tv1);
172
173 pushBoxTile(wx, wz, topY, botY, tu0, tv0, tu1, tv1, raw);
174 ++tileCount_;
175 }
176 }
177
178 if (pos.empty()) return nullptr;
179 return gfx->newMeshFromArrays(pos.data(), nrm.data(), uv.data(), int(pos.size() / 3),
180 idx.data(), int(idx.size()));
181}
182
185 if (!gfx) throw eve::Exception("TileMap3D.buildRenderable: null graphics");
186 if (!layer) throw eve::Exception("TileMap3D.buildRenderable: null tile layer");
187
189 if (!mesh) throw eve::Exception("TileMap3D.buildRenderable: no non-empty tiles");
190
191 auto *renderable = graphics::Renderable3D::create();
192 if (!renderable) throw eve::Exception("TileMap3D.buildRenderable: Renderable3D create failed");
193 renderable->setMesh(mesh);
194 if (layer->tileset()->texture) renderable->setTexture(layer->tileset()->texture);
195 renderable->setTint(tintR_, tintG_, tintB_, tintA_);
196 return renderable;
197}
198
199} // namespace eve::hd2d
SQInteger top
const std::string & s
float cx
Definition CardTypes.cpp:33
float cy
Definition CardTypes.cpp:34
float uv
glm::vec4 p[6]
int cols
tensor::Graph g
Definition GpuGraph.cpp:7
float u
Definition Grass.cpp:233
glm::vec3 n
Definition Grass.cpp:63
double r
float v
std::array< float, 3 > scale
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
TileLayer * layer
int idx
float f
float t
Mesh * mesh
Cell cell
int spacing
int tileW
int margin
int tileH
std::uint32_t depth
float wz
float wx
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)....
GPU mesh handle (+ optional CPU morph targets).
Definition Mesh.h:25
EVENGINE_API_BACKENDS public API.
void setWallUV(float u0, float v0, float u1, float v1)
UV sub-rectangle of the atlas used for extruded side walls.
Definition TileMap3D.cpp:68
graphics::Mesh * buildMesh(graphics::Graphics *gfx, map::TileLayer *layer)
Build and upload the terrain mesh from a tile layer.
Definition TileMap3D.cpp:81
void setTint(float r, float g, float b, float a=1.f)
Tint multiplier applied to the whole terrain.
Definition TileMap3D.cpp:74
float getSideDepth() const
Returns the side depth.
Definition TileMap3D.cpp:62
void setHeightScale(float scale)
World units per unit of tile "height" metadata (default 1).
Definition TileMap3D.cpp:63
float getHeightScale() const
Returns the height scale.
Definition TileMap3D.cpp:67
void setSideDepth(float depth)
Downward side-wall depth in world units (default 6).
Definition TileMap3D.cpp:58
graphics::Renderable3D * buildRenderable(graphics::Graphics *gfx, map::TileLayer *layer)
Build a scene-ready Renderable3D carrying the extruded terrain. The returned entity is already regist...
ECS tile layer entity. Script mutates tile GIDs / tileset / draw; TileRenderSystem batch-draws atlas ...
Definition TileLayer.h:32
uint32_t tileGid(uint32_t raw)
Strip Tiled flip / rotate flags; keep low 28 bits.
Definition TileLayer.h:383