TrinityCore
Loading...
Searching...
No Matches
GameObjectModel.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 "VMapFactory.h"
19#include "VMapManager.h"
20#include "VMapDefinitions.h"
21#include "WorldModel.h"
22#include "GameObjectModel.h"
23#include "Log.h"
24#include "MapTree.h"
25#include "Memory.h"
26#include "Timer.h"
27#include <G3D/Quat.h>
28
29using G3D::Vector3;
30using G3D::Ray;
31using G3D::AABox;
32
34{
35 GameobjectModelData(char const* name_, uint32 nameLength, Vector3 const& lowBound, Vector3 const& highBound) :
36 bound(lowBound, highBound), name(name_, nameLength) { }
37
38 AABox bound;
39 std::string name;
40};
41
42typedef std::unordered_map<uint32, GameobjectModelData> ModelList;
44
45bool LoadGameObjectModelList(std::string const& dataPath)
46{
47 uint32 oldMSTime = getMSTime();
48
49 auto model_list_file = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen((dataPath + "vmaps/" + VMAP::GAMEOBJECT_MODELS).c_str(), "rb"));
50 if (!model_list_file)
51 {
52 TC_LOG_ERROR("misc", "Unable to open '{}' file.", VMAP::GAMEOBJECT_MODELS);
53 return false;
54 }
55
56 char magic[8];
57 if (fread(magic, 1, 8, model_list_file.get()) != 8
58 || memcmp(magic, VMAP::VMAP_MAGIC, 8) != 0)
59 {
60 TC_LOG_ERROR("misc", "File '{}' has wrong header, expected {}.", VMAP::GAMEOBJECT_MODELS, VMAP::VMAP_MAGIC);
61 return false;
62 }
63
64 uint32 name_length, displayId;
65 char buff[500];
66 while (true)
67 {
68 Vector3 v1, v2;
69 if (fread(&displayId, sizeof(uint32), 1, model_list_file.get()) != 1)
70 if (feof(model_list_file.get())) // EOF flag is only set after failed reading attempt
71 break;
72
73 if (fread(&name_length, sizeof(uint32), 1, model_list_file.get()) != 1
74 || name_length >= sizeof(buff)
75 || fread(&buff, sizeof(char), name_length, model_list_file.get()) != name_length
76 || fread(&v1, sizeof(Vector3), 1, model_list_file.get()) != 1
77 || fread(&v2, sizeof(Vector3), 1, model_list_file.get()) != 1)
78 {
79 TC_LOG_ERROR("misc", "File '{}' seems to be corrupted!", VMAP::GAMEOBJECT_MODELS);
80 break;
81 }
82
83 if (v1.isNaN() || v2.isNaN())
84 {
85 TC_LOG_ERROR("misc", "File '{}' Model '{}' has invalid v1{} v2{} values!", VMAP::GAMEOBJECT_MODELS, std::string(buff, name_length), v1.toString(), v2.toString());
86 continue;
87 }
88
89 model_list.emplace(std::piecewise_construct, std::forward_as_tuple(displayId), std::forward_as_tuple(&buff[0], name_length, v1, v2));
90 }
91
92 TC_LOG_INFO("server.loading", ">> Loaded {} GameObject models in {} ms", uint32(model_list.size()), GetMSTimeDiffToNow(oldMSTime));
93 return true;
94}
95
97
98bool GameObjectModel::initialize(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath)
99{
100 ModelList::const_iterator it = model_list.find(modelOwner->GetDisplayId());
101 if (it == model_list.end())
102 return false;
103
104 G3D::AABox mdl_box(it->second.bound);
105 // ignore models with no bounds
106 if (mdl_box == G3D::AABox::zero())
107 {
108 TC_LOG_ERROR("misc", "GameObject model {} has zero bounds, loading skipped", it->second.name);
109 return false;
110 }
111
112 iModel = VMAP::VMapFactory::createOrGetVMapManager()->acquireModelInstance(dataPath + "vmaps/", it->second.name);
113
114 if (!iModel)
115 return false;
116
117 iPos = modelOwner->GetPosition();
118 iScale = modelOwner->GetScale();
119 iInvScale = 1.f / iScale;
120
121 G3D::Matrix3 iRotation = modelOwner->GetRotation().toRotationMatrix();
122 iInvRot = iRotation.inverse();
123 // transform bounding box:
124 mdl_box = AABox(mdl_box.low() * iScale, mdl_box.high() * iScale);
125 AABox rotated_bounds = G3D::AABox::empty();
126 for (int i = 0; i < 8; ++i)
127 rotated_bounds.merge(iRotation * mdl_box.corner(i));
128
129 iBound = rotated_bounds + iPos;
130#ifdef SPAWN_CORNERS
131 // test:
132 for (int i = 0; i < 8; ++i)
133 {
134 Vector3 pos(iBound.corner(i));
135 modelOwner->DebugVisualizeCorner(pos);
136 }
137#endif
138
139 owner = std::move(modelOwner);
140 return true;
141}
142
143std::unique_ptr<GameObjectModel> GameObjectModel::Create(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath)
144{
145 std::unique_ptr<GameObjectModel> mdl(new GameObjectModel());
146 if (!mdl->initialize(std::move(modelOwner), dataPath))
147 mdl = nullptr;
148
149 return mdl;
150}
151
153{
154 return !iModel->IsM2();
155}
156
157bool GameObjectModel::IntersectRay(G3D::Ray const& ray, float& maxDist, bool stopAtFirstHit, PhaseShift const& phaseShift, VMAP::ModelIgnoreFlags ignoreFlags) const
158{
159 if (!IsCollisionEnabled() || !owner->IsSpawned())
160 return false;
161
162 if (!owner->IsInPhase(phaseShift))
163 return false;
164
165 float time = ray.intersectionTime(iBound);
166 if (time == G3D::finf())
167 return false;
168
169 // child bounds are defined in object space:
170 Vector3 p = iInvRot * (ray.origin() - iPos) * iInvScale;
171 Ray modRay(p, iInvRot * ray.direction());
172 float distance = maxDist * iInvScale;
173 bool hit = iModel->IntersectRay(modRay, distance, stopAtFirstHit, ignoreFlags);
174 if (hit)
175 {
176 distance *= iScale;
177 maxDist = distance;
178 }
179 return hit;
180}
181
182bool GameObjectModel::GetLocationInfo(G3D::Vector3 const& point, VMAP::LocationInfo& info, PhaseShift const& phaseShift) const
183{
184 if (!IsCollisionEnabled() || !owner->IsSpawned() || !IsMapObject())
185 return false;
186
187 if (!owner->IsInPhase(phaseShift))
188 return false;
189
190 if (!iBound.contains(point))
191 return false;
192
193 // child bounds are defined in object space:
194 Vector3 pModel = iInvRot * (point - iPos) * iInvScale;
195 Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
196 float zDist;
197
198 VMAP::GroupLocationInfo groupInfo;
199 if (iModel->GetLocationInfo(pModel, zDirModel, zDist, groupInfo))
200 {
201 Vector3 modelGround = pModel + zDist * zDirModel;
202 float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
203 if (info.ground_Z < world_Z)
204 {
205 info.ground_Z = world_Z;
206 return true;
207 }
208 }
209
210 return false;
211}
212
213bool GameObjectModel::GetLiquidLevel(G3D::Vector3 const& point, VMAP::LocationInfo& info, float& liqHeight) const
214{
215 // child bounds are defined in object space:
216 Vector3 pModel = iInvRot * (point - iPos) * iInvScale;
217 //Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
218 float zDist;
219 if (info.hitModel->GetLiquidLevel(pModel, zDist))
220 {
221 // calculate world height (zDist in model coords):
222 liqHeight = (Vector3(pModel.x, pModel.y, zDist) * iInvRot * iScale + iPos).z;
223 return true;
224 }
225 return false;
226}
227
229{
230 if (!iModel)
231 return false;
232
233 ModelList::const_iterator it = model_list.find(owner->GetDisplayId());
234 if (it == model_list.end())
235 return false;
236
237 G3D::AABox mdl_box(it->second.bound);
238 // ignore models with no bounds
239 if (mdl_box == G3D::AABox::zero())
240 {
241 TC_LOG_ERROR("misc", "GameObject model {} has zero bounds, loading skipped", it->second.name);
242 return false;
243 }
244
245 iPos = owner->GetPosition();
246
247 G3D::Matrix3 iRotation = owner->GetRotation().toRotationMatrix();
248 iInvRot = iRotation.inverse();
249 // transform bounding box:
250 mdl_box = AABox(mdl_box.low() * iScale, mdl_box.high() * iScale);
251 AABox rotated_bounds = G3D::AABox::empty();
252 for (int i = 0; i < 8; ++i)
253 rotated_bounds.merge(iRotation * mdl_box.corner(i));
254
255 iBound = rotated_bounds + iPos;
256#ifdef SPAWN_CORNERS
257 // test:
258 for (int i = 0; i < 8; ++i)
259 {
260 Vector3 pos(iBound.corner(i));
261 owner->DebugVisualizeCorner(pos);
262 }
263#endif
264
265 return true;
266}
uint32_t uint32
Definition Define.h:154
std::unordered_map< uint32, GameobjectModelData > ModelList
ModelList model_list
bool LoadGameObjectModelList(std::string const &dataPath)
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
#define TC_LOG_INFO(filterType__, message__,...)
Definition Log.h:184
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition Timer.h:57
uint32 getMSTime()
Definition Timer.h:33
G3D::Vector3 iPos
std::shared_ptr< VMAP::WorldModel > iModel
bool IsCollisionEnabled() const
G3D::Matrix3 iInvRot
bool GetLocationInfo(G3D::Vector3 const &point, VMAP::LocationInfo &info, PhaseShift const &phaseShift) const
bool GetLiquidLevel(G3D::Vector3 const &point, VMAP::LocationInfo &info, float &liqHeight) const
static std::unique_ptr< GameObjectModel > Create(std::unique_ptr< GameObjectModelOwnerBase > modelOwner, std::string const &dataPath)
std::unique_ptr< GameObjectModelOwnerBase > owner
bool IsMapObject() const
bool IntersectRay(G3D::Ray const &ray, float &maxDist, bool stopAtFirstHit, PhaseShift const &phaseShift, VMAP::ModelIgnoreFlags ignoreFlags) const
bool initialize(std::unique_ptr< GameObjectModelOwnerBase > modelOwner, std::string const &dataPath)
bool GetLiquidLevel(const G3D::Vector3 &pos, float &liqHeight) const
static VMapManager * createOrGetVMapManager()
std::shared_ptr< WorldModel > acquireModelInstance(std::string const &basepath, std::string const &filename)
const char VMAP_MAGIC[]
const char GAMEOBJECT_MODELS[]
GameobjectModelData(char const *name_, uint32 nameLength, Vector3 const &lowBound, Vector3 const &highBound)
GroupModel const * hitModel
Definition MapTree.h:45