22de17e1ad0fe5b89c89fb1c36b6706f44f938dd
[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 return true;
149 }
150
151 GameObjectModel* GameObjectModel::Create(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath)
152 {
153 GameObjectModel* mdl = new GameObjectModel();
154 if (!mdl->initialize(std::move(modelOwner), dataPath))
155 {
156 delete mdl;
157 return NULL;
158 }
159
160 return mdl;
161 }
162
163 bool GameObjectModel::intersectRay(G3D::Ray const& ray, float& maxDist, bool stopAtFirstHit, PhaseShift const& phaseShift) const
164 {
165 if (!isCollisionEnabled() || !owner->IsSpawned())
166 return false;
167
168 if (!owner->IsInPhase(phaseShift))
169 return false;
170
171 float time = ray.intersectionTime(iBound);
172 if (time == G3D::finf())
173 return false;
174
175 // child bounds are defined in object space:
176 Vector3 p = iInvRot * (ray.origin() - iPos) * iInvScale;
177 Ray modRay(p, iInvRot * ray.direction());
178 float distance = maxDist * iInvScale;
179 bool hit = iModel->IntersectRay(modRay, distance, stopAtFirstHit);
180 if (hit)
181 {
182 distance *= iScale;
183 maxDist = distance;
184 }
185 return hit;
186 }
187
188 void GameObjectModel::intersectPoint(G3D::Vector3 const& point, VMAP::AreaInfo& info, PhaseShift const& phaseShift) const
189 {
190 if (!isCollisionEnabled() || !owner->IsSpawned())
191 return;
192
193 if (!owner->IsInPhase(phaseShift))
194 return;
195
196 if (!iBound.contains(point))
197 return;
198
199 // child bounds are defined in object space:
200 Vector3 pModel = iInvRot * (point - iPos) * iInvScale;
201 Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
202 float zDist;
203 if (iModel->IntersectPoint(pModel, zDirModel, zDist, info))
204 {
205 Vector3 modelGround = pModel + zDist * zDirModel;
206 float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
207 if (info.ground_Z < world_Z)
208 {
209 info.ground_Z = world_Z;
210 info.adtId = owner->GetNameSetId();
211 }
212 }
213 }
214
215 bool GameObjectModel::UpdatePosition()
216 {
217 if (!iModel)
218 return false;
219
220 ModelList::const_iterator it = model_list.find(owner->GetDisplayId());
221 if (it == model_list.end())
222 return false;
223
224 G3D::AABox mdl_box(it->second.bound);
225 // ignore models with no bounds
226 if (mdl_box == G3D::AABox::zero())
227 {
228 TC_LOG_ERROR("misc", "GameObject model %s has zero bounds, loading skipped", it->second.name.c_str());
229 return false;
230 }
231
232 iPos = owner->GetPosition();
233
234 G3D::Matrix3 iRotation = G3D::Matrix3::fromEulerAnglesZYX(owner->GetOrientation(), 0, 0);
235 iInvRot = iRotation.inverse();
236 // transform bounding box:
237 mdl_box = AABox(mdl_box.low() * iScale, mdl_box.high() * iScale);
238 AABox rotated_bounds;
239 for (int i = 0; i < 8; ++i)
240 rotated_bounds.merge(iRotation * mdl_box.corner(i));
241
242 iBound = rotated_bounds + iPos;
243 #ifdef SPAWN_CORNERS
244 // test:
245 for (int i = 0; i < 8; ++i)
246 {
247 Vector3 pos(iBound.corner(i));
248 owner->DebugVisualizeCorner(pos);
249 }
250 #endif
251
252 return true;
253 }