Core/GameObjects: Skip gameobjects with M2 models when searching for area info (perf...
[trinitycore] / src / common / Collision / Models / GameObjectModel.cpp
1 /*
2 * Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
3 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "VMapFactory.h"
20 #include "VMapManager2.h"
21 #include "VMapDefinitions.h"
22 #include "WorldModel.h"
23 #include "GameObjectModel.h"
24 #include "Log.h"
25 #include "MapTree.h"
26 #include "Timer.h"
27
28 using G3D::Vector3;
29 using G3D::Ray;
30 using G3D::AABox;
31
32 struct GameobjectModelData
33 {
34 GameobjectModelData(char const* name_, uint32 nameLength, Vector3 const& lowBound, Vector3 const& highBound, bool isWmo_) :
35 bound(lowBound, highBound), name(name_, nameLength), isWmo(isWmo_) { }
36
37 AABox bound;
38 std::string name;
39 bool isWmo;
40 };
41
42 typedef std::unordered_map<uint32, GameobjectModelData> ModelList;
43 ModelList model_list;
44
45 void LoadGameObjectModelList(std::string const& dataPath)
46 {
47 uint32 oldMSTime = getMSTime();
48
49 FILE* model_list_file = fopen((dataPath + "vmaps/" + VMAP::GAMEOBJECT_MODELS).c_str(), "rb");
50 if (!model_list_file)
51 {
52 TC_LOG_ERROR("misc", "Unable to open '%s' file.", VMAP::GAMEOBJECT_MODELS);
53 return;
54 }
55
56 char magic[8];
57 if (fread(magic, 1, 8, model_list_file) != 8
58 || memcmp(magic, VMAP::VMAP_MAGIC, 8) != 0)
59 {
60 TC_LOG_ERROR("misc", "File '%s' has wrong header, expected %s.", VMAP::GAMEOBJECT_MODELS, VMAP::VMAP_MAGIC);
61 fclose(model_list_file);
62 return;
63 }
64
65 uint32 name_length, displayId;
66 uint8 isWmo;
67 char buff[500];
68 while (true)
69 {
70 Vector3 v1, v2;
71 if (fread(&displayId, sizeof(uint32), 1, model_list_file) != 1)
72 if (feof(model_list_file)) // EOF flag is only set after failed reading attempt
73 break;
74
75 if (fread(&isWmo, sizeof(uint8), 1, model_list_file) != 1
76 || fread(&name_length, sizeof(uint32), 1, model_list_file) != 1
77 || name_length >= sizeof(buff)
78 || fread(&buff, sizeof(char), name_length, model_list_file) != name_length
79 || fread(&v1, sizeof(Vector3), 1, model_list_file) != 1
80 || fread(&v2, sizeof(Vector3), 1, model_list_file) != 1)
81 {
82 TC_LOG_ERROR("misc", "File '%s' seems to be corrupted!", VMAP::GAMEOBJECT_MODELS);
83 break;
84 }
85
86 if (v1.isNaN() || v2.isNaN())
87 {
88 TC_LOG_ERROR("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());
89 continue;
90 }
91
92 model_list.emplace(std::piecewise_construct, std::forward_as_tuple(displayId), std::forward_as_tuple(&buff[0], name_length, v1, v2, isWmo != 0));
93 }
94
95 fclose(model_list_file);
96 TC_LOG_INFO("server.loading", ">> Loaded %u GameObject models in %u ms", uint32(model_list.size()), GetMSTimeDiffToNow(oldMSTime));
97 }
98
99 GameObjectModel::~GameObjectModel()
100 {
101 if (iModel)
102 ((VMAP::VMapManager2*)VMAP::VMapFactory::createOrGetVMapManager())->releaseModelInstance(name);
103 }
104
105 bool GameObjectModel::initialize(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath)
106 {
107 ModelList::const_iterator it = model_list.find(modelOwner->GetDisplayId());
108 if (it == model_list.end())
109 return false;
110
111 G3D::AABox mdl_box(it->second.bound);
112 // ignore models with no bounds
113 if (mdl_box == G3D::AABox::zero())
114 {
115 TC_LOG_ERROR("misc", "GameObject model %s has zero bounds, loading skipped", it->second.name.c_str());
116 return false;
117 }
118
119 iModel = ((VMAP::VMapManager2*)VMAP::VMapFactory::createOrGetVMapManager())->acquireModelInstance(dataPath + "vmaps/", it->second.name);
120
121 if (!iModel)
122 return false;
123
124 name = it->second.name;
125 iPos = modelOwner->GetPosition();
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
152 GameObjectModel* 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 NULL;
159 }
160
161 return mdl;
162 }
163
164 bool GameObjectModel::intersectRay(G3D::Ray const& ray, float& maxDist, bool stopAtFirstHit, PhaseShift const& phaseShift) 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);
181 if (hit)
182 {
183 distance *= iScale;
184 maxDist = distance;
185 }
186 return hit;
187 }
188
189 void GameObjectModel::intersectPoint(G3D::Vector3 const& point, VMAP::AreaInfo& info, PhaseShift const& phaseShift) const
190 {
191 if (!isCollisionEnabled() || !owner->IsSpawned() || !isMapObject())
192 return;
193
194 if (!owner->IsInPhase(phaseShift))
195 return;
196
197 if (!iBound.contains(point))
198 return;
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 if (iModel->IntersectPoint(pModel, zDirModel, zDist, info))
205 {
206 Vector3 modelGround = pModel + zDist * zDirModel;
207 float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
208 if (info.ground_Z < world_Z)
209 {
210 info.ground_Z = world_Z;
211 info.adtId = owner->GetNameSetId();
212 }
213 }
214 }
215
216 bool GameObjectModel::UpdatePosition()
217 {
218 if (!iModel)
219 return false;
220
221 ModelList::const_iterator it = model_list.find(owner->GetDisplayId());
222 if (it == model_list.end())
223 return false;
224
225 G3D::AABox mdl_box(it->second.bound);
226 // ignore models with no bounds
227 if (mdl_box == G3D::AABox::zero())
228 {
229 TC_LOG_ERROR("misc", "GameObject model %s has zero bounds, loading skipped", it->second.name.c_str());
230 return false;
231 }
232
233 iPos = owner->GetPosition();
234
235 G3D::Matrix3 iRotation = G3D::Matrix3::fromEulerAnglesZYX(owner->GetOrientation(), 0, 0);
236 iInvRot = iRotation.inverse();
237 // transform bounding box:
238 mdl_box = AABox(mdl_box.low() * iScale, mdl_box.high() * iScale);
239 AABox rotated_bounds;
240 for (int i = 0; i < 8; ++i)
241 rotated_bounds.merge(iRotation * mdl_box.corner(i));
242
243 iBound = rotated_bounds + iPos;
244 #ifdef SPAWN_CORNERS
245 // test:
246 for (int i = 0; i < 8; ++i)
247 {
248 Vector3 pos(iBound.corner(i));
249 owner->DebugVisualizeCorner(pos);
250 }
251 #endif
252
253 return true;
254 }