TrinityCore
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 "VMapManager2.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(fopen((dataPath + "vmaps/" + VMAP::GAMEOBJECT_MODELS).c_str(), "rb"), &::fclose);
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{
98 if (iModel)
100}
101
102bool GameObjectModel::initialize(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath)
103{
104 ModelList::const_iterator it = model_list.find(modelOwner->GetDisplayId());
105 if (it == model_list.end())
106 return false;
107
108 G3D::AABox mdl_box(it->second.bound);
109 // ignore models with no bounds
110 if (mdl_box == G3D::AABox::zero())
111 {
112 TC_LOG_ERROR("misc", "GameObject model {} has zero bounds, loading skipped", it->second.name);
113 return false;
114 }
115
116 iModel = VMAP::VMapFactory::createOrGetVMapManager()->acquireModelInstance(dataPath + "vmaps/", it->second.name);
117
118 if (!iModel)
119 return false;
120
121 iPos = modelOwner->GetPosition();
122 iScale = modelOwner->GetScale();
123 iInvScale = 1.f / iScale;
124
125 G3D::Matrix3 iRotation = modelOwner->GetRotation().toRotationMatrix();
126 iInvRot = iRotation.inverse();
127 // transform bounding box:
128 mdl_box = AABox(mdl_box.low() * iScale, mdl_box.high() * iScale);
129 AABox rotated_bounds;
130 for (int i = 0; i < 8; ++i)
131 rotated_bounds.merge(iRotation * mdl_box.corner(i));
132
133 iBound = rotated_bounds + iPos;
134#ifdef SPAWN_CORNERS
135 // test:
136 for (int i = 0; i < 8; ++i)
137 {
138 Vector3 pos(iBound.corner(i));
139 modelOwner->DebugVisualizeCorner(pos);
140 }
141#endif
142
143 owner = std::move(modelOwner);
144 return true;
145}
146
147GameObjectModel* GameObjectModel::Create(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath)
148{
149 GameObjectModel* mdl = new GameObjectModel();
150 if (!mdl->initialize(std::move(modelOwner), dataPath))
151 {
152 delete mdl;
153 return nullptr;
154 }
155
156 return mdl;
157}
158
160{
161 return !iModel->IsM2();
162}
163
164bool GameObjectModel::intersectRay(G3D::Ray const& ray, float& maxDist, bool stopAtFirstHit, PhaseShift const& phaseShift, VMAP::ModelIgnoreFlags ignoreFlags) const
165{
166 if (!isCollisionEnabled() || !owner->IsSpawned())
167 return false;
168
169 if (!owner->IsInPhase(phaseShift))
170 return false;
171
172 float time = ray.intersectionTime(iBound);
173 if (time == G3D::finf())
174 return false;
175
176 // child bounds are defined in object space:
177 Vector3 p = iInvRot * (ray.origin() - iPos) * iInvScale;
178 Ray modRay(p, iInvRot * ray.direction());
179 float distance = maxDist * iInvScale;
180 bool hit = iModel->IntersectRay(modRay, distance, stopAtFirstHit, ignoreFlags);
181 if (hit)
182 {
183 distance *= iScale;
184 maxDist = distance;
185 }
186 return hit;
187}
188
189bool GameObjectModel::GetLocationInfo(G3D::Vector3 const& point, VMAP::LocationInfo& info, PhaseShift const& phaseShift) const
190{
191 if (!isCollisionEnabled() || !owner->IsSpawned() || !isMapObject())
192 return false;
193
194 if (!owner->IsInPhase(phaseShift))
195 return false;
196
197 if (!iBound.contains(point))
198 return false;
199
200 // child bounds are defined in object space:
201 Vector3 pModel = iInvRot * (point - iPos) * iInvScale;
202 Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
203 float zDist;
204
205 VMAP::GroupLocationInfo groupInfo;
206 if (iModel->GetLocationInfo(pModel, zDirModel, zDist, groupInfo))
207 {
208 Vector3 modelGround = pModel + zDist * zDirModel;
209 float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
210 if (info.ground_Z < world_Z)
211 {
212 info.ground_Z = world_Z;
213 return true;
214 }
215 }
216
217 return false;
218}
219
220bool GameObjectModel::GetLiquidLevel(G3D::Vector3 const& point, VMAP::LocationInfo& info, float& liqHeight) const
221{
222 // child bounds are defined in object space:
223 Vector3 pModel = iInvRot * (point - iPos) * iInvScale;
224 //Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
225 float zDist;
226 if (info.hitModel->GetLiquidLevel(pModel, zDist))
227 {
228 // calculate world height (zDist in model coords):
229 // assume WMO not tilted (wouldn't make much sense anyway)
230 liqHeight = zDist * iScale + iPos.z;
231 return true;
232 }
233 return false;
234}
235
237{
238 if (!iModel)
239 return false;
240
241 ModelList::const_iterator it = model_list.find(owner->GetDisplayId());
242 if (it == model_list.end())
243 return false;
244
245 G3D::AABox mdl_box(it->second.bound);
246 // ignore models with no bounds
247 if (mdl_box == G3D::AABox::zero())
248 {
249 TC_LOG_ERROR("misc", "GameObject model {} has zero bounds, loading skipped", it->second.name);
250 return false;
251 }
252
253 iPos = owner->GetPosition();
254
255 G3D::Matrix3 iRotation = owner->GetRotation().toRotationMatrix();
256 iInvRot = iRotation.inverse();
257 // transform bounding box:
258 mdl_box = AABox(mdl_box.low() * iScale, mdl_box.high() * iScale);
259 AABox rotated_bounds;
260 for (int i = 0; i < 8; ++i)
261 rotated_bounds.merge(iRotation * mdl_box.corner(i));
262
263 iBound = rotated_bounds + iPos;
264#ifdef SPAWN_CORNERS
265 // test:
266 for (int i = 0; i < 8; ++i)
267 {
268 Vector3 pos(iBound.corner(i));
269 owner->DebugVisualizeCorner(pos);
270 }
271#endif
272
273 return true;
274}
uint32_t uint32
Definition: Define.h:142
std::unordered_map< uint32, GameobjectModelData > ModelList
ModelList model_list
bool LoadGameObjectModelList(std::string const &dataPath)
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
#define TC_LOG_INFO(filterType__,...)
Definition: Log.h:159
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition: Timer.h:57
uint32 getMSTime()
Definition: Timer.h:33
G3D::Vector3 iPos
G3D::Matrix3 iInvRot
VMAP::WorldModel * iModel
bool intersectRay(G3D::Ray const &ray, float &maxDist, bool stopAtFirstHit, PhaseShift const &phaseShift, VMAP::ModelIgnoreFlags ignoreFlags) const
G3D::AABox iBound
bool GetLocationInfo(G3D::Vector3 const &point, VMAP::LocationInfo &info, PhaseShift const &phaseShift) const
static GameObjectModel * Create(std::unique_ptr< GameObjectModelOwnerBase > modelOwner, std::string const &dataPath)
bool isMapObject() const
bool isCollisionEnabled() const
bool GetLiquidLevel(G3D::Vector3 const &point, VMAP::LocationInfo &info, float &liqHeight) const
std::unique_ptr< GameObjectModelOwnerBase > owner
bool initialize(std::unique_ptr< GameObjectModelOwnerBase > modelOwner, std::string const &dataPath)
bool GetLiquidLevel(const G3D::Vector3 &pos, float &liqHeight) const
Definition: WorldModel.cpp:432
static VMapManager2 * createOrGetVMapManager()
Definition: VMapFactory.cpp:27
void releaseModelInstance(std::string const &filename)
WorldModel * acquireModelInstance(std::string const &basepath, std::string const &filename)
bool IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit, ModelIgnoreFlags ignoreFlags) const
Definition: WorldModel.cpp:468
std::string const & GetName() const
Definition: WorldModel.h:130
bool GetLocationInfo(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, GroupLocationInfo &info) const
Definition: WorldModel.cpp:525
bool IsM2() const
Definition: WorldModel.h:128
auto make_unique_ptr_with_deleter(T ptr, Del &&deleter)
Definition: Memory.h:41
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:44