TrinityCore
Loading...
Searching...
No Matches
WorldModel.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "WorldModel.h"
19#include "VMapDefinitions.h"
20#include "MapTree.h"
21#include "ModelIgnoreFlags.h"
22#include <array>
23#include <cstring>
24
25using G3D::Vector3;
26
27template<> struct BoundsTrait<VMAP::GroupModel>
28{
29 static void getBounds(VMAP::GroupModel const& obj, G3D::AABox& out) { out = obj.GetBound(); }
30 void operator()(VMAP::GroupModel const& obj, G3D::AABox& out) const { getBounds(obj, out); }
31};
32
33namespace VMAP
34{
35 bool IntersectTriangle(MeshTriangle const& tri, std::vector<Vector3>::const_iterator points, G3D::Ray const& ray, float& distance)
36 {
37 static const float EPS = 1e-5f;
38
39 // See RTR2 ch. 13.7 for the algorithm.
40
41 const Vector3 e1 = points[tri.idx1] - points[tri.idx0];
42 const Vector3 e2 = points[tri.idx2] - points[tri.idx0];
43 const Vector3 p(ray.direction().cross(e2));
44 const float a = e1.dot(p);
45
46 if (std::fabs(a) < EPS) {
47 // Determinant is ill-conditioned; abort early
48 return false;
49 }
50
51 const float f = 1.0f / a;
52 const Vector3 s(ray.origin() - points[tri.idx0]);
53 const float u = f * s.dot(p);
54
55 if ((u < 0.0f) || (u > 1.0f)) {
56 // We hit the plane of the m_geometry, but outside the m_geometry
57 return false;
58 }
59
60 const Vector3 q(s.cross(e1));
61 const float v = f * ray.direction().dot(q);
62
63 if ((v < 0.0f) || ((u + v) > 1.0f)) {
64 // We hit the plane of the triangle, but outside the triangle
65 return false;
66 }
67
68 const float t = f * e2.dot(q);
69
70 if ((t > 0.0f) && (t < distance))
71 {
72 // This is a new hit, closer than the previous one
73 distance = t;
74
75 /* baryCoord[0] = 1.0 - u - v;
76 baryCoord[1] = u;
77 baryCoord[2] = v; */
78
79 return true;
80 }
81 // This hit is after the previous hit, so ignore it
82 return false;
83 }
84
86 {
87 public:
88 TriBoundFunc(std::vector<Vector3>& vert): vertices(vert.begin()) { }
89 void operator()(MeshTriangle const& tri, G3D::AABox& out) const
90 {
91 G3D::Vector3 lo = vertices[tri.idx0];
92 G3D::Vector3 hi = lo;
93
94 lo = (lo.min(vertices[tri.idx1])).min(vertices[tri.idx2]);
95 hi = (hi.max(vertices[tri.idx1])).max(vertices[tri.idx2]);
96
97 out = G3D::AABox(lo, hi);
98 }
99 protected:
100 const std::vector<Vector3>::const_iterator vertices;
101 };
102
103 // ===================== WmoLiquid ==================================
104
105 WmoLiquid::WmoLiquid(uint32 width, uint32 height, Vector3 const& corner, uint32 type) :
106 iTilesX(width), iTilesY(height), iCorner(corner), iType(type)
107 {
108 if (width && height)
109 {
110 iHeight = new float[(width + 1) * (height + 1)];
111 iFlags = new uint8[width * height];
112 }
113 else
114 {
115 iHeight = new float[1];
116 iFlags = nullptr;
117 }
118 }
119
120 WmoLiquid::WmoLiquid(WmoLiquid const& other) : iHeight(nullptr), iFlags(nullptr)
121 {
122 *this = other; // use assignment operator...
123 }
124
126 {
127 delete[] iHeight;
128 delete[] iFlags;
129 }
130
132 {
133 if (this == &other)
134 return *this;
135 iTilesX = other.iTilesX;
136 iTilesY = other.iTilesY;
137 iCorner = other.iCorner;
138 iType = other.iType;
139 delete[] iHeight;
140 delete[] iFlags;
141 if (other.iHeight)
142 {
143 iHeight = new float[(iTilesX+1)*(iTilesY+1)];
144 memcpy(iHeight, other.iHeight, (iTilesX+1)*(iTilesY+1)*sizeof(float));
145 }
146 else
147 iHeight = nullptr;
148 if (other.iFlags)
149 {
150 iFlags = new uint8[iTilesX * iTilesY];
151 memcpy(iFlags, other.iFlags, iTilesX * iTilesY);
152 }
153 else
154 iFlags = nullptr;
155 return *this;
156 }
157
158 bool WmoLiquid::GetLiquidHeight(Vector3 const& pos, float& liqHeight) const
159 {
160 // simple case
161 if (!iFlags)
162 {
163 liqHeight = iHeight[0];
164 return true;
165 }
166
167 float tx_f = (pos.x - iCorner.x) / LIQUID_TILE_SIZE;
168 uint32 tx = uint32(tx_f);
169 if (tx_f < 0.0f || tx >= iTilesX)
170 return false;
171 float ty_f = (pos.y - iCorner.y) / LIQUID_TILE_SIZE;
172 uint32 ty = uint32(ty_f);
173 if (ty_f < 0.0f || ty >= iTilesY)
174 return false;
175
176 // check if tile shall be used for liquid level
177 // checking for 0x08 *might* be enough, but disabled tiles always are 0x?F:
178 if ((iFlags[tx + ty * iTilesX] & 0x0F) == 0x0F)
179 return false;
180
181 // (dx, dy) coordinates inside tile, in [0, 1]^2
182 float dx = tx_f - (float)tx;
183 float dy = ty_f - (float)ty;
184
185 /* Tesselate tile to two triangles (not sure if client does it exactly like this)
186
187 ^ dy
188 |
189 1 x---------x (1, 1)
190 | (b) / |
191 | / |
192 | / |
193 | / (a) |
194 x---------x---> dx
195 0 1
196 */
197
198 uint32 const rowOffset = iTilesX + 1;
199 if (dx > dy) // case (a)
200 {
201 float sx = iHeight[tx+1 + ty * rowOffset] - iHeight[tx + ty * rowOffset];
202 float sy = iHeight[tx+1 + (ty+1) * rowOffset] - iHeight[tx+1 + ty * rowOffset];
203 liqHeight = iHeight[tx + ty * rowOffset] + dx * sx + dy * sy;
204 }
205 else // case (b)
206 {
207 float sx = iHeight[tx+1 + (ty+1) * rowOffset] - iHeight[tx + (ty+1) * rowOffset];
208 float sy = iHeight[tx + (ty+1) * rowOffset] - iHeight[tx + ty * rowOffset];
209 liqHeight = iHeight[tx + ty * rowOffset] + dx * sx + dy * sy;
210 }
211 return true;
212 }
213
215 {
216 return 2 * sizeof(uint32) +
217 sizeof(Vector3) +
218 sizeof(uint32) +
219 (iFlags ? ((iTilesX + 1) * (iTilesY + 1) * sizeof(float) + iTilesX * iTilesY) : sizeof(float));
220 }
221
223 {
224 bool result = false;
225 if (fwrite(&iTilesX, sizeof(uint32), 1, wf) == 1 &&
226 fwrite(&iTilesY, sizeof(uint32), 1, wf) == 1 &&
227 fwrite(&iCorner, sizeof(Vector3), 1, wf) == 1 &&
228 fwrite(&iType, sizeof(uint32), 1, wf) == 1)
229 {
230 if (iTilesX && iTilesY)
231 {
232 uint32 size = (iTilesX + 1) * (iTilesY + 1);
233 if (fwrite(iHeight, sizeof(float), size, wf) == size)
234 {
235 size = iTilesX * iTilesY;
236 result = fwrite(iFlags, sizeof(uint8), size, wf) == size;
237 }
238 }
239 else
240 result = fwrite(iHeight, sizeof(float), 1, wf) == 1;
241 }
242
243 return result;
244 }
245
246 bool WmoLiquid::readFromFile(FILE* rf, WmoLiquid*& out)
247 {
248 bool result = false;
249 WmoLiquid* liquid = new WmoLiquid();
250
251 if (fread(&liquid->iTilesX, sizeof(uint32), 1, rf) == 1 &&
252 fread(&liquid->iTilesY, sizeof(uint32), 1, rf) == 1 &&
253 fread(&liquid->iCorner, sizeof(Vector3), 1, rf) == 1 &&
254 fread(&liquid->iType, sizeof(uint32), 1, rf) == 1)
255 {
256 if (liquid->iTilesX && liquid->iTilesY)
257 {
258 uint32 size = (liquid->iTilesX + 1) * (liquid->iTilesY + 1);
259 liquid->iHeight = new float[size];
260 if (fread(liquid->iHeight, sizeof(float), size, rf) == size)
261 {
262 size = liquid->iTilesX * liquid->iTilesY;
263 liquid->iFlags = new uint8[size];
264 result = fread(liquid->iFlags, sizeof(uint8), size, rf) == size;
265 }
266 }
267 else
268 {
269 liquid->iHeight = new float[1];
270 result = fread(liquid->iHeight, sizeof(float), 1, rf) == 1;
271 }
272 }
273
274 if (!result)
275 delete liquid;
276 else
277 out = liquid;
278
279 return result;
280 }
281
282 void WmoLiquid::getPosInfo(uint32& tilesX, uint32& tilesY, G3D::Vector3& corner) const
283 {
284 tilesX = iTilesX;
285 tilesY = iTilesY;
286 corner = iCorner;
287 }
288
289 // ===================== GroupModel ==================================
290
292 iBound(other.iBound), iMogpFlags(other.iMogpFlags), iGroupWMOID(other.iGroupWMOID),
293 vertices(other.vertices), triangles(other.triangles), meshTree(other.meshTree), iLiquid(nullptr)
294 {
295 if (other.iLiquid)
296 iLiquid = new WmoLiquid(*other.iLiquid);
297 }
298
299 void GroupModel::setMeshData(std::vector<Vector3>&& vert, std::vector<MeshTriangle>&& tri)
300 {
301 vertices = std::move(vert);
302 triangles = std::move(tri);
303 TriBoundFunc bFunc(vertices);
304 meshTree.build(triangles, bFunc);
305 }
306
308 {
309 bool result = true;
310 uint32 chunkSize, count;
311
312 if (result && fwrite(&iBound, sizeof(G3D::AABox), 1, wf) != 1) result = false;
313 if (result && fwrite(&iMogpFlags, sizeof(uint32), 1, wf) != 1) result = false;
314 if (result && fwrite(&iGroupWMOID, sizeof(uint32), 1, wf) != 1) result = false;
315
316 // write vertices
317 if (result && fwrite("VERT", 1, 4, wf) != 4) result = false;
318 count = vertices.size();
319 chunkSize = sizeof(uint32) + sizeof(Vector3) * count;
320 if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
321 if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) result = false;
322 if (!count) // models without (collision) geometry end here, unsure if they are useful
323 return result;
324 if (result && fwrite(&vertices[0], sizeof(Vector3), count, wf) != count) result = false;
325
326 // write triangle mesh
327 if (result && fwrite("TRIM", 1, 4, wf) != 4) result = false;
328 count = triangles.size();
329 chunkSize = sizeof(uint32) + sizeof(MeshTriangle) * count;
330 if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
331 if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) result = false;
332 if (result && fwrite(&triangles[0], sizeof(MeshTriangle), count, wf) != count) result = false;
333
334 // write mesh BIH
335 if (result && fwrite("MBIH", 1, 4, wf) != 4) result = false;
336 if (result) result = meshTree.writeToFile(wf);
337
338 // write liquid data
339 if (result && fwrite("LIQU", 1, 4, wf) != 4) result = false;
340 if (!iLiquid)
341 {
342 chunkSize = 0;
343 if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
344 return result;
345 }
346 chunkSize = iLiquid->GetFileSize();
347 if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
348 if (result) result = iLiquid->writeToFile(wf);
349
350 return result;
351 }
352
354 {
355 char chunk[8];
356 bool result = true;
357 uint32 chunkSize = 0;
358 uint32 count = 0;
359 triangles.clear();
360 vertices.clear();
361 delete iLiquid;
362 iLiquid = nullptr;
363
364 if (result && fread(&iBound, sizeof(G3D::AABox), 1, rf) != 1) result = false;
365 if (result && fread(&iMogpFlags, sizeof(uint32), 1, rf) != 1) result = false;
366 if (result && fread(&iGroupWMOID, sizeof(uint32), 1, rf) != 1) result = false;
367
368 // read vertices
369 if (result && !readChunk(rf, chunk, "VERT", 4)) result = false;
370 if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
371 if (result && fread(&count, sizeof(uint32), 1, rf) != 1) result = false;
372 if (!count) // models without (collision) geometry end here, unsure if they are useful
373 return result;
374 if (result) vertices.resize(count);
375 if (result && fread(&vertices[0], sizeof(Vector3), count, rf) != count) result = false;
376
377 // read triangle mesh
378 if (result && !readChunk(rf, chunk, "TRIM", 4)) result = false;
379 if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
380 if (result && fread(&count, sizeof(uint32), 1, rf) != 1) result = false;
381 if (result) triangles.resize(count);
382 if (result && fread(&triangles[0], sizeof(MeshTriangle), count, rf) != count) result = false;
383
384 // read mesh BIH
385 if (result && !readChunk(rf, chunk, "MBIH", 4)) result = false;
386 if (result) result = meshTree.readFromFile(rf);
387
388 // write liquid data
389 if (result && !readChunk(rf, chunk, "LIQU", 4)) result = false;
390 if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
391 if (result && chunkSize > 0)
392 result = WmoLiquid::readFromFile(rf, iLiquid);
393 return result;
394 }
395
397 {
398 GModelRayCallback(std::vector<MeshTriangle> const& tris, const std::vector<Vector3> &vert):
399 vertices(vert.begin()), triangles(tris.begin()), hit(false) { }
400 bool operator()(G3D::Ray const& ray, uint32 entry, float& distance, bool /*pStopAtFirstHit*/)
401 {
402 hit = IntersectTriangle(triangles[entry], vertices, ray, distance) || hit;
403 return hit;
404 }
405 std::vector<Vector3>::const_iterator vertices;
406 std::vector<MeshTriangle>::const_iterator triangles;
407 bool hit;
408 };
409
410 bool GroupModel::IntersectRay(G3D::Ray const& ray, float& distance, bool stopAtFirstHit) const
411 {
412 if (triangles.empty())
413 return false;
414
416 meshTree.intersectRay(ray, callback, distance, stopAtFirstHit);
417 return callback.hit;
418 }
419
420 inline bool IsInsideOrAboveBound(G3D::AABox const& bounds, const G3D::Point3& point)
421 {
422 return point.x >= bounds.low().x
423 && point.y >= bounds.low().y
424 && point.z >= bounds.low().z
425 && point.x <= bounds.high().x
426 && point.y <= bounds.high().y;
427 }
428
429 GroupModel::InsideResult GroupModel::IsInsideObject(G3D::Ray const& ray, float& z_dist) const
430 {
431 if (triangles.empty() || !IsInsideOrAboveBound(iBound, ray.origin()))
432 return OUT_OF_BOUNDS;
433
434 if (meshTree.bound().high().z >= ray.origin().z)
435 {
436 float dist = G3D::finf();
437 if (IntersectRay(ray, dist, false))
438 {
439 z_dist = dist - 0.1f;
440 return INSIDE;
441 }
442 if (meshTree.bound().contains(ray.origin()))
443 return MAYBE_INSIDE;
444 }
445 else
446 {
447 // some group models don't have any floor to intersect with
448 // so we should attempt to intersect with a model part below this group
449 // then find back where we originated from (in WorldModel::GetLocationInfo)
450 float dist = G3D::finf();
451 float delta = ray.origin().z - meshTree.bound().high().z;
452 if (IntersectRay(ray.bumpedRay(delta), dist, false))
453 {
454 z_dist = dist - 0.1f + delta;
455 return ABOVE;
456 }
457 }
458
459 return OUT_OF_BOUNDS;
460 }
461
462 bool GroupModel::GetLiquidLevel(Vector3 const& pos, float& liqHeight) const
463 {
464 if (iLiquid)
465 return iLiquid->GetLiquidHeight(pos, liqHeight);
466 return false;
467 }
468
470 {
471 if (iLiquid)
472 return iLiquid->GetType();
473 return 0;
474 }
475
476 // ===================== WorldModel ==================================
477
478 void WorldModel::setGroupModels(std::vector<GroupModel>& models)
479 {
480 groupModels.swap(models);
481 groupTree.build(groupModels, BoundsTrait<GroupModel>(), 1);
482 }
483
485 {
486 WModelRayCallBack(std::vector<GroupModel> const& mod): models(mod.begin()), hit(false) { }
487 bool operator()(G3D::Ray const& ray, uint32 entry, float& distance, bool pStopAtFirstHit)
488 {
489 bool result = models[entry].IntersectRay(ray, distance, pStopAtFirstHit);
490 if (result)
491 hit = true;
492 return hit;
493 }
494 std::vector<GroupModel>::const_iterator models;
495 bool hit;
496 };
497
498 bool WorldModel::IntersectRay(G3D::Ray const& ray, float& distance, bool stopAtFirstHit, ModelIgnoreFlags ignoreFlags) const
499 {
500 // If the caller asked us to ignore certain objects we should check flags
501 if ((ignoreFlags & ModelIgnoreFlags::M2) != ModelIgnoreFlags::Nothing)
502 {
503 // M2 models are not taken into account for LoS calculation if caller requested their ignoring.
504 if (IsM2())
505 return false;
506 }
507
508 // small M2 workaround, maybe better make separate class with virtual intersection funcs
509 // in any case, there's no need to use a bound tree if we only have one submodel
510 if (groupModels.size() == 1)
511 return groupModels[0].IntersectRay(ray, distance, stopAtFirstHit);
512
514 groupTree.intersectRay(ray, isc, distance, stopAtFirstHit);
515 return isc.hit;
516 }
517
519 {
520 public:
521 WModelAreaCallback(std::vector<GroupModel> const& vals) :
522 prims(vals), hit() { }
523 std::vector<GroupModel> const& prims;
524 std::array<GroupModel const*, 3> hit;
525
526 bool operator()(G3D::Ray const& ray, uint32 entry, float& distance, bool /*stopAtFirstHit*/)
527 {
528 float group_Z;
529 if (GroupModel::InsideResult result = prims[entry].IsInsideObject(ray, group_Z); result != GroupModel::OUT_OF_BOUNDS)
530 {
531 if (result != GroupModel::MAYBE_INSIDE)
532 {
533 if (group_Z < distance)
534 {
535 distance = group_Z;
536 hit[result] = &prims[entry];
537 return true;
538 }
539 }
540 else
541 hit[result] = &prims[entry];
542 }
543 return false;
544 }
545 };
546
547 bool WorldModel::GetLocationInfo(const G3D::Vector3& p, const G3D::Vector3& down, float& dist, GroupLocationInfo& info) const
548 {
549 if (groupModels.empty())
550 return false;
551
553 G3D::Ray r(p - down * 0.1f, down);
554 float zDist = groupTree.bound().extent().length();
555 groupTree.intersectRay(r, callback, zDist, false);
556 if (callback.hit[GroupModel::INSIDE])
557 {
558 info.rootId = RootWMOID;
559 info.hitModel = callback.hit[GroupModel::INSIDE];
560 dist = zDist;
561 return true;
562 }
563
564 // some group models don't have any floor to intersect with
565 // so we should attempt to intersect with a model part below the group `p` is in (stored in GroupModel::ABOVE)
566 // then find back where we originated from (GroupModel::MAYBE_INSIDE)
567 if (callback.hit[GroupModel::MAYBE_INSIDE] && callback.hit[GroupModel::ABOVE])
568 {
569 info.rootId = RootWMOID;
570 info.hitModel = callback.hit[GroupModel::MAYBE_INSIDE];
571 dist = zDist;
572 return true;
573 }
574 return false;
575 }
576
577 bool WorldModel::writeFile(const std::string& filename)
578 {
579 FILE* wf = fopen(filename.c_str(), "wb");
580 if (!wf)
581 return false;
582
583 uint32 chunkSize, count;
584 bool result = fwrite(VMAP_MAGIC, 1, 8, wf) == 8;
585 if (result && fwrite("WMOD", 1, 4, wf) != 4) result = false;
586 chunkSize = sizeof(uint32) + sizeof(uint32);
587 if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
588 if (result)
589 {
591 if (fwrite(&flags, sizeof(uint32), 1, wf) != 1) result = false;
592 }
593 if (result && fwrite(&RootWMOID, sizeof(uint32), 1, wf) != 1) result = false;
594
595 // write group models
596 count = groupModels.size();
597 if (count)
598 {
599 if (result && fwrite("GMOD", 1, 4, wf) != 4) result = false;
600 //chunkSize = sizeof(uint32)+ sizeof(GroupModel)*count;
601 //if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
602 if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) result = false;
603 for (uint32 i = 0; i < groupModels.size() && result; ++i)
604 result = groupModels[i].writeToFile(wf);
605
606 // write group BIH
607 if (result && fwrite("GBIH", 1, 4, wf) != 4) result = false;
608 if (result) result = groupTree.writeToFile(wf);
609 }
610
611 fclose(wf);
612 return result;
613 }
614
615 bool WorldModel::readFile(const std::string& filename)
616 {
617 FILE* rf = fopen(filename.c_str(), "rb");
618 if (!rf)
619 return false;
620
621 bool result = true;
622 uint32 chunkSize = 0;
623 uint32 count = 0;
624 char chunk[8]; // Ignore the added magic header
625 if (!readChunk(rf, chunk, VMAP_MAGIC, 8)) result = false;
626
627 if (result && !readChunk(rf, chunk, "WMOD", 4)) result = false;
628 if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
629 if (result)
630 {
632 if (fread(&flags, sizeof(flags), 1, rf) == 1)
633 Flags = flags;
634 else
635 result = false;
636 }
637 if (result && fread(&RootWMOID, sizeof(uint32), 1, rf) != 1) result = false;
638
639 // read group models
640 if (result && readChunk(rf, chunk, "GMOD", 4))
641 {
642 //if (fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
643
644 if (result && fread(&count, sizeof(uint32), 1, rf) != 1) result = false;
645 if (result) groupModels.resize(count);
646 //if (result && fread(&groupModels[0], sizeof(GroupModel), count, rf) != count) result = false;
647 for (uint32 i = 0; i < count && result; ++i)
648 result = groupModels[i].readFromFile(rf);
649
650 // read group BIH
651 if (result && !readChunk(rf, chunk, "GBIH", 4)) result = false;
652 if (result) result = groupTree.readFromFile(rf);
653 }
654
655 fclose(rf);
656 return result;
657 }
658}
uint8_t uint8
Definition Define.h:156
uint32_t uint32
Definition Define.h:154
uint16 flags
#define LIQUID_TILE_SIZE
bool writeToFile(FILE *wf) const
void build(PrimArray const &primitives, BoundsFunc const &getBounds, uint32 leafSize=3, bool printStats=false)
G3D::AABox const & bound() const
void intersectRay(G3D::Ray const &r, RayCallback &intersectCallback, float &maxDist, bool stopAtFirst=false) const
bool readFromFile(FILE *rf)
constexpr std::underlying_type_t< T > AsUnderlyingType() const
Definition EnumFlag.h:122
std::vector< G3D::Vector3 > vertices
Definition WorldModel.h:110
uint32 GetLiquidType() const
bool readFromFile(FILE *rf)
WmoLiquid * iLiquid
Definition WorldModel.h:113
std::vector< MeshTriangle > triangles
Definition WorldModel.h:111
bool GetLiquidLevel(const G3D::Vector3 &pos, float &liqHeight) const
InsideResult IsInsideObject(G3D::Ray const &ray, float &z_dist) const
bool IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const
bool writeToFile(FILE *wf)
void setMeshData(std::vector< G3D::Vector3 > &&vert, std::vector< MeshTriangle > &&tri)
pass mesh data to object and create BIH.
G3D::AABox iBound
Definition WorldModel.h:107
G3D::AABox const & GetBound() const
Definition WorldModel.h:99
void operator()(MeshTriangle const &tri, G3D::AABox &out) const
const std::vector< Vector3 >::const_iterator vertices
TriBoundFunc(std::vector< Vector3 > &vert)
bool operator()(G3D::Ray const &ray, uint32 entry, float &distance, bool)
std::array< GroupModel const *, 3 > hit
std::vector< GroupModel > const & prims
WModelAreaCallback(std::vector< GroupModel > const &vals)
G3D::Vector3 iCorner
the lower corner
Definition WorldModel.h:73
void getPosInfo(uint32 &tilesX, uint32 &tilesY, G3D::Vector3 &corner) const
uint32 iType
liquid type
Definition WorldModel.h:74
uint32 GetFileSize()
float * iHeight
(tilesX + 1)*(tilesY + 1) height values
Definition WorldModel.h:75
uint32 iTilesX
number of tiles in x direction, each
Definition WorldModel.h:71
bool GetLiquidHeight(G3D::Vector3 const &pos, float &liqHeight) const
uint32 GetType() const
Definition WorldModel.h:60
uint8 * iFlags
info if liquid tile is used
Definition WorldModel.h:76
static bool readFromFile(FILE *rf, WmoLiquid *&liquid)
WmoLiquid & operator=(WmoLiquid const &other)
bool writeToFile(FILE *wf)
EnumFlag< ModelFlags > Flags
Definition WorldModel.h:133
bool IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit, ModelIgnoreFlags ignoreFlags) const
bool readFile(const std::string &filename)
bool GetLocationInfo(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, GroupLocationInfo &info) const
void setGroupModels(std::vector< GroupModel > &models)
pass group models to WorldModel and create BIH. Passed vector is swapped with old geometry!
bool IsM2() const
Definition WorldModel.h:130
bool writeFile(const std::string &filename)
std::vector< GroupModel > groupModels
Definition WorldModel.h:135
bool readChunk(FILE *rf, char *dest, const char *compare, uint32 len)
bool IntersectTriangle(MeshTriangle const &tri, std::vector< Vector3 >::const_iterator points, G3D::Ray const &ray, float &distance)
bool IsInsideOrAboveBound(G3D::AABox const &bounds, const G3D::Point3 &point)
const char VMAP_MAGIC[]
ModelFlags
Definition WorldModel.h:38
void operator()(VMAP::GroupModel const &obj, G3D::AABox &out) const
static void getBounds(VMAP::GroupModel const &obj, G3D::AABox &out)
std::vector< MeshTriangle >::const_iterator triangles
bool operator()(G3D::Ray const &ray, uint32 entry, float &distance, bool)
GModelRayCallback(std::vector< MeshTriangle > const &tris, const std::vector< Vector3 > &vert)
std::vector< Vector3 >::const_iterator vertices
const GroupModel * hitModel
Definition MapTree.h:36
bool operator()(G3D::Ray const &ray, uint32 entry, float &distance, bool pStopAtFirstHit)
std::vector< GroupModel >::const_iterator models
WModelRayCallBack(std::vector< GroupModel > const &mod)