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