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