载入中...
搜索中...
未找到
BuildingFx.cpp
浏览该文件的文档.
2
4#include "building/Ghost.h"
7#include "common/ECS.h"
8#include "graphics/Graphics.h"
9#include "graphics/Mesh.h"
12
13#include <simplesquirrel/simplesquirrel.hpp>
14
15#include <algorithm>
16#include <cmath>
17
18namespace eve::buildingfx {
19
20// Color lives in eve::graphics (see graphics/Canvas.h); keep the unqualified form.
22
24
25namespace {
26
27float toFloat(const std::string &s, float fallback) {
28 if (s.empty()) return fallback;
29 try {
30 return std::stof(s);
31 } catch (...) {
32 return fallback;
33 }
34}
35
36graphics::Graphics *gfxOrNull() { return getModInst(graphics, Graphics); }
37
38} // namespace
39
41 if (!world) return false;
42 states_[world]; // ensure entry
43 return true;
44}
45
47 if (!world) return false;
48 auto it = states_.find(world);
49 if (it == states_.end()) return false;
50 destroyAll(it->second);
51 states_.erase(it);
52 return true;
53}
54
56 return world != nullptr && states_.count(world) > 0;
57}
58
59int BuildingFx::getAttachedCount() const { return int(states_.size()); }
60
61bool BuildingFx::is3d(const building::BuildingDefinition &def) const {
62 return def.renderMode == "3d" || def.renderMode == "3D";
63}
64
65graphics::Mesh *BuildingFx::cubeMesh(graphics::Graphics *gfx) {
66 if (!gfx) return nullptr;
67 if (!cubeMesh_) cubeMesh_ = gfx->newMeshCube(1.f);
68 return cubeMesh_;
69}
70
71void BuildingFx::createVisual(WorldState &st, const building::BuildingDefinition &def,
72 const building::PlacedBuilding &pb,
73 building::PlacementWorld *world, Visual &v, float alpha) {
74 (void)st;
75 const float cellW = world->getGrid().cellW;
76 const float cellH = world->getGrid().cellH;
77 int effW = def.footprintW;
78 int effH = def.footprintH;
79 building::PlacementSystem::effectiveFootprint(def, pb.rotationDeg, &effW, &effH);
80
81 if (is3d(def)) {
82 auto *r = graphics::Renderable3D::create();
83 float wx = 0.f, wy = 0.f, wz = 0.f;
84 world->cellToWorld3D(pb.originCellX, pb.originCellY, pb.elevation, wx, wy, wz);
85 auto tr = r->transform();
86 tr->x = wx + toFloat(def.getVisual3d("offsetX"), 0.f);
87 tr->y = wy + toFloat(def.getVisual3d("offsetY"), 0.f);
88 tr->z = wz + toFloat(def.getVisual3d("offsetZ"), 0.f);
89 const float height = toFloat(def.getVisual3d("height"), 1.f);
90 tr->sx = float(effW) * cellW;
91 tr->sy = height;
92 tr->sz = float(effH) * cellH;
93 const float rad = pb.rotationDeg * 3.14159265f / 180.f;
94 if (world->getGrid().plane == grid::GridPlane::XZ) {
95 tr->yaw = rad;
96 } else {
97 tr->roll = rad;
98 }
99 auto mr = r->meshRenderer();
100 mr->mesh = cubeMesh(gfxOrNull());
101 mr->r = toFloat(def.getVisual3d("colorR"), 0.62f);
102 mr->g = toFloat(def.getVisual3d("colorG"), 0.62f);
103 mr->b = toFloat(def.getVisual3d("colorB"), 0.62f);
104 mr->a = alpha;
105 mr->visible = true;
106 mr->castShadow = true;
107 mr->receiveShadow = true;
108 v.r3d = r;
109 } else {
110 auto *r = graphics::Renderable2D::create();
111 auto tr = r->transform();
112 tr->x = pb.worldX + toFloat(def.getVisual2d("offsetX"), 0.f);
113 tr->y = pb.worldY + toFloat(def.getVisual2d("offsetY"), 0.f);
114 tr->rot = pb.rotationDeg;
115 auto sp = r->sprite();
116 sp->width = float(effW) * cellW;
117 sp->height = float(effH) * cellH;
118 sp->r = toFloat(def.getVisual2d("colorR"), 0.72f);
119 sp->g = toFloat(def.getVisual2d("colorG"), 0.72f);
120 sp->b = toFloat(def.getVisual2d("colorB"), 0.72f);
121 sp->a = alpha;
122 sp->layer = int(toFloat(def.getVisual2d("layer"), 0.f));
123 sp->visible = true;
124 const std::string texPath = def.getVisual2d("texture");
125 if (!texPath.empty()) {
126 if (auto *gfx = gfxOrNull()) {
127 if (graphics::Texture *tex = gfx->newTextureFromFile(texPath)) {
128 sp->texture = tex;
129 }
130 }
131 }
132 v.r2d = r;
133 }
134}
135
136void BuildingFx::updateVisual(const building::BuildingDefinition &def,
137 const building::PlacedBuilding &pb,
138 building::PlacementWorld *world, Visual &v) {
139 const float cellW = world->getGrid().cellW;
140 const float cellH = world->getGrid().cellH;
141 int effW = def.footprintW;
142 int effH = def.footprintH;
143 building::PlacementSystem::effectiveFootprint(def, pb.rotationDeg, &effW, &effH);
144
145 if (v.r3d) {
146 float wx = 0.f, wy = 0.f, wz = 0.f;
147 world->cellToWorld3D(pb.originCellX, pb.originCellY, pb.elevation, wx, wy, wz);
148 auto tr = v.r3d->transform();
149 tr->x = wx + toFloat(def.getVisual3d("offsetX"), 0.f);
150 tr->y = wy + toFloat(def.getVisual3d("offsetY"), 0.f);
151 tr->z = wz + toFloat(def.getVisual3d("offsetZ"), 0.f);
152 const float height = toFloat(def.getVisual3d("height"), 1.f);
153 tr->sx = float(effW) * cellW;
154 tr->sy = height;
155 tr->sz = float(effH) * cellH;
156 const float rad = pb.rotationDeg * 3.14159265f / 180.f;
157 if (world->getGrid().plane == grid::GridPlane::XZ) {
158 tr->yaw = rad;
159 } else {
160 tr->roll = rad;
161 }
162 } else if (v.r2d) {
163 auto tr = v.r2d->transform();
164 tr->x = pb.worldX + toFloat(def.getVisual2d("offsetX"), 0.f);
165 tr->y = pb.worldY + toFloat(def.getVisual2d("offsetY"), 0.f);
166 tr->rot = pb.rotationDeg;
167 auto sp = v.r2d->sprite();
168 sp->width = float(effW) * cellW;
169 sp->height = float(effH) * cellH;
170 }
171}
172
173void BuildingFx::destroyVisual(Visual &v) {
174 if (v.r2d) {
175 ecs::DestroyEntity(v.r2d);
176 v.r2d = nullptr;
177 }
178 if (v.r3d) {
179 ecs::DestroyEntity(v.r3d);
180 v.r3d = nullptr;
181 }
182}
183
184void BuildingFx::setVisible(Visual &v, bool visible) {
185 if (v.r2d) v.r2d->sprite()->visible = visible;
186 if (v.r3d) v.r3d->meshRenderer()->visible = visible;
187}
188
189void BuildingFx::destroyAll(WorldState &st) {
190 for (auto &kv : st.visuals) destroyVisual(kv.second);
191 st.visuals.clear();
192 destroyVisual(st.ghost);
193 destroyVisual(st.cursor);
194 st.ghostBuildingId.clear();
195 for (graphics::Renderable3D *line : st.gridLines) {
196 if (line) ecs::DestroyEntity(line);
197 }
198 st.gridLines.clear();
199 st.gridLineCount = -1;
200}
201
203 if (!world) return;
204 auto it = states_.find(world);
205 if (it == states_.end()) return;
206 WorldState &st = it->second;
207
208 std::unordered_map<int, Visual> next;
209 const int n = world->getBuildingCount();
210 for (int i = 0; i < n; ++i) {
211 const int inst = world->getBuildingInstanceAt(i);
212 auto bit = world->buildings().find(inst);
213 if (bit == world->buildings().end()) continue;
214 const auto &pb = bit->second;
217 if (!def) continue;
218 auto existing = st.visuals.find(inst);
219 if (existing != st.visuals.end()) {
220 next[inst] = existing->second;
221 updateVisual(*def, pb, world, next[inst]);
222 } else {
223 Visual v;
224 createVisual(st, *def, pb, world, v, 1.f);
225 next[inst] = v;
226 }
227 }
228 for (auto &kv : st.visuals) {
229 if (next.count(kv.first) == 0) destroyVisual(kv.second);
230 }
231 st.visuals = std::move(next);
232}
233
235 auto it = states_.find(world);
236 return it == states_.end() ? 0 : int(it->second.visuals.size());
237}
238
240 if (!world || !ghost) {
241 hideGhost(world);
242 return;
243 }
244 auto it = states_.find(world);
245 if (it == states_.end()) return;
246 WorldState &st = it->second;
249 if (!def) return;
250
251 if (st.ghostBuildingId != ghost->getBuildingId()) {
252 destroyVisual(st.ghost);
253 destroyVisual(st.cursor);
254 st.ghostBuildingId = ghost->getBuildingId();
255 }
256 if (!st.ghost.r2d && !st.ghost.r3d) {
257 // 用 ghost 当前位姿构造临时 PlacedBuilding 以复用 createVisual。
259 pb.buildingId = ghost->getBuildingId();
260 pb.originCellX = ghost->getCellX();
261 pb.originCellY = ghost->getCellY();
262 pb.worldX = ghost->getWorldX();
263 pb.worldY = ghost->getWorldY();
264 pb.elevation = ghost->getElevation();
265 pb.rotationDeg = ghost->getRotationDeg();
266 createVisual(st, *def, pb, world, st.ghost, 0.5f);
267 }
268 {
270 pb.buildingId = ghost->getBuildingId();
271 pb.originCellX = ghost->getCellX();
272 pb.originCellY = ghost->getCellY();
273 pb.worldX = ghost->getWorldX();
274 pb.worldY = ghost->getWorldY();
275 pb.elevation = ghost->getElevation();
276 pb.rotationDeg = ghost->getRotationDeg();
277 updateVisual(*def, pb, world, st.ghost);
278 }
279
280 const bool valid = ghost->isValid();
281 const float cr = valid ? 0.25f : 0.90f;
282 const float cg = valid ? 0.85f : 0.25f;
283 const float cb = valid ? 0.35f : 0.22f;
284 if (st.ghost.r2d) {
285 auto sp = st.ghost.r2d->sprite();
286 sp->r = cr;
287 sp->g = cg;
288 sp->b = cb;
289 }
290 if (st.ghost.r3d) {
291 auto mr = st.ghost.r3d->meshRenderer();
292 mr->r = cr;
293 mr->g = cg;
294 mr->b = cb;
295 }
296 setVisible(st.ghost, true);
297
298 // 占地光标。
299 if (!st.cursor.r2d && !st.cursor.r3d) {
300 const float cellW = world->getGrid().cellW;
301 const float cellH = world->getGrid().cellH;
302 int effW = def->footprintW;
303 int effH = def->footprintH;
305 if (is3d(*def)) {
306 auto *r = graphics::Renderable3D::create();
307 auto tr = r->transform();
308 float wx = 0.f, wy = 0.f, wz = 0.f;
309 world->cellToWorld3D(ghost->getCellX(), ghost->getCellY(), ghost->getElevation(), wx,
310 wy, wz);
311 tr->x = wx;
312 tr->y = wy + 0.03f;
313 tr->z = wz;
314 tr->sx = float(effW) * cellW;
315 tr->sy = 0.04f;
316 tr->sz = float(effH) * cellH;
317 auto mr = r->meshRenderer();
318 mr->mesh = cubeMesh(gfxOrNull());
319 mr->r = cr;
320 mr->g = cg;
321 mr->b = cb;
322 mr->a = 0.35f;
323 st.cursor.r3d = r;
324 } else {
325 auto *r = graphics::Renderable2D::create();
326 auto tr = r->transform();
327 tr->x = ghost->getWorldX();
328 tr->y = ghost->getWorldY();
329 auto sp = r->sprite();
330 sp->width = float(effW) * cellW;
331 sp->height = float(effH) * cellH;
332 sp->r = cr;
333 sp->g = cg;
334 sp->b = cb;
335 sp->a = 0.35f;
336 st.cursor.r2d = r;
337 }
338 } else {
339 const float cellW = world->getGrid().cellW;
340 const float cellH = world->getGrid().cellH;
341 int effW = def->footprintW;
342 int effH = def->footprintH;
344 if (st.cursor.r3d) {
345 auto tr = st.cursor.r3d->transform();
346 float wx = 0.f, wy = 0.f, wz = 0.f;
347 world->cellToWorld3D(ghost->getCellX(), ghost->getCellY(), ghost->getElevation(), wx,
348 wy, wz);
349 tr->x = wx;
350 tr->y = wy + 0.03f;
351 tr->z = wz;
352 tr->sx = float(effW) * cellW;
353 tr->sz = float(effH) * cellH;
354 auto mr = st.cursor.r3d->meshRenderer();
355 mr->r = cr;
356 mr->g = cg;
357 mr->b = cb;
358 } else if (st.cursor.r2d) {
359 auto tr = st.cursor.r2d->transform();
360 tr->x = ghost->getWorldX();
361 tr->y = ghost->getWorldY();
362 auto sp = st.cursor.r2d->sprite();
363 sp->width = float(effW) * cellW;
364 sp->height = float(effH) * cellH;
365 sp->r = cr;
366 sp->g = cg;
367 sp->b = cb;
368 }
369 }
370 setVisible(st.cursor, true);
371}
372
374 if (!world) return;
375 auto it = states_.find(world);
376 if (it == states_.end()) return;
377 WorldState &st = it->second;
378 setVisible(st.ghost, false);
379 setVisible(st.cursor, false);
380}
381
383 if (!world) return;
384 states_[world].gridVisible = visible;
385}
386
388 auto it = states_.find(world);
389 return it != states_.end() && it->second.gridVisible;
390}
391
393 if (!world || !gfx) return;
394 auto it = states_.find(world);
395 if (it == states_.end() || !it->second.gridVisible) return;
396 const float cellW = world->getGrid().cellW;
397 const float cellH = world->getGrid().cellH;
398 for (int y = 0; y < world->getHeight(); ++y) {
399 for (int x = 0; x < world->getWidth(); ++x) {
400 float px = 0.f, py = 0.f;
401 world->cellToWorldPlane(x, y, px, py);
402 gfx->drawSolidRect(px, py, cellW - 1.f, cellH - 1.f,
403 Color{0.75f, 0.78f, 0.85f, 0.06f});
404 }
405 }
406}
407
408void BuildingFx::rebuildGridLines(WorldState &st, building::PlacementWorld *world,
409 graphics::Graphics *gfx, float height) {
410 const int w = world->getWidth();
411 const int h = world->getHeight();
412 const float cellW = world->getGrid().cellW;
413 const float cellH = world->getGrid().cellH;
414 const int want = (w + 1) + (h + 1);
415 if (int(st.gridLines.size()) == want && st.gridLineCount == want) return;
416 for (graphics::Renderable3D *line : st.gridLines) {
417 if (line) ecs::DestroyEntity(line);
418 }
419 st.gridLines.clear();
420 graphics::Mesh *mesh = cubeMesh(gfx);
421 const bool xz = world->getGrid().plane == grid::GridPlane::XZ;
422 const float xExtent = float(w) * cellW;
423 const float zExtent = float(h) * cellH;
424 for (int x = 0; x <= w; ++x) {
425 auto *r = graphics::Renderable3D::create();
426 auto tr = r->transform();
427 float px = 0.f, py = 0.f;
428 world->cellToWorldPlane(x, 0, px, py);
429 tr->x = px;
430 tr->y = height;
431 tr->z = xz ? zExtent * 0.5f : 0.f;
432 tr->sx = 0.03f;
433 tr->sy = 0.02f;
434 tr->sz = zExtent;
435 auto mr = r->meshRenderer();
436 mr->mesh = mesh;
437 mr->r = 0.85f;
438 mr->g = 0.88f;
439 mr->b = 0.95f;
440 mr->a = 0.5f;
441 st.gridLines.push_back(r);
442 }
443 for (int y = 0; y <= h; ++y) {
444 auto *r = graphics::Renderable3D::create();
445 auto tr = r->transform();
446 float px = 0.f, py = 0.f;
447 world->cellToWorldPlane(0, y, px, py);
448 tr->x = xz ? xExtent * 0.5f : 0.f;
449 tr->y = height;
450 tr->z = py;
451 tr->sx = xExtent;
452 tr->sy = 0.02f;
453 tr->sz = 0.03f;
454 auto mr = r->meshRenderer();
455 mr->mesh = mesh;
456 mr->r = 0.85f;
457 mr->g = 0.88f;
458 mr->b = 0.95f;
459 mr->a = 0.5f;
460 st.gridLines.push_back(r);
461 }
462 st.gridLineCount = want;
463}
464
466 float height) {
467 if (!world || !gfx) return;
468 auto it = states_.find(world);
469 if (it == states_.end()) return;
470 WorldState &st = it->second;
471 rebuildGridLines(st, world, gfx, height);
472 for (graphics::Renderable3D *line : st.gridLines) {
473 if (line) line->meshRenderer()->visible = st.gridVisible;
474 }
475}
476
477void BuildingFx::expose(ssq::Table &table) {
478 auto cls = table.addClass(name, BuildingFx::create, false);
479 expose(cls);
480}
481
482void BuildingFx::expose(ssq::Class &cls) {
483 cls.addFunc("attach", &BuildingFx::attach);
484 cls.addFunc("detach", &BuildingFx::detach);
485 cls.addFunc("isAttached", &BuildingFx::isAttached);
486 cls.addFunc("getAttachedCount", &BuildingFx::getAttachedCount);
487 cls.addFunc("sync", &BuildingFx::sync);
488 cls.addFunc("getVisualCount", &BuildingFx::getVisualCount);
489 cls.addFunc("updateGhost", &BuildingFx::updateGhost);
490 cls.addFunc("hideGhost", &BuildingFx::hideGhost);
491 cls.addFunc("setGridVisible", &BuildingFx::setGridVisible);
492 cls.addFunc("getGridVisible", &BuildingFx::getGridVisible);
493 cls.addFunc("drawGrid2D", &BuildingFx::drawGrid2D);
494 cls.addFunc("drawGrid3D", &BuildingFx::drawGrid3D);
495}
496
497} // namespace eve::buildingfx
int line
HSQOBJECT cls
Definition ECS.cpp:21
int y
Definition Grass.cpp:135
float height
Definition Grass.cpp:235
int x
Definition Grass.cpp:135
glm::vec3 n
Definition Grass.cpp:64
int h
int w
std::vector< Colorf > px
float tr
#define Module_IMPL(ModuleName, newExpr)
Definition Module.h:24
#define getModInst(N, T)
Definition Module.h:36
Renderable3D::MeshRenderer * mr
Mesh * mesh
bool valid
const char * name
Definition RockMesh.cpp:21
int v
uint32_t s
Definition Weather.cpp:28
static const BuildingDefinition * find(const std::string &id)
float getElevation() const
Definition Ghost.h:30
int getCellY() const
Definition Ghost.h:24
float getWorldY() const
Definition Ghost.h:28
int getCellX() const
Definition Ghost.h:23
bool isValid() const
Definition Ghost.h:37
float getWorldX() const
Definition Ghost.h:27
float getRotationDeg() const
Definition Ghost.h:33
std::string getBuildingId() const
Definition Ghost.h:20
static void effectiveFootprint(const BuildingDefinition &def, float rotationDeg, int *outW, int *outH)
旋转后的占地宽高(cardinal 90/270 交换)。
格子型建筑放置世界(脚本可直接操作)。
int getWidth() const
尺寸 / 格子大小 / 原点。
const std::unordered_map< int, PlacedBuilding > & buildings() const
const grid::GridConfig & getGrid() const
int getBuildingInstanceAt(int index) const
按插入顺序取实例 id。
void cellToWorldPlane(int cellX, int cellY, float &px, float &py) const
void cellToWorld3D(int cellX, int cellY, float elevation, float &worldX, float &worldY, float &worldZ) const
bool attach(building::PlacementWorld *world)
int getVisualCount(building::PlacementWorld *world) const
bool getGridVisible(building::PlacementWorld *world) const
void hideGhost(building::PlacementWorld *world)
void setGridVisible(building::PlacementWorld *world, bool visible)
bool isAttached(building::PlacementWorld *world) const
void drawGrid3D(building::PlacementWorld *world, graphics::Graphics *gfx, float height=0.f)
void updateGhost(building::PlacementWorld *world, building::Ghost *ghost)
bool detach(building::PlacementWorld *world)
void drawGrid2D(building::PlacementWorld *world, graphics::Graphics *gfx)
void sync(building::PlacementWorld *world)
Mesh * newMeshCube(float size=1.f)
Procedural cube (edge length size, centered at origin, outward CCW for RH Y-up), with per-face normal...
Definition Graphics.cpp:790
virtual Texture * newTextureFromFile(const std::string &filename)=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.
GPU mesh handle (+ optional CPU morph targets).
Definition Mesh.h:18
glm::vec4 Color
RGBA color used by every graphics draw call. Lives inside eve::graphics so including a graphics heade...
Definition Color.h:13
建筑模板(进程级注册表中的定义)。
已放置的建筑实例。