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 "GameObjectModel.h"
19#include "Log.h"
20#include "MapTree.h"
21#include "Memory.h"
22#include "Timer.h"
23#include "VMapDefinitions.h"
24#include "VMapFactory.h"
25#include "VMapManager.h"
26#include "WorldModel.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
197 VMAP::WorldModelLocationInfoQueryResult locationQueryResult;
198 if (iModel->GetLocationInfo(pModel, zDirModel, locationQueryResult))
199 {
200 Vector3 modelGround = pModel + locationQueryResult.distanceToModel * zDirModel;
201 float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
202 if (info.ground_Z < world_Z)
203 {
204 info.rootId = locationQueryResult.rootId;
205 info.hitModel = locationQueryResult.hitModel;
206 info.ground_Z = world_Z;
207 return true;
208 }
209 }
210
211 return false;
212}
213
214bool GameObjectModel::GetLiquidLevel(G3D::Vector3 const& point, VMAP::GroupModel const* model, float& liqHeight) const
215{
216 // child bounds are defined in object space:
217 Vector3 pModel = iInvRot * (point - iPos) * iInvScale;
218 //Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
219 float zDist;
220 if (model->GetLiquidLevel(pModel, zDist))
221 {
222 // calculate world height (zDist in model coords):
223 liqHeight = (Vector3(pModel.x, pModel.y, zDist) * iInvRot * iScale + iPos).z;
224 return true;
225 }
226 return false;
227}
228
230{
231 if (!iModel)
232 return false;
233
234 ModelList::const_iterator it = model_list.find(owner->GetDisplayId());
235 if (it == model_list.end())
236 return false;
237
238 G3D::AABox mdl_box(it->second.bound);
239 // ignore models with no bounds
240 if (mdl_box == G3D::AABox::zero())
241 {
242 TC_LOG_ERROR("misc", "GameObject model {} has zero bounds, loading skipped", it->second.name);
243 return false;
244 }
245
246 iPos = owner->GetPosition();
247
248 G3D::Matrix3 iRotation = owner->GetRotation().toRotationMatrix();
249 iInvRot = iRotation.inverse();
250 // transform bounding box:
251 mdl_box = AABox(mdl_box.low() * iScale, mdl_box.high() * iScale);
252 AABox rotated_bounds = G3D::AABox::empty();
253 for (int i = 0; i < 8; ++i)
254 rotated_bounds.merge(iRotation * mdl_box.corner(i));
255
256 iBound = rotated_bounds + iPos;
257#ifdef SPAWN_CORNERS
258 // test:
259 for (int i = 0; i < 8; ++i)
260 {
261 Vector3 pos(iBound.corner(i));
262 owner->DebugVisualizeCorner(pos);
263 }
264#endif
265
266 return true;
267}
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:182
#define TC_LOG_INFO(filterType__, message__,...)
Definition Log.h:176
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
static std::unique_ptr< GameObjectModel > Create(std::unique_ptr< GameObjectModelOwnerBase > modelOwner, std::string const &dataPath)
bool GetLiquidLevel(G3D::Vector3 const &point, VMAP::GroupModel const *model, float &liqHeight) const
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:36