TrinityCore
model.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 "vmapexport.h"
19#include "Errors.h"
20#include "model.h"
21#include "StringFormat.h"
22#include "wmo.h"
23#include "adtfile.h"
24#include "cascfile.h"
25#include "VMapDefinitions.h"
26#include <G3D/Quat.h>
27#include <algorithm>
28#include <cstdio>
29#include <limits>
30
31extern std::shared_ptr<CASC::Storage> CascStorage;
32
33Model::Model(std::string &filename) : filename(filename), header(), vertices(nullptr), indices(nullptr)
34{
35}
36
38{
39 CASCFile f(CascStorage, filename.c_str());
40
41 if (f.isEof())
42 {
43 f.close();
44 // Do not show this error on console to avoid confusion, the extractor can continue working even if some models fail to load
45 //printf("Error loading model %s\n", filename.c_str());
46 return false;
47 }
48
49 _unload();
50
51 uint32 m2start = 0;
52 char const* ptr = f.getBuffer();
53 while (m2start + 4 < f.getSize() && memcmp(ptr, "MD20", 4) != 0)
54 {
55 ++m2start;
56 ++ptr;
57 if (m2start + sizeof(ModelHeader) > f.getSize())
58 return false;
59 }
60
61 memcpy(&header, f.getBuffer() + m2start, sizeof(ModelHeader));
64 {
65 f.seek(m2start);
69 for (uint32 i=0; i<header.nBoundingVertices; i++)
71 f.seek(m2start);
74 std::unique_ptr<uint16[]> tempindices = std::make_unique<uint16[]>(header.nBoundingTriangles);
75 f.read(tempindices.get(), header.nBoundingTriangles * 2);
76 std::copy_n(tempindices.get(), header.nBoundingTriangles, indices);
77 f.close();
78 }
79 else
80 {
81 //printf("not included %s\n", filename.c_str());
82 f.close();
83 return false;
84 }
85 return true;
86}
87
88bool Model::ConvertToVMAPModel(const char * outfilename)
89{
90 int N[12] = { };
91 FILE* output = fopen(outfilename, "wb");
92 if (!output)
93 {
94 printf("Can't create the output file '%s'\n", outfilename);
95 return false;
96 }
97 fwrite(VMAP::RAW_VMAP_MAGIC, 8, 1, output);
99 fwrite(&nVertices, sizeof(int), 1, output);
100 uint32 nofgroups = 1;
101 fwrite(&nofgroups, sizeof(uint32), 1, output);
102 fwrite(N, 4, 1, output);// RootWMOID
104 fwrite(&tcFlags, sizeof(ModelFlags), 1, output);
105 fwrite(N, 4 * 2, 1, output);// mogpFlags, groupWMOID
106 fwrite(&bounds, sizeof(AaBox3D), 1, output);//bbox, only needed for WMO currently
107 fwrite(N, 4, 1, output);// liquidflags
108 fwrite("GRP ", 4, 1, output);
109 uint32 branches = 1;
110 int wsize;
111 wsize = sizeof(branches) + sizeof(uint32) * branches;
112 fwrite(&wsize, sizeof(int), 1, output);
113 fwrite(&branches, sizeof(branches), 1, output);
115 fwrite(&nIndexes, sizeof(uint32), 1, output);
116 fwrite("INDX", 4, 1, output);
117 wsize = sizeof(uint32) + sizeof(unsigned short) * nIndexes;
118 fwrite(&wsize, sizeof(int), 1, output);
119 fwrite(&nIndexes, sizeof(uint32), 1, output);
120 if (nIndexes > 0)
121 {
122 for (uint32 i = 0; i < nIndexes; ++i)
123 {
124 if ((i % 3) - 1 == 0 && i + 1 < nIndexes)
125 {
126 uint32 tmp = indices[i];
127 indices[i] = indices[i + 1];
128 indices[i + 1] = tmp;
129 }
130 }
131 fwrite(indices, sizeof(uint32), nIndexes, output);
132 }
133
134 fwrite("VERT", 4, 1, output);
135 wsize = sizeof(int) + sizeof(float) * 3 * nVertices;
136 fwrite(&wsize, sizeof(int), 1, output);
137 fwrite(&nVertices, sizeof(int), 1, output);
138 if (nVertices > 0)
139 {
140 for (uint32 vpos = 0; vpos < nVertices; ++vpos)
141 {
142 float tmp = vertices[vpos].y;
143 vertices[vpos].y = -vertices[vpos].z;
144 vertices[vpos].z = tmp;
145 }
146
147 fwrite(vertices, sizeof(float) * 3, nVertices, output);
148 }
149
150 fclose(output);
151
152 return true;
153}
154
156{
157 return Vec3D(v.x, v.z, -v.y);
158}
159
160void Doodad::Extract(ADT::MDDF const& doodadDef, char const* ModelInstName, uint32 mapID, uint32 originalMapId, FILE* pDirfile, std::vector<ADTOutputCache>* dirfileCache)
161{
162 std::string tempname = Trinity::StringFormat("{}/{}", szWorkDirWmo, ModelInstName);
163 FILE* input = fopen(tempname.c_str(), "r+b");
164
165 if (!input)
166 return;
167
168 fseek(input, 8, SEEK_SET); // get the correct no of vertices
169 int nVertices;
170 int count = fread(&nVertices, sizeof(int), 1, input);
171 fclose(input);
172
173 if (count != 1 || nVertices == 0)
174 return;
175
176 // scale factor - divide by 1024. blizzard devs must be on crack, why not just use a float?
177 float sc = doodadDef.Scale / 1024.0f;
178
179 Vec3D position = fixCoords(doodadDef.Position);
180
181 uint8 nameSet = 0;// not used for models
182 uint32 uniqueId = GenerateUniqueObjectId(doodadDef.UniqueId, 0, false);
183 uint8 tcflags = 0;
184 if (mapID != originalMapId)
185 tcflags |= MOD_PARENT_SPAWN;
186
187 //write mapID, Flags, NameSet, UniqueId, Pos, Rot, Scale, name
188 fwrite(&mapID, sizeof(uint32), 1, pDirfile);
189 fwrite(&tcflags, sizeof(uint8), 1, pDirfile);
190 fwrite(&nameSet, sizeof(uint8), 1, pDirfile);
191 fwrite(&uniqueId, sizeof(uint32), 1, pDirfile);
192 fwrite(&position, sizeof(Vec3D), 1, pDirfile);
193 fwrite(&doodadDef.Rotation, sizeof(Vec3D), 1, pDirfile);
194 fwrite(&sc, sizeof(float), 1, pDirfile);
195 uint32 nlen = strlen(ModelInstName);
196 fwrite(&nlen, sizeof(uint32), 1, pDirfile);
197 fwrite(ModelInstName, sizeof(char), nlen, pDirfile);
198
199 if (dirfileCache)
200 {
201 dirfileCache->emplace_back();
202 ADTOutputCache& cacheModelData = dirfileCache->back();
203 cacheModelData.Flags = tcflags & ~MOD_PARENT_SPAWN;
204 cacheModelData.Data.resize(
205 sizeof(uint8) + // nameSet
206 sizeof(uint32) + // uniqueId
207 sizeof(Vec3D) + // position
208 sizeof(Vec3D) + // doodadDef.Rotation
209 sizeof(float) + // sc
210 sizeof(uint32) + // nlen
211 nlen); // ModelInstName
212
213 uint8* cacheData = cacheModelData.Data.data();
214#define CACHE_WRITE(value, size, cnt, dest) memcpy(dest, value, size * cnt); dest += size * cnt;
215
216 CACHE_WRITE(&nameSet, sizeof(uint8), 1, cacheData);
217 CACHE_WRITE(&uniqueId, sizeof(uint32), 1, cacheData);
218 CACHE_WRITE(&position, sizeof(Vec3D), 1, cacheData);
219 CACHE_WRITE(&doodadDef.Rotation, sizeof(Vec3D), 1, cacheData);
220 CACHE_WRITE(&sc, sizeof(float), 1, cacheData);
221 CACHE_WRITE(&nlen, sizeof(uint32), 1, cacheData);
222 CACHE_WRITE(ModelInstName, sizeof(char), nlen, cacheData);
223 }
224}
225
226void Doodad::ExtractSet(WMODoodadData const& doodadData, ADT::MODF const& wmo, bool isGlobalWmo, uint32 mapID, uint32 originalMapId,
227 FILE* pDirfile, std::vector<ADTOutputCache>* dirfileCache)
228{
229 if (doodadData.Sets.empty())
230 return;
231
232 G3D::Vector3 wmoPosition(wmo.Position.z, wmo.Position.x, wmo.Position.y);
233 G3D::Matrix3 wmoRotation = G3D::Matrix3::fromEulerAnglesZYX(G3D::toRadians(wmo.Rotation.y), G3D::toRadians(wmo.Rotation.x), G3D::toRadians(wmo.Rotation.z));
234
235 if (isGlobalWmo)
236 wmoPosition += G3D::Vector3(533.33333f * 32, 533.33333f * 32, 0.0f);
237
238 uint16 doodadId = 0;
239 auto extractSingleSet = [&](WMO::MODS const& doodadSetData)
240 {
241 for (uint16 doodadIndex : doodadData.References)
242 {
243 if (doodadIndex < doodadSetData.StartIndex ||
244 doodadIndex >= doodadSetData.StartIndex + doodadSetData.Count)
245 continue;
246
247 WMO::MODD const& doodad = doodadData.Spawns[doodadIndex];
248
249 std::string ModelInstName;
250 if (doodadData.Paths)
251 ModelInstName = GetPlainName(&doodadData.Paths[doodad.NameIndex]);
252 else if (doodadData.FileDataIds)
253 ModelInstName = Trinity::StringFormat("FILE{:08X}.xxx", doodadData.FileDataIds[doodad.NameIndex]);
254 else
255 ASSERT(false);
256
257 uint32 nlen = ModelInstName.length();
258 NormalizeFileName(ModelInstName.data(), nlen);
259 if (ModelInstName.ends_with(".mdx") || ModelInstName.ends_with(".mdl"))
260 ModelInstName.replace(ModelInstName.length() - 2, 2, "2");
261
262 std::string tempname = Trinity::StringFormat("{}/{}", szWorkDirWmo, ModelInstName);
263 FILE* input = fopen(tempname.c_str(), "r+b");
264 if (!input)
265 continue;
266
267 fseek(input, 8, SEEK_SET); // get the correct no of vertices
268 int nVertices;
269 int count = fread(&nVertices, sizeof(int), 1, input);
270 fclose(input);
271
272 if (count != 1 || nVertices == 0)
273 continue;
274
275 ASSERT(doodadId < std::numeric_limits<uint16>::max());
276 ++doodadId;
277
278 G3D::Vector3 position = wmoPosition + (wmoRotation * G3D::Vector3(doodad.Position.x, doodad.Position.y, doodad.Position.z));
279
280 Vec3D rotation;
281 (G3D::Quat(doodad.Rotation.X, doodad.Rotation.Y, doodad.Rotation.Z, doodad.Rotation.W)
282 .toRotationMatrix() * wmoRotation)
283 .toEulerAnglesXYZ(rotation.z, rotation.x, rotation.y);
284
285 rotation.z = G3D::toDegrees(rotation.z);
286 rotation.x = G3D::toDegrees(rotation.x);
287 rotation.y = G3D::toDegrees(rotation.y);
288
289 uint8 nameSet = 0; // not used for models
290 uint32 uniqueId = GenerateUniqueObjectId(wmo.UniqueId, doodadId, false);
291 uint8 tcflags = 0;
292 if (mapID != originalMapId)
293 tcflags |= MOD_PARENT_SPAWN;
294
295 //write mapID, Flags, NameSet, UniqueId, Pos, Rot, Scale, name
296 fwrite(&mapID, sizeof(uint32), 1, pDirfile);
297 fwrite(&tcflags, sizeof(uint8), 1, pDirfile);
298 fwrite(&nameSet, sizeof(uint8), 1, pDirfile);
299 fwrite(&uniqueId, sizeof(uint32), 1, pDirfile);
300 fwrite(&position, sizeof(Vec3D), 1, pDirfile);
301 fwrite(&rotation, sizeof(Vec3D), 1, pDirfile);
302 fwrite(&doodad.Scale, sizeof(float), 1, pDirfile);
303 fwrite(&nlen, sizeof(uint32), 1, pDirfile);
304 fwrite(ModelInstName.c_str(), sizeof(char), nlen, pDirfile);
305
306 if (dirfileCache)
307 {
308 dirfileCache->emplace_back();
309 ADTOutputCache& cacheModelData = dirfileCache->back();
310 cacheModelData.Flags = tcflags & ~MOD_PARENT_SPAWN;
311 cacheModelData.Data.resize(
312 sizeof(uint8) + // nameSet
313 sizeof(uint32) + // uniqueId
314 sizeof(Vec3D) + // position
315 sizeof(Vec3D) + // rotation
316 sizeof(float) + // doodad.Scale
317 sizeof(uint32) + // nlen
318 nlen); // ModelInstName
319
320 uint8* cacheData = cacheModelData.Data.data();
321 CACHE_WRITE(&nameSet, sizeof(uint8), 1, cacheData);
322 CACHE_WRITE(&uniqueId, sizeof(uint32), 1, cacheData);
323 CACHE_WRITE(&position, sizeof(Vec3D), 1, cacheData);
324 CACHE_WRITE(&rotation, sizeof(Vec3D), 1, cacheData);
325 CACHE_WRITE(&doodad.Scale, sizeof(float), 1, cacheData);
326 CACHE_WRITE(&nlen, sizeof(uint32), 1, cacheData);
327 CACHE_WRITE(ModelInstName.c_str(), sizeof(char), nlen, cacheData);
328 }
329 }
330 };
331
332 // first doodad set is always active
333 extractSingleSet(doodadData.Sets[0]);
334
335 if (wmo.DoodadSet != 0 && wmo.DoodadSet < doodadData.Sets.size())
336 extractSingleSet(doodadData.Sets[wmo.DoodadSet]);
337}
338
339#undef CACHE_WRITE
uint8_t uint8
Definition: Define.h:144
uint16_t uint16
Definition: Define.h:143
uint32_t uint32
Definition: Define.h:142
#define ASSERT
Definition: Errors.h:68
char const * GetPlainName(char const *FileName)
Definition: adtfile.cpp:24
void NormalizeFileName(char *name, size_t len)
Definition: adtfile.cpp:69
Definition: vec3d.h:135
void seekRelative(int offset)
Definition: cascfile.cpp:105
bool isEof()
Definition: cascfile.h:52
void seek(int offset)
Definition: cascfile.cpp:99
size_t read(void *dest, size_t bytes)
Definition: cascfile.cpp:80
char * getBuffer()
Definition: cascfile.h:50
size_t getSize()
Definition: cascfile.h:48
void close()
Definition: cascfile.cpp:111
Vec3D * vertices
Definition: model.h:45
ModelHeader header
Definition: model.h:44
std::string filename
Definition: model.h:42
bool open()
Definition: model.cpp:37
void _unload()
Definition: model.h:35
AaBox3D bounds
Definition: model.h:47
uint32 * indices
Definition: model.h:46
Model(std::string &filename)
Definition: model.cpp:33
bool ConvertToVMAPModel(char const *outfilename)
Definition: model.cpp:88
Definition: vec3d.h:25
float x
Definition: vec3d.h:27
float y
Definition: vec3d.h:27
float z
Definition: vec3d.h:27
std::shared_ptr< CASC::Storage > CascStorage
Definition: System.cpp:49
Vec3D fixCoordSystem(Vec3D const &v)
Definition: model.cpp:155
#define CACHE_WRITE(value, size, cnt, dest)
void Extract(ADT::MDDF const &doodadDef, char const *ModelInstName, uint32 mapID, uint32 originalMapId, FILE *pDirfile, std::vector< ADTOutputCache > *dirfileCache)
Definition: model.cpp:160
void ExtractSet(WMODoodadData const &doodadData, ADT::MODF const &wmo, bool isGlobalWmo, uint32 mapID, uint32 originalMapId, FILE *pDirfile, std::vector< ADTOutputCache > *dirfileCache)
Definition: model.cpp:226
std::string StringFormat(FormatString< Args... > fmt, Args &&... args)
Default TC string format function.
Definition: StringFormat.h:38
const char RAW_VMAP_MAGIC[]
std::vector< uint8 > Data
Definition: adtfile.h:56
uint8 Flags
Definition: adtfile.h:55
uint32 UniqueId
Definition: adtfile.h:31
Vec3D Position
Definition: adtfile.h:32
Vec3D Rotation
Definition: adtfile.h:33
uint16 Scale
Definition: adtfile.h:34
Vec3D Rotation
Definition: adtfile.h:43
uint32 UniqueId
Definition: adtfile.h:41
Vec3D Position
Definition: adtfile.h:42
uint16 DoodadSet
Definition: adtfile.h:46
uint32 nBoundingVertices
Definition: modelheaders.h:74
uint32 nBoundingTriangles
Definition: modelheaders.h:72
AaBox3D collisionBox
Definition: modelheaders.h:70
uint32 ofsBoundingVertices
Definition: modelheaders.h:75
uint32 ofsBoundingTriangles
Definition: modelheaders.h:73
float X
Definition: vec3d.h:253
float Z
Definition: vec3d.h:253
float Y
Definition: vec3d.h:253
float W
Definition: vec3d.h:253
std::unordered_set< uint16 > References
Definition: wmo.h:75
std::vector< WMO::MODS > Sets
Definition: wmo.h:71
std::vector< WMO::MODD > Spawns
Definition: wmo.h:74
std::unique_ptr< uint32[]> FileDataIds
Definition: wmo.h:73
std::unique_ptr< char[]> Paths
Definition: wmo.h:72
Definition: wmo.h:57
Quaternion Rotation
Definition: wmo.h:60
uint32 NameIndex
Definition: wmo.h:58
float Scale
Definition: wmo.h:61
Vec3D Position
Definition: wmo.h:59
Definition: wmo.h:49
uint32 GenerateUniqueObjectId(uint32 clientId, uint16 clientDoodadId, bool isWmo)
Definition: vmapexport.cpp:170
char const * szWorkDirWmo
Definition: vmapexport.cpp:74
ModelFlags
Definition: vmapexport.h:34
@ MOD_PARENT_SPAWN
Definition: vmapexport.h:29
static Vec3D fixCoords(Vec3D const &v)
Definition: wmo.h:67