aboutsummaryrefslogtreecommitdiff
path: root/src/common/Collision/Models
diff options
context:
space:
mode:
authorStormBytePP <stormbyte@gmail.com>2015-08-15 02:19:10 +0200
committerStormBytePP <stormbyte@gmail.com>2015-08-16 21:23:15 +0200
commit1f66d719f2cbbcb144b5080c89dd73fcae261798 (patch)
tree6a3778749b629c92de95cef7eb3d1d8c2630bdc4 /src/common/Collision/Models
parent222eaccc51b8d358c7b60d8def40d6461244ed31 (diff)
Core/BuildSystem: Merge collision, debugging, threading, utilities and configuration into "common" which does not depend on shared anymore and moved database out of shared library
These changes enables to build tools only without even having MySQL installed
Diffstat (limited to 'src/common/Collision/Models')
-rw-r--r--src/common/Collision/Models/GameObjectModel.cpp216
-rw-r--r--src/common/Collision/Models/GameObjectModel.h87
-rw-r--r--src/common/Collision/Models/ModelInstance.cpp222
-rw-r--r--src/common/Collision/Models/ModelInstance.h82
-rw-r--r--src/common/Collision/Models/WorldModel.cpp585
-rw-r--r--src/common/Collision/Models/WorldModel.h129
6 files changed, 1321 insertions, 0 deletions
diff --git a/src/common/Collision/Models/GameObjectModel.cpp b/src/common/Collision/Models/GameObjectModel.cpp
new file mode 100644
index 00000000000..dbdc0554e06
--- /dev/null
+++ b/src/common/Collision/Models/GameObjectModel.cpp
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "VMapFactory.h"
+#include "VMapManager2.h"
+#include "VMapDefinitions.h"
+#include "WorldModel.h"
+#include "GameObjectModel.h"
+#include "Log.h"
+#include "Timer.h"
+
+using G3D::Vector3;
+using G3D::Ray;
+using G3D::AABox;
+
+struct GameobjectModelData
+{
+ GameobjectModelData(const std::string& name_, const AABox& box) :
+ bound(box), name(name_) { }
+
+ AABox bound;
+ std::string name;
+};
+
+typedef std::unordered_map<uint32, GameobjectModelData> ModelList;
+ModelList model_list;
+
+void LoadGameObjectModelList(std::string const& dataPath)
+{
+#ifndef NO_CORE_FUNCS
+ uint32 oldMSTime = getMSTime();
+#endif
+
+ FILE* model_list_file = fopen((dataPath + "vmaps/" + VMAP::GAMEOBJECT_MODELS).c_str(), "rb");
+ if (!model_list_file)
+ {
+ VMAP_ERROR_LOG("misc", "Unable to open '%s' file.", VMAP::GAMEOBJECT_MODELS);
+ return;
+ }
+
+ uint32 name_length, displayId;
+ char buff[500];
+ while (true)
+ {
+ Vector3 v1, v2;
+ if (fread(&displayId, sizeof(uint32), 1, model_list_file) != 1)
+ if (feof(model_list_file)) // EOF flag is only set after failed reading attempt
+ break;
+
+ if (fread(&name_length, sizeof(uint32), 1, model_list_file) != 1
+ || name_length >= sizeof(buff)
+ || fread(&buff, sizeof(char), name_length, model_list_file) != name_length
+ || fread(&v1, sizeof(Vector3), 1, model_list_file) != 1
+ || fread(&v2, sizeof(Vector3), 1, model_list_file) != 1)
+ {
+ VMAP_ERROR_LOG("misc", "File '%s' seems to be corrupted!", VMAP::GAMEOBJECT_MODELS);
+ break;
+ }
+
+ if (v1.isNaN() || v2.isNaN())
+ {
+ VMAP_ERROR_LOG("misc", "File '%s' Model '%s' has invalid v1%s v2%s values!", VMAP::GAMEOBJECT_MODELS, std::string(buff, name_length).c_str(), v1.toString().c_str(), v2.toString().c_str());
+ continue;
+ }
+
+ model_list.insert
+ (
+ ModelList::value_type(displayId, GameobjectModelData(std::string(buff, name_length), AABox(v1, v2)))
+ );
+ }
+
+ fclose(model_list_file);
+ VMAP_INFO_LOG("server.loading", ">> Loaded %u GameObject models in %u ms", uint32(model_list.size()), GetMSTimeDiffToNow(oldMSTime));
+}
+
+GameObjectModel::~GameObjectModel()
+{
+ if (iModel)
+ ((VMAP::VMapManager2*)VMAP::VMapFactory::createOrGetVMapManager())->releaseModelInstance(name);
+}
+
+bool GameObjectModel::initialize(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath)
+{
+ ModelList::const_iterator it = model_list.find(modelOwner->GetDisplayId());
+ if (it == model_list.end())
+ return false;
+
+ G3D::AABox mdl_box(it->second.bound);
+ // ignore models with no bounds
+ if (mdl_box == G3D::AABox::zero())
+ {
+ VMAP_ERROR_LOG("misc", "GameObject model %s has zero bounds, loading skipped", it->second.name.c_str());
+ return false;
+ }
+
+ iModel = ((VMAP::VMapManager2*)VMAP::VMapFactory::createOrGetVMapManager())->acquireModelInstance(dataPath + "vmaps/", it->second.name);
+
+ if (!iModel)
+ return false;
+
+ name = it->second.name;
+ iPos = modelOwner->GetPosition();
+ phasemask = modelOwner->GetPhaseMask();
+ iScale = modelOwner->GetScale();
+ iInvScale = 1.f / iScale;
+
+ G3D::Matrix3 iRotation = G3D::Matrix3::fromEulerAnglesZYX(modelOwner->GetOrientation(), 0, 0);
+ iInvRot = iRotation.inverse();
+ // transform bounding box:
+ mdl_box = AABox(mdl_box.low() * iScale, mdl_box.high() * iScale);
+ AABox rotated_bounds;
+ for (int i = 0; i < 8; ++i)
+ rotated_bounds.merge(iRotation * mdl_box.corner(i));
+
+ iBound = rotated_bounds + iPos;
+#ifdef SPAWN_CORNERS
+ // test:
+ for (int i = 0; i < 8; ++i)
+ {
+ Vector3 pos(iBound.corner(i));
+ modelOwner->DebugVisualizeCorner(pos);
+ }
+#endif
+
+ owner = std::move(modelOwner);
+ return true;
+}
+
+GameObjectModel* GameObjectModel::Create(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath)
+{
+ GameObjectModel* mdl = new GameObjectModel();
+ if (!mdl->initialize(std::move(modelOwner), dataPath))
+ {
+ delete mdl;
+ return NULL;
+ }
+
+ return mdl;
+}
+
+bool GameObjectModel::intersectRay(const G3D::Ray& ray, float& MaxDist, bool StopAtFirstHit, uint32 ph_mask) const
+{
+ if (!(phasemask & ph_mask) || !owner->IsSpawned())
+ return false;
+
+ float time = ray.intersectionTime(iBound);
+ if (time == G3D::finf())
+ return false;
+
+ // child bounds are defined in object space:
+ Vector3 p = iInvRot * (ray.origin() - iPos) * iInvScale;
+ Ray modRay(p, iInvRot * ray.direction());
+ float distance = MaxDist * iInvScale;
+ bool hit = iModel->IntersectRay(modRay, distance, StopAtFirstHit);
+ if (hit)
+ {
+ distance *= iScale;
+ MaxDist = distance;
+ }
+ return hit;
+}
+
+bool GameObjectModel::UpdatePosition()
+{
+ if (!iModel)
+ return false;
+
+ ModelList::const_iterator it = model_list.find(owner->GetDisplayId());
+ if (it == model_list.end())
+ return false;
+
+ G3D::AABox mdl_box(it->second.bound);
+ // ignore models with no bounds
+ if (mdl_box == G3D::AABox::zero())
+ {
+ VMAP_ERROR_LOG("misc", "GameObject model %s has zero bounds, loading skipped", it->second.name.c_str());
+ return false;
+ }
+
+ iPos = owner->GetPosition();
+
+ G3D::Matrix3 iRotation = G3D::Matrix3::fromEulerAnglesZYX(owner->GetOrientation(), 0, 0);
+ iInvRot = iRotation.inverse();
+ // transform bounding box:
+ mdl_box = AABox(mdl_box.low() * iScale, mdl_box.high() * iScale);
+ AABox rotated_bounds;
+ for (int i = 0; i < 8; ++i)
+ rotated_bounds.merge(iRotation * mdl_box.corner(i));
+
+ iBound = rotated_bounds + iPos;
+#ifdef SPAWN_CORNERS
+ // test:
+ for (int i = 0; i < 8; ++i)
+ {
+ Vector3 pos(iBound.corner(i));
+ owner->DebugVisualizeCorner(pos);
+ }
+#endif
+
+ return true;
+}
diff --git a/src/common/Collision/Models/GameObjectModel.h b/src/common/Collision/Models/GameObjectModel.h
new file mode 100644
index 00000000000..17669189af5
--- /dev/null
+++ b/src/common/Collision/Models/GameObjectModel.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _GAMEOBJECT_MODEL_H
+#define _GAMEOBJECT_MODEL_H
+
+#include <G3D/Matrix3.h>
+#include <G3D/Vector3.h>
+#include <G3D/AABox.h>
+#include <G3D/Ray.h>
+
+#include "Define.h"
+#include <memory>
+
+namespace VMAP
+{
+ class WorldModel;
+}
+
+class GameObject;
+struct GameObjectDisplayInfoEntry;
+
+class GameObjectModelOwnerBase
+{
+public:
+ virtual bool IsSpawned() const { return false; }
+ virtual uint32 GetDisplayId() const { return 0; }
+ virtual uint32 GetPhaseMask() const { return 0; }
+ virtual G3D::Vector3 GetPosition() const { return G3D::Vector3::zero(); }
+ virtual float GetOrientation() const { return 0.0f; }
+ virtual float GetScale() const { return 1.0f; }
+ virtual void DebugVisualizeCorner(G3D::Vector3 const& /*corner*/) const { }
+};
+
+class GameObjectModel /*, public Intersectable*/
+{
+ GameObjectModel() : phasemask(0), iInvScale(0), iScale(0), iModel(NULL) { }
+public:
+ std::string name;
+
+ const G3D::AABox& getBounds() const { return iBound; }
+
+ ~GameObjectModel();
+
+ const G3D::Vector3& getPosition() const { return iPos;}
+
+ /** Enables\disables collision. */
+ void disable() { phasemask = 0;}
+ void enable(uint32 ph_mask) { phasemask = ph_mask;}
+
+ bool isEnabled() const {return phasemask != 0;}
+
+ bool intersectRay(const G3D::Ray& Ray, float& MaxDist, bool StopAtFirstHit, uint32 ph_mask) const;
+
+ static GameObjectModel* Create(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath);
+
+ bool UpdatePosition();
+
+private:
+ bool initialize(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath);
+
+ uint32 phasemask;
+ G3D::AABox iBound;
+ G3D::Matrix3 iInvRot;
+ G3D::Vector3 iPos;
+ float iInvScale;
+ float iScale;
+ VMAP::WorldModel* iModel;
+ std::unique_ptr<GameObjectModelOwnerBase> owner;
+};
+
+#endif // _GAMEOBJECT_MODEL_H
diff --git a/src/common/Collision/Models/ModelInstance.cpp b/src/common/Collision/Models/ModelInstance.cpp
new file mode 100644
index 00000000000..45440a99666
--- /dev/null
+++ b/src/common/Collision/Models/ModelInstance.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ModelInstance.h"
+#include "WorldModel.h"
+#include "MapTree.h"
+
+using G3D::Vector3;
+using G3D::Ray;
+
+namespace VMAP
+{
+ ModelInstance::ModelInstance(const ModelSpawn &spawn, WorldModel* model): ModelSpawn(spawn), iModel(model)
+ {
+ iInvRot = G3D::Matrix3::fromEulerAnglesZYX(G3D::pif()*iRot.y/180.f, G3D::pif()*iRot.x/180.f, G3D::pif()*iRot.z/180.f).inverse();
+ iInvScale = 1.f/iScale;
+ }
+
+ bool ModelInstance::intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit) const
+ {
+ if (!iModel)
+ {
+ //std::cout << "<object not loaded>\n";
+ return false;
+ }
+ float time = pRay.intersectionTime(iBound);
+ if (time == G3D::finf())
+ {
+// std::cout << "Ray does not hit '" << name << "'\n";
+
+ return false;
+ }
+// std::cout << "Ray crosses bound of '" << name << "'\n";
+/* std::cout << "ray from:" << pRay.origin().x << ", " << pRay.origin().y << ", " << pRay.origin().z
+ << " dir:" << pRay.direction().x << ", " << pRay.direction().y << ", " << pRay.direction().z
+ << " t/tmax:" << time << '/' << pMaxDist;
+ std::cout << "\nBound lo:" << iBound.low().x << ", " << iBound.low().y << ", " << iBound.low().z << " hi: "
+ << iBound.high().x << ", " << iBound.high().y << ", " << iBound.high().z << std::endl; */
+ // child bounds are defined in object space:
+ Vector3 p = iInvRot * (pRay.origin() - iPos) * iInvScale;
+ Ray modRay(p, iInvRot * pRay.direction());
+ float distance = pMaxDist * iInvScale;
+ bool hit = iModel->IntersectRay(modRay, distance, pStopAtFirstHit);
+ if (hit)
+ {
+ distance *= iScale;
+ pMaxDist = distance;
+ }
+ return hit;
+ }
+
+ void ModelInstance::intersectPoint(const G3D::Vector3& p, AreaInfo &info) const
+ {
+ if (!iModel)
+ {
+#ifdef VMAP_DEBUG
+ std::cout << "<object not loaded>\n";
+#endif
+ return;
+ }
+
+ // M2 files don't contain area info, only WMO files
+ if (flags & MOD_M2)
+ return;
+ if (!iBound.contains(p))
+ return;
+ // child bounds are defined in object space:
+ Vector3 pModel = iInvRot * (p - iPos) * iInvScale;
+ Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
+ float zDist;
+ if (iModel->IntersectPoint(pModel, zDirModel, zDist, info))
+ {
+ Vector3 modelGround = pModel + zDist * zDirModel;
+ // Transform back to world space. Note that:
+ // Mat * vec == vec * Mat.transpose()
+ // and for rotation matrices: Mat.inverse() == Mat.transpose()
+ float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
+ if (info.ground_Z < world_Z)
+ {
+ info.ground_Z = world_Z;
+ info.adtId = adtId;
+ }
+ }
+ }
+
+ bool ModelInstance::GetLocationInfo(const G3D::Vector3& p, LocationInfo &info) const
+ {
+ if (!iModel)
+ {
+#ifdef VMAP_DEBUG
+ std::cout << "<object not loaded>\n";
+#endif
+ return false;
+ }
+
+ // M2 files don't contain area info, only WMO files
+ if (flags & MOD_M2)
+ return false;
+ if (!iBound.contains(p))
+ return false;
+ // child bounds are defined in object space:
+ Vector3 pModel = iInvRot * (p - iPos) * iInvScale;
+ Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
+ float zDist;
+ if (iModel->GetLocationInfo(pModel, zDirModel, zDist, info))
+ {
+ Vector3 modelGround = pModel + zDist * zDirModel;
+ // Transform back to world space. Note that:
+ // Mat * vec == vec * Mat.transpose()
+ // and for rotation matrices: Mat.inverse() == Mat.transpose()
+ float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
+ if (info.ground_Z < world_Z) // hm...could it be handled automatically with zDist at intersection?
+ {
+ info.ground_Z = world_Z;
+ info.hitInstance = this;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool ModelInstance::GetLiquidLevel(const G3D::Vector3& p, LocationInfo &info, float &liqHeight) const
+ {
+ // child bounds are defined in object space:
+ Vector3 pModel = iInvRot * (p - iPos) * iInvScale;
+ //Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
+ float zDist;
+ if (info.hitModel->GetLiquidLevel(pModel, zDist))
+ {
+ // calculate world height (zDist in model coords):
+ // assume WMO not tilted (wouldn't make much sense anyway)
+ liqHeight = zDist * iScale + iPos.z;
+ return true;
+ }
+ return false;
+ }
+
+ bool ModelSpawn::readFromFile(FILE* rf, ModelSpawn &spawn)
+ {
+ uint32 check = 0, nameLen;
+ check += fread(&spawn.flags, sizeof(uint32), 1, rf);
+ // EoF?
+ if (!check)
+ {
+ if (ferror(rf))
+ std::cout << "Error reading ModelSpawn!\n";
+ return false;
+ }
+ check += fread(&spawn.adtId, sizeof(uint16), 1, rf);
+ check += fread(&spawn.ID, sizeof(uint32), 1, rf);
+ check += fread(&spawn.iPos, sizeof(float), 3, rf);
+ check += fread(&spawn.iRot, sizeof(float), 3, rf);
+ check += fread(&spawn.iScale, sizeof(float), 1, rf);
+ bool has_bound = (spawn.flags & MOD_HAS_BOUND) != 0;
+ if (has_bound) // only WMOs have bound in MPQ, only available after computation
+ {
+ Vector3 bLow, bHigh;
+ check += fread(&bLow, sizeof(float), 3, rf);
+ check += fread(&bHigh, sizeof(float), 3, rf);
+ spawn.iBound = G3D::AABox(bLow, bHigh);
+ }
+ check += fread(&nameLen, sizeof(uint32), 1, rf);
+ if (check != uint32(has_bound ? 17 : 11))
+ {
+ std::cout << "Error reading ModelSpawn!\n";
+ return false;
+ }
+ char nameBuff[500];
+ if (nameLen > 500) // file names should never be that long, must be file error
+ {
+ std::cout << "Error reading ModelSpawn, file name too long!\n";
+ return false;
+ }
+ check = fread(nameBuff, sizeof(char), nameLen, rf);
+ if (check != nameLen)
+ {
+ std::cout << "Error reading ModelSpawn!\n";
+ return false;
+ }
+ spawn.name = std::string(nameBuff, nameLen);
+ return true;
+ }
+
+ bool ModelSpawn::writeToFile(FILE* wf, const ModelSpawn &spawn)
+ {
+ uint32 check=0;
+ check += fwrite(&spawn.flags, sizeof(uint32), 1, wf);
+ check += fwrite(&spawn.adtId, sizeof(uint16), 1, wf);
+ check += fwrite(&spawn.ID, sizeof(uint32), 1, wf);
+ check += fwrite(&spawn.iPos, sizeof(float), 3, wf);
+ check += fwrite(&spawn.iRot, sizeof(float), 3, wf);
+ check += fwrite(&spawn.iScale, sizeof(float), 1, wf);
+ bool has_bound = (spawn.flags & MOD_HAS_BOUND) != 0;
+ if (has_bound) // only WMOs have bound in MPQ, only available after computation
+ {
+ check += fwrite(&spawn.iBound.low(), sizeof(float), 3, wf);
+ check += fwrite(&spawn.iBound.high(), sizeof(float), 3, wf);
+ }
+ uint32 nameLen = spawn.name.length();
+ check += fwrite(&nameLen, sizeof(uint32), 1, wf);
+ if (check != uint32(has_bound ? 17 : 11)) return false;
+ check = fwrite(spawn.name.c_str(), sizeof(char), nameLen, wf);
+ if (check != nameLen) return false;
+ return true;
+ }
+
+}
diff --git a/src/common/Collision/Models/ModelInstance.h b/src/common/Collision/Models/ModelInstance.h
new file mode 100644
index 00000000000..dfdb001db0a
--- /dev/null
+++ b/src/common/Collision/Models/ModelInstance.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MODELINSTANCE_H_
+#define _MODELINSTANCE_H_
+
+#include <G3D/Matrix3.h>
+#include <G3D/Vector3.h>
+#include <G3D/AABox.h>
+#include <G3D/Ray.h>
+
+#include "Define.h"
+
+namespace VMAP
+{
+ class WorldModel;
+ struct AreaInfo;
+ struct LocationInfo;
+
+ enum ModelFlags
+ {
+ MOD_M2 = 1,
+ MOD_WORLDSPAWN = 1<<1,
+ MOD_HAS_BOUND = 1<<2
+ };
+
+ class ModelSpawn
+ {
+ public:
+ //mapID, tileX, tileY, Flags, ID, Pos, Rot, Scale, Bound_lo, Bound_hi, name
+ uint32 flags;
+ uint16 adtId;
+ uint32 ID;
+ G3D::Vector3 iPos;
+ G3D::Vector3 iRot;
+ float iScale;
+ G3D::AABox iBound;
+ std::string name;
+ bool operator==(const ModelSpawn &other) const { return ID == other.ID; }
+ //uint32 hashCode() const { return ID; }
+ // temp?
+ const G3D::AABox& getBounds() const { return iBound; }
+
+ static bool readFromFile(FILE* rf, ModelSpawn &spawn);
+ static bool writeToFile(FILE* rw, const ModelSpawn &spawn);
+ };
+
+ class ModelInstance: public ModelSpawn
+ {
+ public:
+ ModelInstance(): iInvScale(0.0f), iModel(nullptr) { }
+ ModelInstance(const ModelSpawn &spawn, WorldModel* model);
+ void setUnloaded() { iModel = nullptr; }
+ bool intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit) const;
+ void intersectPoint(const G3D::Vector3& p, AreaInfo &info) const;
+ bool GetLocationInfo(const G3D::Vector3& p, LocationInfo &info) const;
+ bool GetLiquidLevel(const G3D::Vector3& p, LocationInfo &info, float &liqHeight) const;
+ protected:
+ G3D::Matrix3 iInvRot;
+ float iInvScale;
+ WorldModel* iModel;
+ public:
+ WorldModel* getWorldModel();
+ };
+} // namespace VMAP
+
+#endif // _MODELINSTANCE
diff --git a/src/common/Collision/Models/WorldModel.cpp b/src/common/Collision/Models/WorldModel.cpp
new file mode 100644
index 00000000000..86ab9366c71
--- /dev/null
+++ b/src/common/Collision/Models/WorldModel.cpp
@@ -0,0 +1,585 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "WorldModel.h"
+#include "VMapDefinitions.h"
+#include "MapTree.h"
+
+using G3D::Vector3;
+using G3D::Ray;
+
+template<> struct BoundsTrait<VMAP::GroupModel>
+{
+ static void getBounds(const VMAP::GroupModel& obj, G3D::AABox& out) { out = obj.GetBound(); }
+};
+
+namespace VMAP
+{
+ bool IntersectTriangle(const MeshTriangle &tri, std::vector<Vector3>::const_iterator points, const G3D::Ray &ray, float &distance)
+ {
+ static const float EPS = 1e-5f;
+
+ // See RTR2 ch. 13.7 for the algorithm.
+
+ const Vector3 e1 = points[tri.idx1] - points[tri.idx0];
+ const Vector3 e2 = points[tri.idx2] - points[tri.idx0];
+ const Vector3 p(ray.direction().cross(e2));
+ const float a = e1.dot(p);
+
+ if (std::fabs(a) < EPS) {
+ // Determinant is ill-conditioned; abort early
+ return false;
+ }
+
+ const float f = 1.0f / a;
+ const Vector3 s(ray.origin() - points[tri.idx0]);
+ const float u = f * s.dot(p);
+
+ if ((u < 0.0f) || (u > 1.0f)) {
+ // We hit the plane of the m_geometry, but outside the m_geometry
+ return false;
+ }
+
+ const Vector3 q(s.cross(e1));
+ const float v = f * ray.direction().dot(q);
+
+ if ((v < 0.0f) || ((u + v) > 1.0f)) {
+ // We hit the plane of the triangle, but outside the triangle
+ return false;
+ }
+
+ const float t = f * e2.dot(q);
+
+ if ((t > 0.0f) && (t < distance))
+ {
+ // This is a new hit, closer than the previous one
+ distance = t;
+
+ /* baryCoord[0] = 1.0 - u - v;
+ baryCoord[1] = u;
+ baryCoord[2] = v; */
+
+ return true;
+ }
+ // This hit is after the previous hit, so ignore it
+ return false;
+ }
+
+ class TriBoundFunc
+ {
+ public:
+ TriBoundFunc(std::vector<Vector3> &vert): vertices(vert.begin()) { }
+ void operator()(const MeshTriangle &tri, G3D::AABox &out) const
+ {
+ G3D::Vector3 lo = vertices[tri.idx0];
+ G3D::Vector3 hi = lo;
+
+ lo = (lo.min(vertices[tri.idx1])).min(vertices[tri.idx2]);
+ hi = (hi.max(vertices[tri.idx1])).max(vertices[tri.idx2]);
+
+ out = G3D::AABox(lo, hi);
+ }
+ protected:
+ const std::vector<Vector3>::const_iterator vertices;
+ };
+
+ // ===================== WmoLiquid ==================================
+
+ WmoLiquid::WmoLiquid(uint32 width, uint32 height, const Vector3 &corner, uint32 type):
+ iTilesX(width), iTilesY(height), iCorner(corner), iType(type)
+ {
+ iHeight = new float[(width+1)*(height+1)];
+ iFlags = new uint8[width*height];
+ }
+
+ WmoLiquid::WmoLiquid(const WmoLiquid &other): iHeight(nullptr), iFlags(nullptr)
+ {
+ *this = other; // use assignment operator...
+ }
+
+ WmoLiquid::~WmoLiquid()
+ {
+ delete[] iHeight;
+ delete[] iFlags;
+ }
+
+ WmoLiquid& WmoLiquid::operator=(const WmoLiquid &other)
+ {
+ if (this == &other)
+ return *this;
+ iTilesX = other.iTilesX;
+ iTilesY = other.iTilesY;
+ iCorner = other.iCorner;
+ iType = other.iType;
+ delete iHeight;
+ delete iFlags;
+ if (other.iHeight)
+ {
+ iHeight = new float[(iTilesX+1)*(iTilesY+1)];
+ memcpy(iHeight, other.iHeight, (iTilesX+1)*(iTilesY+1)*sizeof(float));
+ }
+ else
+ iHeight = nullptr;
+ if (other.iFlags)
+ {
+ iFlags = new uint8[iTilesX * iTilesY];
+ memcpy(iFlags, other.iFlags, iTilesX * iTilesY);
+ }
+ else
+ iFlags = nullptr;
+ return *this;
+ }
+
+ bool WmoLiquid::GetLiquidHeight(const Vector3 &pos, float &liqHeight) const
+ {
+ float tx_f = (pos.x - iCorner.x)/LIQUID_TILE_SIZE;
+ uint32 tx = uint32(tx_f);
+ if (tx_f < 0.0f || tx >= iTilesX)
+ return false;
+ float ty_f = (pos.y - iCorner.y)/LIQUID_TILE_SIZE;
+ uint32 ty = uint32(ty_f);
+ if (ty_f < 0.0f || ty >= iTilesY)
+ return false;
+
+ // check if tile shall be used for liquid level
+ // checking for 0x08 *might* be enough, but disabled tiles always are 0x?F:
+ if ((iFlags[tx + ty*iTilesX] & 0x0F) == 0x0F)
+ return false;
+
+ // (dx, dy) coordinates inside tile, in [0, 1]^2
+ float dx = tx_f - (float)tx;
+ float dy = ty_f - (float)ty;
+
+ /* Tesselate tile to two triangles (not sure if client does it exactly like this)
+
+ ^ dy
+ |
+ 1 x---------x (1, 1)
+ | (b) / |
+ | / |
+ | / |
+ | / (a) |
+ x---------x---> dx
+ 0 1
+ */
+
+ const uint32 rowOffset = iTilesX + 1;
+ if (dx > dy) // case (a)
+ {
+ float sx = iHeight[tx+1 + ty * rowOffset] - iHeight[tx + ty * rowOffset];
+ float sy = iHeight[tx+1 + (ty+1) * rowOffset] - iHeight[tx+1 + ty * rowOffset];
+ liqHeight = iHeight[tx + ty * rowOffset] + dx * sx + dy * sy;
+ }
+ else // case (b)
+ {
+ float sx = iHeight[tx+1 + (ty+1) * rowOffset] - iHeight[tx + (ty+1) * rowOffset];
+ float sy = iHeight[tx + (ty+1) * rowOffset] - iHeight[tx + ty * rowOffset];
+ liqHeight = iHeight[tx + ty * rowOffset] + dx * sx + dy * sy;
+ }
+ return true;
+ }
+
+ uint32 WmoLiquid::GetFileSize()
+ {
+ return 2 * sizeof(uint32) +
+ sizeof(Vector3) +
+ (iTilesX + 1)*(iTilesY + 1) * sizeof(float) +
+ iTilesX * iTilesY;
+ }
+
+ bool WmoLiquid::writeToFile(FILE* wf)
+ {
+ bool result = false;
+ if (fwrite(&iTilesX, sizeof(uint32), 1, wf) == 1 &&
+ fwrite(&iTilesY, sizeof(uint32), 1, wf) == 1 &&
+ fwrite(&iCorner, sizeof(Vector3), 1, wf) == 1 &&
+ fwrite(&iType, sizeof(uint32), 1, wf) == 1)
+ {
+ uint32 size = (iTilesX + 1) * (iTilesY + 1);
+ if (fwrite(iHeight, sizeof(float), size, wf) == size)
+ {
+ size = iTilesX*iTilesY;
+ result = fwrite(iFlags, sizeof(uint8), size, wf) == size;
+ }
+ }
+
+ return result;
+ }
+
+ bool WmoLiquid::readFromFile(FILE* rf, WmoLiquid* &out)
+ {
+ bool result = false;
+ WmoLiquid* liquid = new WmoLiquid();
+
+ if (fread(&liquid->iTilesX, sizeof(uint32), 1, rf) == 1 &&
+ fread(&liquid->iTilesY, sizeof(uint32), 1, rf) == 1 &&
+ fread(&liquid->iCorner, sizeof(Vector3), 1, rf) == 1 &&
+ fread(&liquid->iType, sizeof(uint32), 1, rf) == 1)
+ {
+ uint32 size = (liquid->iTilesX + 1) * (liquid->iTilesY + 1);
+ liquid->iHeight = new float[size];
+ if (fread(liquid->iHeight, sizeof(float), size, rf) == size)
+ {
+ size = liquid->iTilesX * liquid->iTilesY;
+ liquid->iFlags = new uint8[size];
+ result = fread(liquid->iFlags, sizeof(uint8), size, rf) == size;
+ }
+ }
+
+ if (!result)
+ delete liquid;
+ else
+ out = liquid;
+
+ return result;
+ }
+
+ // ===================== GroupModel ==================================
+
+ GroupModel::GroupModel(const GroupModel &other):
+ iBound(other.iBound), iMogpFlags(other.iMogpFlags), iGroupWMOID(other.iGroupWMOID),
+ vertices(other.vertices), triangles(other.triangles), meshTree(other.meshTree), iLiquid(nullptr)
+ {
+ if (other.iLiquid)
+ iLiquid = new WmoLiquid(*other.iLiquid);
+ }
+
+ void GroupModel::setMeshData(std::vector<Vector3> &vert, std::vector<MeshTriangle> &tri)
+ {
+ vertices.swap(vert);
+ triangles.swap(tri);
+ TriBoundFunc bFunc(vertices);
+ meshTree.build(triangles, bFunc);
+ }
+
+ bool GroupModel::writeToFile(FILE* wf)
+ {
+ bool result = true;
+ uint32 chunkSize, count;
+
+ if (result && fwrite(&iBound, sizeof(G3D::AABox), 1, wf) != 1) result = false;
+ if (result && fwrite(&iMogpFlags, sizeof(uint32), 1, wf) != 1) result = false;
+ if (result && fwrite(&iGroupWMOID, sizeof(uint32), 1, wf) != 1) result = false;
+
+ // write vertices
+ if (result && fwrite("VERT", 1, 4, wf) != 4) result = false;
+ count = vertices.size();
+ chunkSize = sizeof(uint32)+ sizeof(Vector3)*count;
+ if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
+ if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) result = false;
+ if (!count) // models without (collision) geometry end here, unsure if they are useful
+ return result;
+ if (result && fwrite(&vertices[0], sizeof(Vector3), count, wf) != count) result = false;
+
+ // write triangle mesh
+ if (result && fwrite("TRIM", 1, 4, wf) != 4) result = false;
+ count = triangles.size();
+ chunkSize = sizeof(uint32)+ sizeof(MeshTriangle)*count;
+ if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
+ if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) result = false;
+ if (result && fwrite(&triangles[0], sizeof(MeshTriangle), count, wf) != count) result = false;
+
+ // write mesh BIH
+ if (result && fwrite("MBIH", 1, 4, wf) != 4) result = false;
+ if (result) result = meshTree.writeToFile(wf);
+
+ // write liquid data
+ if (result && fwrite("LIQU", 1, 4, wf) != 4) result = false;
+ if (!iLiquid)
+ {
+ chunkSize = 0;
+ if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
+ return result;
+ }
+ chunkSize = iLiquid->GetFileSize();
+ if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
+ if (result) result = iLiquid->writeToFile(wf);
+
+ return result;
+ }
+
+ bool GroupModel::readFromFile(FILE* rf)
+ {
+ char chunk[8];
+ bool result = true;
+ uint32 chunkSize = 0;
+ uint32 count = 0;
+ triangles.clear();
+ vertices.clear();
+ delete iLiquid;
+ iLiquid = NULL;
+
+ if (result && fread(&iBound, sizeof(G3D::AABox), 1, rf) != 1) result = false;
+ if (result && fread(&iMogpFlags, sizeof(uint32), 1, rf) != 1) result = false;
+ if (result && fread(&iGroupWMOID, sizeof(uint32), 1, rf) != 1) result = false;
+
+ // read vertices
+ if (result && !readChunk(rf, chunk, "VERT", 4)) result = false;
+ if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
+ if (result && fread(&count, sizeof(uint32), 1, rf) != 1) result = false;
+ if (!count) // models without (collision) geometry end here, unsure if they are useful
+ return result;
+ if (result) vertices.resize(count);
+ if (result && fread(&vertices[0], sizeof(Vector3), count, rf) != count) result = false;
+
+ // read triangle mesh
+ if (result && !readChunk(rf, chunk, "TRIM", 4)) result = false;
+ if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
+ if (result && fread(&count, sizeof(uint32), 1, rf) != 1) result = false;
+ if (result) triangles.resize(count);
+ if (result && fread(&triangles[0], sizeof(MeshTriangle), count, rf) != count) result = false;
+
+ // read mesh BIH
+ if (result && !readChunk(rf, chunk, "MBIH", 4)) result = false;
+ if (result) result = meshTree.readFromFile(rf);
+
+ // write liquid data
+ if (result && !readChunk(rf, chunk, "LIQU", 4)) result = false;
+ if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
+ if (result && chunkSize > 0)
+ result = WmoLiquid::readFromFile(rf, iLiquid);
+ return result;
+ }
+
+ struct GModelRayCallback
+ {
+ GModelRayCallback(const std::vector<MeshTriangle> &tris, const std::vector<Vector3> &vert):
+ vertices(vert.begin()), triangles(tris.begin()), hit(false) { }
+ bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool /*pStopAtFirstHit*/)
+ {
+ bool result = IntersectTriangle(triangles[entry], vertices, ray, distance);
+ if (result) hit=true;
+ return hit;
+ }
+ std::vector<Vector3>::const_iterator vertices;
+ std::vector<MeshTriangle>::const_iterator triangles;
+ bool hit;
+ };
+
+ bool GroupModel::IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const
+ {
+ if (triangles.empty())
+ return false;
+
+ GModelRayCallback callback(triangles, vertices);
+ meshTree.intersectRay(ray, callback, distance, stopAtFirstHit);
+ return callback.hit;
+ }
+
+ bool GroupModel::IsInsideObject(const Vector3 &pos, const Vector3 &down, float &z_dist) const
+ {
+ if (triangles.empty() || !iBound.contains(pos))
+ return false;
+ GModelRayCallback callback(triangles, vertices);
+ Vector3 rPos = pos - 0.1f * down;
+ float dist = G3D::finf();
+ G3D::Ray ray(rPos, down);
+ bool hit = IntersectRay(ray, dist, false);
+ if (hit)
+ z_dist = dist - 0.1f;
+ return hit;
+ }
+
+ bool GroupModel::GetLiquidLevel(const Vector3 &pos, float &liqHeight) const
+ {
+ if (iLiquid)
+ return iLiquid->GetLiquidHeight(pos, liqHeight);
+ return false;
+ }
+
+ uint32 GroupModel::GetLiquidType() const
+ {
+ if (iLiquid)
+ return iLiquid->GetType();
+ return 0;
+ }
+
+ // ===================== WorldModel ==================================
+
+ void WorldModel::setGroupModels(std::vector<GroupModel> &models)
+ {
+ groupModels.swap(models);
+ groupTree.build(groupModels, BoundsTrait<GroupModel>::getBounds, 1);
+ }
+
+ struct WModelRayCallBack
+ {
+ WModelRayCallBack(const std::vector<GroupModel> &mod): models(mod.begin()), hit(false) { }
+ bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool pStopAtFirstHit)
+ {
+ bool result = models[entry].IntersectRay(ray, distance, pStopAtFirstHit);
+ if (result) hit=true;
+ return hit;
+ }
+ std::vector<GroupModel>::const_iterator models;
+ bool hit;
+ };
+
+ bool WorldModel::IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const
+ {
+ // small M2 workaround, maybe better make separate class with virtual intersection funcs
+ // in any case, there's no need to use a bound tree if we only have one submodel
+ if (groupModels.size() == 1)
+ return groupModels[0].IntersectRay(ray, distance, stopAtFirstHit);
+
+ WModelRayCallBack isc(groupModels);
+ groupTree.intersectRay(ray, isc, distance, stopAtFirstHit);
+ return isc.hit;
+ }
+
+ class WModelAreaCallback {
+ public:
+ WModelAreaCallback(const std::vector<GroupModel> &vals, const Vector3 &down):
+ prims(vals.begin()), hit(vals.end()), minVol(G3D::finf()), zDist(G3D::finf()), zVec(down) { }
+ std::vector<GroupModel>::const_iterator prims;
+ std::vector<GroupModel>::const_iterator hit;
+ float minVol;
+ float zDist;
+ Vector3 zVec;
+ void operator()(const Vector3& point, uint32 entry)
+ {
+ float group_Z;
+ //float pVol = prims[entry].GetBound().volume();
+ //if (pVol < minVol)
+ //{
+ /* if (prims[entry].iBound.contains(point)) */
+ if (prims[entry].IsInsideObject(point, zVec, group_Z))
+ {
+ //minVol = pVol;
+ //hit = prims + entry;
+ if (group_Z < zDist)
+ {
+ zDist = group_Z;
+ hit = prims + entry;
+ }
+#ifdef VMAP_DEBUG
+ const GroupModel &gm = prims[entry];
+ printf("%10u %8X %7.3f, %7.3f, %7.3f | %7.3f, %7.3f, %7.3f | z=%f, p_z=%f\n", gm.GetWmoID(), gm.GetMogpFlags(),
+ gm.GetBound().low().x, gm.GetBound().low().y, gm.GetBound().low().z,
+ gm.GetBound().high().x, gm.GetBound().high().y, gm.GetBound().high().z, group_Z, point.z);
+#endif
+ }
+ //}
+ //std::cout << "trying to intersect '" << prims[entry].name << "'\n";
+ }
+ };
+
+ bool WorldModel::IntersectPoint(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, AreaInfo &info) const
+ {
+ if (groupModels.empty())
+ return false;
+
+ WModelAreaCallback callback(groupModels, down);
+ groupTree.intersectPoint(p, callback);
+ if (callback.hit != groupModels.end())
+ {
+ info.rootId = RootWMOID;
+ info.groupId = callback.hit->GetWmoID();
+ info.flags = callback.hit->GetMogpFlags();
+ info.result = true;
+ dist = callback.zDist;
+ return true;
+ }
+ return false;
+ }
+
+ bool WorldModel::GetLocationInfo(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, LocationInfo &info) const
+ {
+ if (groupModels.empty())
+ return false;
+
+ WModelAreaCallback callback(groupModels, down);
+ groupTree.intersectPoint(p, callback);
+ if (callback.hit != groupModels.end())
+ {
+ info.hitModel = &(*callback.hit);
+ dist = callback.zDist;
+ return true;
+ }
+ return false;
+ }
+
+ bool WorldModel::writeFile(const std::string &filename)
+ {
+ FILE* wf = fopen(filename.c_str(), "wb");
+ if (!wf)
+ return false;
+
+ uint32 chunkSize, count;
+ bool result = fwrite(VMAP_MAGIC, 1, 8, wf) == 8;
+ if (result && fwrite("WMOD", 1, 4, wf) != 4) result = false;
+ chunkSize = sizeof(uint32) + sizeof(uint32);
+ if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
+ if (result && fwrite(&RootWMOID, sizeof(uint32), 1, wf) != 1) result = false;
+
+ // write group models
+ count=groupModels.size();
+ if (count)
+ {
+ if (result && fwrite("GMOD", 1, 4, wf) != 4) result = false;
+ //chunkSize = sizeof(uint32)+ sizeof(GroupModel)*count;
+ //if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
+ if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) result = false;
+ for (uint32 i=0; i<groupModels.size() && result; ++i)
+ result = groupModels[i].writeToFile(wf);
+
+ // write group BIH
+ if (result && fwrite("GBIH", 1, 4, wf) != 4) result = false;
+ if (result) result = groupTree.writeToFile(wf);
+ }
+
+ fclose(wf);
+ return result;
+ }
+
+ bool WorldModel::readFile(const std::string &filename)
+ {
+ FILE* rf = fopen(filename.c_str(), "rb");
+ if (!rf)
+ return false;
+
+ bool result = true;
+ uint32 chunkSize = 0;
+ uint32 count = 0;
+ char chunk[8]; // Ignore the added magic header
+ if (!readChunk(rf, chunk, VMAP_MAGIC, 8)) result = false;
+
+ if (result && !readChunk(rf, chunk, "WMOD", 4)) result = false;
+ if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
+ if (result && fread(&RootWMOID, sizeof(uint32), 1, rf) != 1) result = false;
+
+ // read group models
+ if (result && readChunk(rf, chunk, "GMOD", 4))
+ {
+ //if (fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
+
+ if (result && fread(&count, sizeof(uint32), 1, rf) != 1) result = false;
+ if (result) groupModels.resize(count);
+ //if (result && fread(&groupModels[0], sizeof(GroupModel), count, rf) != count) result = false;
+ for (uint32 i=0; i<count && result; ++i)
+ result = groupModels[i].readFromFile(rf);
+
+ // read group BIH
+ if (result && !readChunk(rf, chunk, "GBIH", 4)) result = false;
+ if (result) result = groupTree.readFromFile(rf);
+ }
+
+ fclose(rf);
+ return result;
+ }
+}
diff --git a/src/common/Collision/Models/WorldModel.h b/src/common/Collision/Models/WorldModel.h
new file mode 100644
index 00000000000..6a901a59fdf
--- /dev/null
+++ b/src/common/Collision/Models/WorldModel.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _WORLDMODEL_H
+#define _WORLDMODEL_H
+
+#include <G3D/HashTrait.h>
+#include <G3D/Vector3.h>
+#include <G3D/AABox.h>
+#include <G3D/Ray.h>
+#include "BoundingIntervalHierarchy.h"
+
+#include "Define.h"
+
+namespace VMAP
+{
+ class TreeNode;
+ struct AreaInfo;
+ struct LocationInfo;
+
+ class MeshTriangle
+ {
+ public:
+ MeshTriangle() : idx0(0), idx1(0), idx2(0) { }
+ MeshTriangle(uint32 na, uint32 nb, uint32 nc): idx0(na), idx1(nb), idx2(nc) { }
+
+ uint32 idx0;
+ uint32 idx1;
+ uint32 idx2;
+ };
+
+ class WmoLiquid
+ {
+ public:
+ WmoLiquid(uint32 width, uint32 height, const G3D::Vector3 &corner, uint32 type);
+ WmoLiquid(const WmoLiquid &other);
+ ~WmoLiquid();
+ WmoLiquid& operator=(const WmoLiquid &other);
+ bool GetLiquidHeight(const G3D::Vector3 &pos, float &liqHeight) const;
+ uint32 GetType() const { return iType; }
+ float *GetHeightStorage() { return iHeight; }
+ uint8 *GetFlagsStorage() { return iFlags; }
+ uint32 GetFileSize();
+ bool writeToFile(FILE* wf);
+ static bool readFromFile(FILE* rf, WmoLiquid* &liquid);
+ private:
+ WmoLiquid() : iTilesX(0), iTilesY(0), iCorner(), iType(0), iHeight(NULL), iFlags(NULL) { }
+ uint32 iTilesX; //!< number of tiles in x direction, each
+ uint32 iTilesY;
+ G3D::Vector3 iCorner; //!< the lower corner
+ uint32 iType; //!< liquid type
+ float *iHeight; //!< (tilesX + 1)*(tilesY + 1) height values
+ uint8 *iFlags; //!< info if liquid tile is used
+ public:
+ void getPosInfo(uint32 &tilesX, uint32 &tilesY, G3D::Vector3 &corner) const;
+ };
+
+ /*! holding additional info for WMO group files */
+ class GroupModel
+ {
+ public:
+ GroupModel() : iBound(), iMogpFlags(0), iGroupWMOID(0), iLiquid(NULL) { }
+ GroupModel(const GroupModel &other);
+ GroupModel(uint32 mogpFlags, uint32 groupWMOID, const G3D::AABox &bound):
+ iBound(bound), iMogpFlags(mogpFlags), iGroupWMOID(groupWMOID), iLiquid(NULL) { }
+ ~GroupModel() { delete iLiquid; }
+
+ //! pass mesh data to object and create BIH. Passed vectors get get swapped with old geometry!
+ void setMeshData(std::vector<G3D::Vector3> &vert, std::vector<MeshTriangle> &tri);
+ void setLiquidData(WmoLiquid*& liquid) { iLiquid = liquid; liquid = NULL; }
+ bool IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const;
+ bool IsInsideObject(const G3D::Vector3 &pos, const G3D::Vector3 &down, float &z_dist) const;
+ bool GetLiquidLevel(const G3D::Vector3 &pos, float &liqHeight) const;
+ uint32 GetLiquidType() const;
+ bool writeToFile(FILE* wf);
+ bool readFromFile(FILE* rf);
+ const G3D::AABox& GetBound() const { return iBound; }
+ uint32 GetMogpFlags() const { return iMogpFlags; }
+ uint32 GetWmoID() const { return iGroupWMOID; }
+ protected:
+ G3D::AABox iBound;
+ uint32 iMogpFlags;// 0x8 outdor; 0x2000 indoor
+ uint32 iGroupWMOID;
+ std::vector<G3D::Vector3> vertices;
+ std::vector<MeshTriangle> triangles;
+ BIH meshTree;
+ WmoLiquid* iLiquid;
+ public:
+ void getMeshData(std::vector<G3D::Vector3> &vertices, std::vector<MeshTriangle> &triangles, WmoLiquid* &liquid);
+ };
+ /*! Holds a model (converted M2 or WMO) in its original coordinate space */
+ class WorldModel
+ {
+ public:
+ WorldModel(): RootWMOID(0) { }
+
+ //! pass group models to WorldModel and create BIH. Passed vector is swapped with old geometry!
+ void setGroupModels(std::vector<GroupModel> &models);
+ void setRootWmoID(uint32 id) { RootWMOID = id; }
+ bool IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const;
+ bool IntersectPoint(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, AreaInfo &info) const;
+ bool GetLocationInfo(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, LocationInfo &info) const;
+ bool writeFile(const std::string &filename);
+ bool readFile(const std::string &filename);
+ protected:
+ uint32 RootWMOID;
+ std::vector<GroupModel> groupModels;
+ BIH groupTree;
+ public:
+ void getGroupModels(std::vector<GroupModel> &groupModels);
+ };
+} // namespace VMAP
+
+#endif // _WORLDMODEL_H