WoW Model Viewer
Your premiere tool for viewing, equipping and animating World of Warcraft models.
Loading...
Searching...
No Matches
WoWModel.cpp
Go to the documentation of this file.
1#include "WoWModel.h"
2#include <algorithm>
3#include <cassert>
4#include <format>
5#include <ostream>
6#include <sstream>
7#include <vector>
8#include <map>
9#include <string>
10#include "AnimMapper.h"
11#include "Attachment.h"
12#include "CASCFile.h"
13#include "DB2Table.h"
14#include "Game.h"
15#include "GlobalSettings.h"
16#include "ModelColor.h"
17#include "ModelEvent.h"
18#include "ModelLight.h"
19#include "ModelRenderPass.h"
20#include "ModelTransparency.h"
21#include "video.h"
22#include "Logger.h"
23#include "string_utils.h"
24#include "glm/gtc/epsilon.hpp"
25#include "glm/gtc/type_ptr.hpp"
26#include "glm/gtx/norm.hpp"
27
28using std::endl;
29
30#define GL_BUFFER_OFFSET(i) ((char *)(0) + (i))
31
37
39{
40 LOG_INFO << "-----------------------------------------";
41
42 for (uint i = 0; i < textures.size(); i++)
43 LOG_INFO << "textures[" << i << "] =" << textures[i];
44
45 for (uint i = 0; i < specialTextures.size(); i++)
46 LOG_INFO << "specialTextures[" << i << "] =" << specialTextures[i];
47
48 for (uint i = 0; i < replaceTextures.size(); i++)
49 LOG_INFO << "replaceTextures[" << i << "] =" << replaceTextures[i];
50
51 LOG_INFO << " #### TEXTUREMANAGER ####";
53 LOG_INFO << " ########################";
54
55 for (uint i = 0; i < passes.size(); i++)
56 LOG_INFO << "passes[" << i << "] -> tex =" << passes[i]->tex << "specialTex" << passes[i]->specialTex <<
57 "useTex2" << passes[i]->useTex2;
58
59 LOG_INFO << "-----------------------------------------";
60}
61
63{
64 GLint bled;
65 LOG_INFO << "glGetAll Information";
66 LOG_INFO << "GL_ALPHA_TEST:" << glIsEnabled(GL_ALPHA_TEST);
67 LOG_INFO << "GL_BLEND:" << glIsEnabled(GL_BLEND);
68 LOG_INFO << "GL_CULL_FACE:" << glIsEnabled(GL_CULL_FACE);
69 glGetIntegerv(GL_FRONT_FACE, &bled);
70 if (bled == GL_CW)
71 {
72 LOG_INFO << "glFrontFace: GL_CW";
73 }
74 else if (bled == GL_CCW)
75 {
76 LOG_INFO << "glFrontFace: GL_CCW";
77 }
78 LOG_INFO << "GL_DEPTH_TEST:" << glIsEnabled(GL_DEPTH_TEST);
79 LOG_INFO << "GL_DEPTH_WRITEMASK:" << glIsEnabled(GL_DEPTH_WRITEMASK);
80 LOG_INFO << "GL_COLOR_MATERIAL:" << glIsEnabled(GL_COLOR_MATERIAL);
81 LOG_INFO << "GL_LIGHT0:" << glIsEnabled(GL_LIGHT0);
82 LOG_INFO << "GL_LIGHT1:" << glIsEnabled(GL_LIGHT1);
83 LOG_INFO << "GL_LIGHT2:" << glIsEnabled(GL_LIGHT2);
84 LOG_INFO << "GL_LIGHT3:" << glIsEnabled(GL_LIGHT3);
85 LOG_INFO << "GL_LIGHTING:" << glIsEnabled(GL_LIGHTING);
86 LOG_INFO << "GL_TEXTURE_2D:" << glIsEnabled(GL_TEXTURE_2D);
87 glGetIntegerv(GL_BLEND_SRC, &bled);
88 LOG_INFO << "GL_BLEND_SRC:" << bled;
89 glGetIntegerv(GL_BLEND_DST, &bled);
90 LOG_INFO << "GL_BLEND_DST:" << bled;
91}
92
94{
95 glDisable(GL_ALPHA_TEST);
96 glDisable(GL_BLEND);
97 glDisable(GL_COLOR_MATERIAL);
98 //glEnable(GL_CULL_FACE);
99 glDisable(GL_CULL_FACE);
100 glEnable(GL_DEPTH_TEST);
101 glDisable(GL_DEPTH_TEST);
102 glEnable(GL_LIGHT0);
103 glLightfv(GL_LIGHT0, GL_DIFFUSE, glm::value_ptr(glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)));
104 glLightfv(GL_LIGHT0, GL_AMBIENT, glm::value_ptr(glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)));
105 glLightfv(GL_LIGHT0, GL_SPECULAR, glm::value_ptr(glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)));
106 glDisable(GL_LIGHT1);
107 glDisable(GL_LIGHT2);
108 glDisable(GL_LIGHT3);
109 glDisable(GL_LIGHTING);
110 glDisable(GL_TEXTURE_2D);
111 glBlendFunc(GL_ONE, GL_ZERO);
112 glFrontFace(GL_CCW);
113 //glDepthMask(GL_TRUE);
114 glDepthFunc(GL_NEVER);
115}
116
117WoWModel::WoWModel(GameFile* file, bool forceAnim):
118 ManagedItem(""),
119 forceAnim(forceAnim),
120 gamefile(file)
121{
122 // Initiate our model variables.
123 trans = 1.0f;
124 rad = 1.0f;
125 pos_ = glm::vec3(0.0f, 0.0f, 0.0f);
126 rot_ = glm::vec3(0.0f, 0.0f, 0.0f);
127 scale_ = 1.0f;
128
129 specialTextures.resize(TEXTURE_MAX, -1);
131
132 for (short& i : attLookup)
133 i = -1;
134
135 for (short& i : keyBoneLookup)
136 i = -1;
137
138 dlist = 0;
139
140 hasCamera = false;
141 hasParticles = false;
142 replaceParticleColors = false;
144 creatureGeosetData.clear();
146
147 isWMO = false;
148 isMount = false;
149
150 showModel = false;
151 showBones = false;
152 showBounds = false;
153 showWireframe = false;
154 showParticles = false;
155 showTexture = true;
156 mirrored_ = false;
157
159
160 vbuf = nbuf = tbuf = 0;
161
162 origVertices.clear();
163 vertices = nullptr;
164 normals = nullptr;
165 texCoords = nullptr;
166 indices.clear();
167
168 animtime = 0;
169 anim = 0;
170 animManager = nullptr;
171 currentAnim = 0;
173 attachment = nullptr;
174
175 rawVertices.clear();
176 rawIndices.clear();
177 rawPasses.clear();
178 rawGeosets.clear();
179
180 mergedModelType = 0;
181
182 initCommon();
183}
184
186{
187 if (ok)
188 {
189 if (attachment)
190 attachment->setModel(nullptr);
191
192 // There is a small memory leak somewhere with the textures.
193 // Especially if the texture was built into the model.
194 // No matter what I try though I can't find the memory to unload.
195 if (header.nTextures)
196 {
197 // For character models, the texture isn't loaded into the texture manager, manually remove it
198 glDeleteTextures(1, &replaceTextures[1]);
199 delete animManager;
200 animManager = nullptr;
201
202 if (animated)
203 {
204 // unload all sorts of crap
205 // Need this if statement because VBO supported
206 // cards have already deleted it.
207 if (video.supportVBO)
208 {
209 glDeleteBuffersARB(1, &nbuf);
210 glDeleteBuffersARB(1, &vbuf);
211 glDeleteBuffersARB(1, &tbuf);
212
213 vertices = nullptr;
214 }
215
216 delete[] normals;
217 normals = nullptr;
218 delete[] vertices;
219 vertices = nullptr;
220 delete[] texCoords;
221 texCoords = nullptr;
222
223 indices.clear();
224 rawIndices.clear();
225 origVertices.clear();
226 rawVertices.clear();
227 texAnims.clear();
228 colors.clear();
229 transparency.clear();
230 lights.clear();
231 particleSystems.clear();
232 ribbons.clear();
233 events.clear();
234
235 for (const auto it : passes)
236 delete it;
237
238 for (const auto it : geosets)
239 delete it;
240 }
241 else
242 {
243 glDeleteLists(dlist, 1);
244 }
245 }
246 }
247}
248
250{
251 LOG_INFO << "id:" << a_header.id[0] << a_header.id[1] << a_header.id[2] << a_header.id[3];
252 LOG_INFO << "version:" << (int)a_header.version[0] << (int)a_header.version[1] << (int)a_header.version[2] << (int)
253 a_header.version[3];
254 LOG_INFO << "nameLength:" << a_header.nameLength;
255 LOG_INFO << "nameOfs:" << a_header.nameOfs;
256 LOG_INFO << "GlobalModelFlags:" << a_header.GlobalModelFlags;
257 LOG_INFO << "nGlobalSequences:" << a_header.nGlobalSequences;
258 LOG_INFO << "ofsGlobalSequences:" << a_header.ofsGlobalSequences;
259 LOG_INFO << "nAnimations:" << a_header.nAnimations;
260 LOG_INFO << "ofsAnimations:" << a_header.ofsAnimations;
261 LOG_INFO << "nAnimationLookup:" << a_header.nAnimationLookup;
262 LOG_INFO << "ofsAnimationLookup:" << a_header.ofsAnimationLookup;
263 LOG_INFO << "nBones:" << a_header.nBones;
264 LOG_INFO << "ofsBones:" << a_header.ofsBones;
265 LOG_INFO << "nKeyBoneLookup:" << a_header.nKeyBoneLookup;
266 LOG_INFO << "ofsKeyBoneLookup:" << a_header.ofsKeyBoneLookup;
267 LOG_INFO << "nVertices:" << a_header.nVertices;
268 LOG_INFO << "ofsVertices:" << a_header.ofsVertices;
269 LOG_INFO << "nViews:" << a_header.nViews;
270 LOG_INFO << "nColors:" << a_header.nColors;
271 LOG_INFO << "ofsColors:" << a_header.ofsColors;
272 LOG_INFO << "nTextures:" << a_header.nTextures;
273 LOG_INFO << "ofsTextures:" << a_header.ofsTextures;
274 LOG_INFO << "nTransparency:" << a_header.nTransparency;
275 LOG_INFO << "ofsTransparency:" << a_header.ofsTransparency;
276 LOG_INFO << "nTexAnims:" << a_header.nTexAnims;
277 LOG_INFO << "ofsTexAnims:" << a_header.ofsTexAnims;
278 LOG_INFO << "nTexReplace:" << a_header.nTexReplace;
279 LOG_INFO << "ofsTexReplace:" << a_header.ofsTexReplace;
280 LOG_INFO << "nTexFlags:" << a_header.nTexFlags;
281 LOG_INFO << "ofsTexFlags:" << a_header.ofsTexFlags;
282 LOG_INFO << "nBoneLookup:" << a_header.nBoneLookup;
283 LOG_INFO << "ofsBoneLookup:" << a_header.ofsBoneLookup;
284 LOG_INFO << "nTexLookup:" << a_header.nTexLookup;
285 LOG_INFO << "ofsTexLookup:" << a_header.ofsTexLookup;
286 LOG_INFO << "nTexUnitLookup:" << a_header.nTexUnitLookup;
287 LOG_INFO << "ofsTexUnitLookup:" << a_header.ofsTexUnitLookup;
288 LOG_INFO << "nTransparencyLookup:" << a_header.nTransparencyLookup;
289 LOG_INFO << "ofsTransparencyLookup:" << a_header.ofsTransparencyLookup;
290 LOG_INFO << "nTexAnimLookup:" << a_header.nTexAnimLookup;
291 LOG_INFO << "ofsTexAnimLookup:" << a_header.ofsTexAnimLookup;
292
293 // LOG_INFO << "collisionSphere :";
294 // displaySphere(a_header.collisionSphere);
295 // LOG_INFO << "boundSphere :";
296 // displaySphere(a_header.boundSphere);
297
298 LOG_INFO << "nBoundingTriangles:" << a_header.nBoundingTriangles;
299 LOG_INFO << "ofsBoundingTriangles:" << a_header.ofsBoundingTriangles;
300 LOG_INFO << "nBoundingVertices:" << a_header.nBoundingVertices;
301 LOG_INFO << "ofsBoundingVertices:" << a_header.ofsBoundingVertices;
302 LOG_INFO << "nBoundingNormals:" << a_header.nBoundingNormals;
303 LOG_INFO << "ofsBoundingNormals:" << a_header.ofsBoundingNormals;
304
305 LOG_INFO << "nAttachments:" << a_header.nAttachments;
306 LOG_INFO << "ofsAttachments:" << a_header.ofsAttachments;
307 LOG_INFO << "nAttachLookup:" << a_header.nAttachLookup;
308 LOG_INFO << "ofsAttachLookup:" << a_header.ofsAttachLookup;
309 LOG_INFO << "nEvents:" << a_header.nEvents;
310 LOG_INFO << "ofsEvents:" << a_header.ofsEvents;
311 LOG_INFO << "nLights:" << a_header.nLights;
312 LOG_INFO << "ofsLights:" << a_header.ofsLights;
313 LOG_INFO << "nCameras:" << a_header.nCameras;
314 LOG_INFO << "ofsCameras:" << a_header.ofsCameras;
315 LOG_INFO << "nCameraLookup:" << a_header.nCameraLookup;
316 LOG_INFO << "ofsCameraLookup:" << a_header.ofsCameraLookup;
317 LOG_INFO << "nRibbonEmitters:" << a_header.nRibbonEmitters;
318 LOG_INFO << "ofsRibbonEmitters:" << a_header.ofsRibbonEmitters;
319 LOG_INFO << "nParticleEmitters:" << a_header.nParticleEmitters;
320 LOG_INFO << "ofsParticleEmitters:" << a_header.ofsParticleEmitters;
321}
322
324{
325 // see if we have any animated bones
326 ModelBoneDef* bo = reinterpret_cast<ModelBoneDef*>(gamefile->getBuffer() + header.ofsBones);
327
328 animGeometry = false;
329 animBones = false;
330 ind = false;
331
332 for (auto ov_it = origVertices.begin(), ov_end = origVertices.end(); (ov_it != ov_end) && !animGeometry; ++ov_it)
333 {
334 for (size_t b = 0; b < 4; b++)
335 {
336 if (ov_it->weights[b] > 0)
337 {
338 const ModelBoneDef& bb = bo[ov_it->bones[b]];
339 if (bb.translation.type || bb.rotation.type || bb.scaling.type || (bb.flags & MODELBONE_BILLBOARD))
340 {
341 if (bb.flags & MODELBONE_BILLBOARD)
342 {
343 // if we have billboarding, the model will need per-instance animation
344 ind = true;
345 }
346 animGeometry = true;
347 break;
348 }
349 }
350 }
351 }
352
353 if (animGeometry)
354 {
355 animBones = true;
356 }
357 else
358 {
359 for (uint i = 0; i < bones.size(); i++)
360 {
361 const ModelBoneDef& bb = bo[i];
362 if (bb.translation.type || bb.rotation.type || bb.scaling.type)
363 {
364 animBones = true;
365 animGeometry = true;
366 break;
367 }
368 }
369 }
370
371 bool animMisc = header.nCameras > 0 || // why waste time, pretty much all models with cameras need animation
372 header.nLights > 0 || // same here
375
376 if (animMisc)
377 animBones = true;
378
379 // animated colors
380 if (header.nColors)
381 {
382 const ModelColorDef* cols = reinterpret_cast<ModelColorDef*>(gamefile->getBuffer() + header.ofsColors);
383 for (size_t i = 0; i < header.nColors; i++)
384 {
385 if (cols[i].color.type != 0 || cols[i].opacity.type != 0)
386 {
387 animMisc = true;
388 break;
389 }
390 }
391 }
392
393 // animated opacity
394 if (header.nTransparency && !animMisc)
395 {
396 const ModelTransDef* trs = reinterpret_cast<ModelTransDef*>(gamefile->getBuffer() + header.ofsTransparency);
397 for (size_t i = 0; i < header.nTransparency; i++)
398 {
399 if (trs[i].trans.type != 0)
400 {
401 animMisc = true;
402 break;
403 }
404 }
405 }
406
407 // guess not...
408 return animGeometry || (header.nTexAnims > 0) || animMisc;
409}
410
412{
413 // --
414 ok = false;
415
416 if (!gamefile)
417 return;
418
419 if (!gamefile->open() || gamefile->isEof() || (gamefile->getSize() < sizeof(ModelHeader)))
420 {
421 LOG_ERROR << "Unable to load model:" << gamefile->fullname();
422 gamefile->close();
423 return;
424 }
425
426 if (!gamefile->setChunk("MD21"))
427 {
428 LOG_ERROR << "Unable to set chunk to MD21 for model:" << gamefile->fullname();
429 gamefile->close();
430 return;
431 }
432
434
436 cd.reset(this);
437
438 std::string tempname = gamefile->fullname();
439
440 ok = true;
441
442 memcpy(&header, gamefile->getBuffer(), sizeof(ModelHeader));
443
444 LOG_INFO << "Loading model:" << tempname.c_str() << "size:" << gamefile->getSize();
445
447
448 if (header.id[0] != 'M' && header.id[1] != 'D' && header.id[2] != '2' && header.id[3] != '0')
449 {
450 LOG_ERROR << "Invalid model! May be corrupted. Header id:" << header.id[0] << header.id[1] << header.id[2] <<
451 header.id[3];
452
453 ok = false;
454 gamefile->close();
455 return;
456 }
457
458 modelname = tempname;
459 auto list = core::split(modelname, '\\');
460 auto lastName = list[list.size() - 1];
461 if (auto pos = lastName.find(".m2"); pos != std::string::npos)
462 lastName.erase(pos, 3);
463 setName(lastName);
464
465 // Error check
466 // 10 1 0 0 = WoW 5.0 models (as of 15464)
467 // 10 1 0 0 = WoW 4.0.0.12319 models
468 // 9 1 0 0 = WoW 4.0 models
469 // 8 1 0 0 = WoW 3.0 models
470 // 4 1 0 0 = WoW 2.0 models
471 // 0 1 0 0 = WoW 1.0 models
472
474 {
475 LOG_ERROR << "Unable to load the Model \"" << tempname.c_str() << "\", appears to be corrupted.";
476 gamefile->close();
477 return;
478 }
479
480 // init race info
481
482
483 if (gamefile->isChunked() && gamefile->setChunk("SKID"))
484 {
485 uint32 skelFileID;
486 gamefile->read(&skelFileID, sizeof(skelFileID));
487 GameFile* skelFile = GAMEDIRECTORY.getFile(skelFileID);
488
489 if (skelFile->open())
490 {
491 if (skelFile->setChunk("SKS1"))
492 {
493 SKS1 sks1;
494 memcpy(&sks1, skelFile->getBuffer(), sizeof(SKS1));
495
496 if (sks1.nGlobalSequences > 0)
497 {
498 // vector.assign() isn't working:
499 // globalSequences.assign(skelFile->getBuffer() + sks1.ofsGlobalSequences, skelFile->getBuffer() + sks1.ofsGlobalSequences + sks1.nGlobalSequences);
500 uint32* buffer = new uint32[sks1.nGlobalSequences];
501 memcpy(buffer, skelFile->getBuffer() + sks1.ofsGlobalSequences,
502 sizeof(uint32) * sks1.nGlobalSequences);
503 globalSequences.assign(buffer, buffer + sks1.nGlobalSequences);
504 delete[] buffer;
505 }
506
507 // let's try to read parent skel file if needed
508 if (skelFile->setChunk("SKPD"))
509 {
510 SKPD skpd;
511 memcpy(&skpd, skelFile->getBuffer(), sizeof(SKPD));
512
513 GameFile* parentFile = GAMEDIRECTORY.getFile(skpd.parentFileId);
514
515 if (parentFile && parentFile->open() && parentFile->setChunk("SKS1"))
516 {
517 SKS1 Sks1;
518 memcpy(&Sks1, parentFile->getBuffer(), sizeof(SKS1));
519
520 if (Sks1.nGlobalSequences > 0)
521 {
522 uint32* buffer = new uint32[Sks1.nGlobalSequences];
523 memcpy(buffer, parentFile->getBuffer() + Sks1.ofsGlobalSequences,
524 sizeof(uint32) * Sks1.nGlobalSequences);
525 for (uint i = 0; i < Sks1.nGlobalSequences; i++)
526 globalSequences.push_back(buffer[i]);
527 delete[] buffer;
528 }
529
530 parentFile->close();
531 }
532 }
533 }
534 skelFile->close();
535 }
536 gamefile->setChunk("MD21");
537 }
538 else if (header.nGlobalSequences)
539 {
540 // vector.assign() isn't working:
541 // globalSequences.assign(gamefile->getBuffer() + header.ofsGlobalSequences, gamefile->getBuffer() + header.ofsGlobalSequences + header.nGlobalSequences);
542 uint32* buffer = new uint32[header.nGlobalSequences];
544 globalSequences.assign(buffer, buffer + header.nGlobalSequences);
545 delete[] buffer;
546 }
547
548 if (gamefile->isChunked() && gamefile->setChunk("SFID"))
549 {
550 uint32 skinfile;
551
552 if (header.nViews > 0)
553 {
554 for (uint i = 0; i < header.nViews; i++)
555 {
556 gamefile->read(&skinfile, sizeof(skinfile));
557 skinFileIDs.push_back(skinfile);
558 LOG_INFO << "Adding skin file" << i << ":" << skinfile;
559 // If the first view is the best, and we don't need to switch to a lower one, then maybe we don't need to store all these file IDs, but we can for now.
560 }
561 }
562 // LOD .skin file IDs are next in SFID, but we'll ignore them. They're probably unnecessary in a model viewer.
563
564 gamefile->setChunk("MD21");
565 }
566
567 if (forceAnim)
568 animBones = true;
569
570 // Ready to render.
571 showModel = true;
572 alpha_ = 1.0f;
573
575 memcpy(buffer, gamefile->getBuffer() + header.ofsVertices, sizeof(ModelVertex) * header.nVertices);
576 rawVertices.assign(buffer, buffer + header.nVertices);
577 delete[] buffer;
578
580
581 // This data is needed for both VBO and non-VBO cards.
582 vertices = new glm::vec3[origVertices.size()];
583 normals = new glm::vec3[origVertices.size()];
584
585 uint i = 0;
586 for (auto ov_it = origVertices.begin(), ov_end = origVertices.end(); ov_it != ov_end; i++, ++ov_it)
587 {
588 // Set the data for our vertices, normals from the model data
589 vertices[i] = ov_it->pos;
590 normals[i] = glm::normalize(ov_it->normal);
591
592 float len = glm::length2(ov_it->pos);
593 if (len > rad)
594 {
595 rad = len;
596 }
597 }
598
599 // model vertex radius
600 rad = sqrtf(rad);
601
602 // bounds
604 {
605 glm::vec3* Buffer = new glm::vec3[header.nBoundingVertices];
606 memcpy(Buffer, gamefile->getBuffer() + header.ofsBoundingVertices,
607 sizeof(glm::vec3) * header.nBoundingVertices);
608 bounds.assign(Buffer, Buffer + header.nBoundingVertices);
609 delete[] Buffer;
610 }
611
613 {
614 uint16* Buffer = new uint16[header.nBoundingTriangles];
616 boundTris.assign(Buffer, Buffer + header.nBoundingTriangles);
617 delete[] Buffer;
618 }
619
620 // textures
621 ModelTextureDef* texdef = reinterpret_cast<ModelTextureDef*>(gamefile->getBuffer() + header.ofsTextures);
622 if (header.nTextures)
623 {
625
626 std::vector<TXID> txids;
627
628 if (gamefile->isChunked() && gamefile->setChunk("TXID"))
629 {
631 gamefile->setChunk("MD21", false);
632 }
633
634 for (size_t I = 0; I < header.nTextures; I++)
635 {
636 /*
637 Texture Types
638 Texture type is 0 for textures whose file IDs or names are contained in the the model file.
639 The older implementation has full file paths, but the newer uses file data IDs
640 contained in a TXID chunk. We have to support both for now.
641
642 All other texture types (nonzero) are for textures that are obtained from other files.
643 For instance, in the NightElfFemale model, her eye glow is a type 0 texture and has a
644 file name. Her other 3 textures have types of 1, 2 and 6. The texture filenames for these
645 come from client database files:
646
647 DBFilesClient\CharSections.dbc
648 DBFilesClient\CreatureDisplayInfo.dbc
649 DBFilesClient\ItemDisplayInfo.dbc
650 (possibly more)
651
652 0 Texture given in filename
653 1 Body + clothes
654 2 Cape
655 6 Hair, beard
656 8 Tauren fur
657 11 Skin for creatures #1
658 12 Skin for creatures #2
659 13 Skin for creatures #3
660
661 Texture Flags
662 Value Meaning
663 1 Texture wrap X
664 2 Texture wrap Y
665 */
666
667 if (texdef[I].type == TEXTURE_FILENAME) // 0
668 {
669 GameFile* Tex;
670 if (txids.size() > 0)
671 {
672 Tex = GAMEDIRECTORY.getFile(txids[I].fileDataId);
673 }
674 else
675 {
676 std::string texname(reinterpret_cast<char*>(gamefile->getBuffer() + texdef[I].nameOfs));
677 Tex = GAMEDIRECTORY.getFile(texname);
678 }
679 textures[I] = TEXTUREMANAGER.add(Tex);
680 }
681 else // non-zero
682 {
683 // special texture - only on characters and such...
684 specialTextures[I] = texdef[I].type;
685
686 if (texdef[I].type == TEXTURE_WEAPON_BLADE) // a fix for weapons with type-3 textures.
688 GAMEDIRECTORY.getFile(R"(Item\ObjectComponents\Weapon\ArmorReflect4.BLP)"));
689 }
690 }
691 }
692
693 /*
694 // replacable textures - it seems to be better to get this info from the texture types
695 if (header.nTexReplace) {
696 size_t m = header.nTexReplace;
697 if (m>16) m = 16;
698 int16 *texrep = (int16*)(gamefile->getBuffer() + header.ofsTexReplace);
699 for (size_t i=0; i<m; i++) specialTextures[i] = texrep[i];
700 }
701 */
702
703 if (gamefile->isChunked() && gamefile->setChunk("SKID"))
704 {
705 uint32 skelFileID;
706 gamefile->read(&skelFileID, sizeof(skelFileID));
707 GameFile* skelFile = GAMEDIRECTORY.getFile(skelFileID);
708
709 if (skelFile->open())
710 {
711 if (skelFile->setChunk("SKA1"))
712 {
713 SKA1 ska1;
714 memcpy(&ska1, skelFile->getBuffer(), sizeof(SKA1));
716 ModelAttachmentDef* attachments = reinterpret_cast<ModelAttachmentDef*>(skelFile->getBuffer() + ska1.ofsAttachments);
717 for (size_t I = 0; I < ska1.nAttachments; I++)
718 {
719 ModelAttachment att;
720 att.model = this;
721 att.init(attachments[I]);
722 atts.push_back(att);
723 }
724
726 if (ska1.nAttachLookup > 0)
727 {
728 int16* p = reinterpret_cast<int16*>(skelFile->getBuffer() + ska1.ofsAttachLookup);
729 if (ska1.nAttachLookup > ATT_MAX)
730 LOG_ERROR << "Model AttachLookup" << ska1.nAttachLookup << "over" << ATT_MAX;
731 for (size_t I = 0; I < ska1.nAttachLookup; I++)
732 {
733 if (I > ATT_MAX - 1)
734 break;
735 attLookup[I] = p[I];
736 }
737 }
738 }
739 skelFile->close();
740 }
741 gamefile->setChunk("MD21");
742 }
743 else
744 {
745 // attachments
747 {
748 ModelAttachmentDef* attachments = reinterpret_cast<ModelAttachmentDef*>(gamefile->getBuffer() + header.ofsAttachments);
749 for (size_t I = 0; I < header.nAttachments; I++)
750 {
751 ModelAttachment att;
752 att.model = this;
753 att.init(attachments[I]);
754 atts.push_back(att);
755 }
756 }
757
759 {
760 int16* p = reinterpret_cast<int16*>(gamefile->getBuffer() + header.ofsAttachLookup);
762 LOG_ERROR << "Model AttachLookup" << header.nAttachLookup << "over" << ATT_MAX;
763 for (size_t I = 0; I < header.nAttachLookup; I++)
764 {
765 if (I > ATT_MAX - 1)
766 break;
767 attLookup[I] = p[I];
768 }
769 }
770 }
771
772
773 // init colors
774 if (header.nColors)
775 {
776 colors.resize(header.nColors);
777 ModelColorDef* colorDefs = reinterpret_cast<ModelColorDef*>(gamefile->getBuffer() + header.ofsColors);
778 for (uint I = 0; I < colors.size(); I++)
779 colors[I].init(gamefile, colorDefs[I], globalSequences);
780 }
781
782 // init transparency
784 {
786 ModelTransDef* trDefs = reinterpret_cast<ModelTransDef*>(gamefile->getBuffer() + header.ofsTransparency);
787 for (uint I = 0; I < header.nTransparency; I++)
788 transparency[I].init(gamefile, trDefs[I], globalSequences);
789 }
790
791 if (header.nViews)
792 {
793 // just use the first LOD/view
794 // First LOD/View being the worst?
795 // TODO: Add support for selecting the LOD.
796 // int viewLOD = 0; // sets LOD to worst
797 // int viewLOD = header.nViews - 1; // sets LOD to best
798 setLOD(0); // Set the default Level of Detail to the best possible.
799 }
800
801 // proceed with specialized init depending on model "type"
802
803 animated = isAnimated() || forceAnim; // isAnimated will set animGeometry
804
805 if (animated)
806 initAnimated();
807 else
808 initStatic();
809
810 if (const DB2Table* cmdTbl = WOWDB.getTable("CreatureModelData"))
811 {
812 for (const auto& row : *cmdTbl)
813 {
814 if (row.getUInt("FileDataID") == static_cast<uint32_t>(gamefile->fileDataId()))
815 {
816 creatureGeosetDataID = static_cast<int>(row.getUInt("CreatureGeosetDataID"));
817 break;
818 }
819 }
820 }
821
822 gamefile->close();
823}
824
826{
827 dlist = glGenLists(1);
828 glNewList(dlist, GL_COMPILE);
829
830 drawModel();
831
832 glEndList();
833
834 // clean up vertices, indices etc
835 delete[] vertices;
836 vertices = nullptr;
837 delete[] normals;
838 normals = nullptr;
839 indices.clear();
840}
841
843{
844 // This mapping links *_sdr character models to their HD equivalents, so they can get race info for display.
845 // *_sdr models are actually now obsolete and the database that used to provide this info is now empty,
846 // but while the models are still appearing in the model tree we may as well keep them working, so they
847 // don't look broken:
848 std::map<int, int> SDReplacementModel = // {SDRFileID , HDFileID}
849 {
850 {1838568, 119369}, {1838570, 119376}, {1838201, 307453}, {1838592, 307454}, {1853956, 535052},
851 {1853610, 589715}, {1838560, 878772}, {1838566, 900914}, {1838578, 917116}, {1838574, 921844},
852 {1838564, 940356}, {1838580, 949470}, {1838562, 950080}, {1838584, 959310}, {1838586, 968705},
853 {1838576, 974343}, {1839008, 986648}, {1838582, 997378}, {1838572, 1000764}, {1839253, 1005887},
854 {1838385, 1011653}, {1838588, 1018060}, {1822372, 1022598}, {1838590, 1022938}, {1853408, 1100087},
855 {1839709, 1100258}, {1825438, 1593999}, {1839042, 1620605}, {1858265, 1630218}, {1859379, 1630402},
856 {1900779, 1630447}, {1894572, 1662187}, {1859345, 1733758}, {1858367, 1734034}, {1858099, 1810676},
857 {1857801, 1814471}, {1892825, 1890763}, {1892543, 1890765}, {1968838, 1968587}, {1842700, 1000764}
858 };
859
860 auto fdid = gamefile->fileDataId();
861 if (SDReplacementModel.count(fdid))
862 // if it's an old *_sdr model, use the file ID of its HD counterpart for race info
863 fdid = SDReplacementModel[fdid];
864
866 LOG_ERROR << "Unable to retrieve race infos for model" << gamefile->fullname() << gamefile->fileDataId();
867}
868
870{
871 std::vector<TXID> txids;
872
873 if (f->setChunk("TXID"))
874 {
875 TXID txid;
876 while (!f->isEof())
877 {
878 f->read(&txid, sizeof(TXID));
879 txids.push_back(txid);
880 }
881 }
882 return txids;
883}
884
886{
887 std::vector<AFID> afids;
888
889 if (f->setChunk("AFID"))
890 {
891 AFID afid;
892 while (!f->isEof())
893 {
894 f->read(&afid, sizeof(AFID));
895 if (afid.fileId != 0)
896 afids.push_back(afid);
897 }
898 }
899
900 return afids;
901}
902
903void WoWModel::readAnimsFromFile(GameFile* f, std::vector<AFID>& afids, modelAnimData& data, uint32 nAnimations,
904 uint32 ofsAnimation, uint32 nAnimationLookup, uint32 ofsAnimationLookup)
905{
906 for (uint i = 0; i < nAnimations; i++)
907 {
909 memcpy(&a, f->getBuffer() + ofsAnimation + i * sizeof(ModelAnimation), sizeof(ModelAnimation));
910
911 anims.push_back(a);
912
913 GameFile* Anim = nullptr;
914
915 // if we have animation file ids from AFID chunk, use them
916 if (afids.size() > 0)
917 {
918 for (const auto it : afids)
919 {
920 if ((it.animId == anims[i].animID) && (it.subAnimId == anims[i].subAnimID))
921 {
922 Anim = GAMEDIRECTORY.getFile(it.fileId);
923 break;
924 }
925 }
926 }
927 else // else use file naming to get them
928 {
929 auto tempname = modelname;
930 auto mpos = tempname.rfind(".m2");
931 if (mpos == std::string::npos) mpos = tempname.rfind(".M2");
932 if (mpos != std::string::npos) tempname.erase(mpos);
933 auto animFileName = std::format("{}{:04d}-{:02d}.anim", tempname, anims[i].animID, anims[i].subAnimID);
934 Anim = GAMEDIRECTORY.getFile(animFileName);
935 }
936
937 if (Anim && Anim->open())
938 {
939 Anim->setChunk("AFSB"); // try to set chunk if it exist, no effect if there is no AFSB chunk present
940 {
941 auto animIt = data.animfiles.find(anims[i].animID);
942 if (animIt != data.animfiles.end())
943 LOG_INFO << "WARNING - replacing" << data.animfiles[anims[i].animID].first->fullname() << "by" <<
944 Anim->fullname();
945 }
946
947 data.animfiles[anims[i].animID] = std::make_pair(Anim, f);
948 }
949 }
950
951 // Index at ofsAnimations which represents the animation in AnimationData.dbc. -1 if none.
952 if (nAnimationLookup > 0)
953 {
954 // for unknown reason, using assign() on vector doesn't work
955 // use intermediate buffer and push back instead...
956 int16* buffer = new int16[nAnimationLookup];
957 memcpy(buffer, f->getBuffer() + ofsAnimationLookup, sizeof(int16) * nAnimationLookup);
958 for (uint i = 0; i < nAnimationLookup; i++)
959 animLookups.push_back(buffer[i]);
960
961 delete[] buffer;
962 }
963}
964
966{
967 modelAnimData data;
969
970 if (gamefile->isChunked() && gamefile->setChunk("SKID"))
971 {
972 uint32 skelFileID;
973 gamefile->read(&skelFileID, sizeof(skelFileID));
974 GameFile* skelFile = GAMEDIRECTORY.getFile(skelFileID);
975
976 if (skelFile->open())
977 {
978 // skelFile->dumpStructure();
979 std::vector<AFID> afids = readAFIDSFromFile(skelFile);
980
981 if (skelFile->setChunk("SKS1"))
982 {
983 SKS1 sks1;
984
985 // let's try if there is a parent skel file to read
986 GameFile* parentFile = nullptr;
987 if (skelFile->setChunk("SKPD"))
988 {
989 SKPD skpd;
990 skelFile->read(&skpd, sizeof(skpd));
991
992 parentFile = GAMEDIRECTORY.getFile(skpd.parentFileId);
993
994 if (parentFile && parentFile->open())
995 {
996 // parentFile->dumpStructure();
997 afids = readAFIDSFromFile(parentFile);
998
999 if (parentFile->setChunk("SKS1"))
1000 {
1001 SKS1 Sks1;
1002 parentFile->read(&Sks1, sizeof(Sks1));
1003 readAnimsFromFile(parentFile, afids, data, Sks1.nAnimations, Sks1.ofsAnimations,
1005 }
1006
1007 parentFile->close();
1008 }
1009 }
1010 else
1011 {
1012 skelFile->read(&sks1, sizeof(sks1));
1013 memcpy(&sks1, skelFile->getBuffer(), sizeof(SKS1));
1014 readAnimsFromFile(skelFile, afids, data, sks1.nAnimations, sks1.ofsAnimations,
1016 }
1017
1018 animManager = new AnimManager(*this);
1019
1020 // init bones...
1021 if (skelFile->setChunk("SKB1"))
1022 {
1023 GameFile* fileToUse = skelFile;
1024 if (parentFile)
1025 {
1026 if (parentFile->open())
1027 {
1028 parentFile->setChunk("SKB1");
1029 skelFile->close();
1030 fileToUse = parentFile;
1031 }
1032 else
1033 {
1034 LOG_ERROR << "Failed to open parentFile in WoWModel::initAnimated";
1035 }
1036 }
1037
1038 SKB1 skb1;
1039 fileToUse->read(&skb1, sizeof(skb1));
1040 memcpy(&skb1, fileToUse->getBuffer(), sizeof(SKB1));
1041 bones.resize(skb1.nBones);
1042 ModelBoneDef* mb = reinterpret_cast<ModelBoneDef*>(fileToUse->getBuffer() + skb1.ofsBones);
1043
1044 for (uint i = 0; i < anims.size(); i++)
1045 data.animIndexToAnimId[i] = anims[i].animID;
1046
1047 for (size_t i = 0; i < skb1.nBones; i++)
1048 bones[i].initV3(*fileToUse, mb[i], data);
1049
1050 // Block keyBoneLookup is a lookup table for Key Skeletal Bones, hands, arms, legs, etc.
1051 if (skb1.nKeyBoneLookup < BONE_MAX)
1052 {
1053 memcpy(keyBoneLookup, fileToUse->getBuffer() + skb1.ofsKeyBoneLookup,
1054 sizeof(int16) * skb1.nKeyBoneLookup);
1055 }
1056 else
1057 {
1058 memcpy(keyBoneLookup, fileToUse->getBuffer() + skb1.ofsKeyBoneLookup, sizeof(int16) * BONE_MAX);
1059 LOG_ERROR << "KeyBone number" << skb1.nKeyBoneLookup << "over" << BONE_MAX;
1060 }
1061 fileToUse->close();
1062 }
1063 }
1064 skelFile->close();
1065 }
1066 gamefile->setChunk("MD21", false);
1067 }
1068 else if (header.nAnimations > 0)
1069 {
1070 std::vector<AFID> afids;
1071
1072 if (gamefile->isChunked() && gamefile->setChunk("AFID"))
1073 {
1074 afids = readAFIDSFromFile(gamefile);
1075 gamefile->setChunk("MD21", false);
1076 }
1077
1080
1081 animManager = new AnimManager(*this);
1082
1083 // init bones...
1084 bones.resize(header.nBones);
1085 ModelBoneDef* mb = reinterpret_cast<ModelBoneDef*>(gamefile->getBuffer() + header.ofsBones);
1086
1087 for (uint i = 0; i < anims.size(); i++)
1088 data.animIndexToAnimId[i] = anims[i].animID;
1089
1090 for (uint i = 0; i < bones.size(); i++)
1091 bones[i].initV3(*gamefile, mb[i], data);
1092
1093 // Block keyBoneLookup is a lookup table for Key Skeletal Bones, hands, arms, legs, etc.
1095 {
1097 sizeof(int16) * header.nKeyBoneLookup);
1098 }
1099 else
1100 {
1102 LOG_ERROR << "KeyBone number" << header.nKeyBoneLookup << "over" << BONE_MAX;
1103 }
1104 }
1105
1106 // free MPQFile
1107 for (auto it : data.animfiles)
1108 {
1109 if (it.second.first != nullptr)
1110 it.second.first->close();
1111 }
1112
1113 const size_t size = (origVertices.size() * sizeof(float));
1114 vbufsize = (3 * size); // we multiple by 3 for the x, y, z positions of the vertex
1115
1116 texCoords = new glm::vec2[origVertices.size()];
1117 auto ov_it = origVertices.begin();
1118 for (size_t i = 0; i < origVertices.size(); i++, ++ov_it)
1119 texCoords[i] = ov_it->texcoords;
1120
1121 if (video.supportVBO)
1122 {
1123 // Vert buffer
1124 glGenBuffersARB(1, &vbuf);
1125 glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbuf);
1126 glBufferDataARB(GL_ARRAY_BUFFER_ARB, vbufsize, vertices, GL_STATIC_DRAW_ARB);
1127 delete[] vertices;
1128 vertices = nullptr;
1129
1130 // Texture buffer
1131 glGenBuffersARB(1, &tbuf);
1132 glBindBufferARB(GL_ARRAY_BUFFER_ARB, tbuf);
1133 glBufferDataARB(GL_ARRAY_BUFFER_ARB, 2 * size, texCoords, GL_STATIC_DRAW_ARB);
1134 delete[] texCoords;
1135 texCoords = nullptr;
1136
1137 // normals buffer
1138 glGenBuffersARB(1, &nbuf);
1139 glBindBufferARB(GL_ARRAY_BUFFER_ARB, nbuf);
1140 glBufferDataARB(GL_ARRAY_BUFFER_ARB, vbufsize, normals, GL_STATIC_DRAW_ARB);
1141 delete[] normals;
1142 normals = nullptr;
1143
1144 // clean bind
1145 glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
1146 }
1147
1148 if (header.nTexAnims > 0)
1149 {
1150 texAnims.resize(header.nTexAnims);
1151 ModelTexAnimDef* ta = reinterpret_cast<ModelTexAnimDef*>(gamefile->getBuffer() + header.ofsTexAnims);
1152
1153 for (uint i = 0; i < texAnims.size(); i++)
1154 texAnims[i].init(gamefile, ta[i], globalSequences);
1155 }
1156
1157 if (header.nEvents)
1158 {
1159 ModelEventDef* edefs = reinterpret_cast<ModelEventDef*>(gamefile->getBuffer() + header.ofsEvents);
1160 events.resize(header.nEvents);
1161 for (uint i = 0; i < events.size(); i++)
1162 events[i].init(edefs[i]);
1163 }
1164
1165 // particle systems
1167 {
1168 M2ParticleDef* pdefs = reinterpret_cast<M2ParticleDef*>(gamefile->getBuffer() + header.ofsParticleEmitters);
1169 M2ParticleDef* pdef;
1171 hasParticles = true;
1172 showParticles = true;
1173 for (uint i = 0; i < particleSystems.size(); i++)
1174 {
1175 pdef = (M2ParticleDef*)&pdefs[i];
1176 particleSystems[i].model = this;
1177 particleSystems[i].init(gamefile, *pdef, globalSequences);
1178 int pci = particleSystems[i].particleColID;
1179 if (pci && (std::find(replacableParticleColorIDs.begin(),
1181 replacableParticleColorIDs.push_back(pci);
1182 }
1183 }
1184
1185 // ribbons
1187 {
1190 for (uint i = 0; i < ribbons.size(); i++)
1191 {
1192 ribbons[i].model = this;
1193 ribbons[i].init(gamefile, rdefs[i], globalSequences);
1194 }
1195 }
1196
1197 // Cameras
1198 if (header.nCameras > 0)
1199 {
1200 if (header.version[0] <= 9)
1201 {
1202 ModelCameraDef* camDefs = reinterpret_cast<ModelCameraDef*>(gamefile->getBuffer() + header.ofsCameras);
1203 for (size_t i = 0; i < header.nCameras; i++)
1204 {
1205 ModelCamera a;
1206 a.init(gamefile, camDefs[i], globalSequences, modelname);
1207 cam.push_back(a);
1208 }
1209 }
1210 else if (header.version[0] <= 16)
1211 {
1212 ModelCameraDefV10* camDefs = reinterpret_cast<ModelCameraDefV10*>(gamefile->getBuffer() + header.ofsCameras);
1213 for (size_t i = 0; i < header.nCameras; i++)
1214 {
1215 ModelCamera a;
1216 a.initv10(gamefile, camDefs[i], globalSequences, modelname);
1217 cam.push_back(a);
1218 }
1219 }
1220 if (cam.size() > 0)
1221 {
1222 hasCamera = true;
1223 }
1224 }
1225
1226 // init lights
1227 if (header.nLights)
1228 {
1229 lights.resize(header.nLights);
1230 ModelLightDef* lDefs = reinterpret_cast<ModelLightDef*>(gamefile->getBuffer() + header.ofsLights);
1231 for (uint i = 0; i < lights.size(); i++)
1232 lights[i].init(gamefile, lDefs[i], globalSequences);
1233 }
1234
1235 animcalc = false;
1236}
1237
1238void WoWModel::setLOD(int index)
1239{
1240 GameFile* g;
1241
1242 if (gamefile->isChunked())
1243 {
1244 const int numSkinFiles = static_cast<int>(skinFileIDs.size());
1245 if (!numSkinFiles)
1246 {
1247 LOG_ERROR << "Attempt to set view level when no .skin files exist.";
1248 return;
1249 }
1250
1251 if (index < 0)
1252 {
1253 index = 0;
1254 LOG_ERROR << "Attempt to set view level to negative number (" << index << ").";
1255 }
1256 else if (index >= numSkinFiles)
1257 {
1258 index = numSkinFiles - 1;
1259 LOG_ERROR << "Attempt to set view level too high (" << index << "). Setting LOD to valid max (" << index <<
1260 ").";
1261 }
1262
1263 const uint32 skinfile = skinFileIDs[index];
1264 g = GAMEDIRECTORY.getFile(skinfile);
1265 if (!g || !g->open())
1266 {
1267 LOG_ERROR << "Unable to load .skin file with ID" << skinfile << ".";
1268 return;
1269 }
1270 }
1271 else
1272 {
1273 auto tmpname = modelname;
1274 auto mpos = tmpname.rfind(".m2");
1275 if (mpos == std::string::npos) mpos = tmpname.rfind(".M2");
1276 if (mpos != std::string::npos) tmpname.erase(mpos);
1277 lodname = std::format("{}{:02d}.skin", tmpname, index); // Lods: 00, 01, 02, 03
1278
1279 g = GAMEDIRECTORY.getFile(lodname);
1280 if (!g || !g->open())
1281 {
1282 LOG_ERROR << "Unable to load .skin file:" << lodname;
1283 return;
1284 }
1285 }
1286
1287 // Texture definitions
1288 const ModelTextureDef* texdef = reinterpret_cast<ModelTextureDef*>(gamefile->getBuffer() + header.ofsTextures);
1289
1290 // Transparency
1291 const int16* transLookup = reinterpret_cast<int16*>(gamefile->getBuffer() + header.ofsTransparencyLookup);
1292
1293 if (g->isEof())
1294 {
1295 LOG_ERROR << "Unable to load .skin file:" << g->fullname() << ", ID:" << g->fileDataId();
1296 g->close();
1297 return;
1298 }
1299
1300 const ModelView* view = reinterpret_cast<ModelView*>(g->getBuffer());
1301
1302 if (view->id[0] != 'S' || view->id[1] != 'K' || view->id[2] != 'I' || view->id[3] != 'N')
1303 {
1304 LOG_ERROR << "Doesn't appear to be .skin file:" << g->fullname() << ", ID:" << g->fileDataId();
1305 g->close();
1306 return;
1307 }
1308
1309 // Indices, Triangles
1310 const uint16* indexLookup = reinterpret_cast<uint16*>(g->getBuffer() + view->ofsIndex);
1311 const uint16* triangles = reinterpret_cast<uint16*>(g->getBuffer() + view->ofsTris);
1312 rawIndices.clear();
1313 rawIndices.resize(view->nTris);
1314
1315 for (size_t i = 0; i < view->nTris; i++)
1316 {
1317 rawIndices[i] = indexLookup[triangles[i]];
1318 }
1319
1321
1322 // render ops
1323 ModelGeoset* ops = reinterpret_cast<ModelGeoset*>(g->getBuffer() + view->ofsSub);
1324 const ModelTexUnit* Tex = reinterpret_cast<ModelTexUnit*>(g->getBuffer() + view->ofsTex);
1325 ModelRenderFlags* renderFlags = reinterpret_cast<ModelRenderFlags*>(gamefile->getBuffer() + header.ofsTexFlags);
1326 const uint16* texlookup = reinterpret_cast<uint16*>(gamefile->getBuffer() + header.ofsTexLookup);
1327 const uint16* texanimlookup = reinterpret_cast<uint16*>(gamefile->getBuffer() + header.ofsTexAnimLookup);
1328 const int16* texunitlookup = reinterpret_cast<int16*>(gamefile->getBuffer() + header.ofsTexUnitLookup);
1329
1330 uint32 istart = 0;
1331 for (size_t i = 0; i < view->nSub; i++)
1332 {
1333 ModelGeosetHD* hdgeo = new ModelGeosetHD(ops[i]);
1334 hdgeo->istart = istart;
1335 istart += hdgeo->icount;
1336 hdgeo->display = (hdgeo->id == 0);
1337 rawGeosets.push_back(hdgeo);
1338 }
1339
1341
1342 rawPasses.clear();
1343
1344 for (size_t j = 0; j < view->nTex; j++)
1345 {
1346 ModelRenderPass* pass = new ModelRenderPass(this, Tex[j].op);
1347
1348 uint texOffset = 0;
1349 const uint texCount = Tex[j].op_count;
1350 // THIS IS A QUICK AND DIRTY WORKAROUND. If op_count > 1 then the texture unit contains multiple textures.
1351 // Properly we should display them all, blended, but WMV doesn't support that yet, and it ends up
1352 // displaying one randomly. So for now we try to guess which one is the most important by checking
1353 // if any are special textures (11, 12 or 13). If so, we choose the first one that fits this criterion.
1354 pass->specialTex = specialTextures[texlookup[Tex[j].textureid]];
1355 for (size_t k = 0; k < texCount; k++)
1356 {
1357 const int special = specialTextures[texlookup[Tex[j].textureid + k]];
1358 if (special == 11 || special == 12 || special == 13)
1359 {
1360 texOffset = static_cast<uint>(k);
1361 pass->specialTex = special;
1362 if (texCount > 1)
1363 LOG_INFO << "setLOD: texture unit" << j << "has" << texCount << "textures. Choosing texture" << k +
1364 1 << ", which has special type =" << special;
1365 break;
1366 }
1367 }
1368 pass->tex = texlookup[Tex[j].textureid + texOffset];
1369
1370 // TODO: figure out these flags properly -_-
1371 const ModelRenderFlags& rf = renderFlags[Tex[j].flagsIndex];
1372
1373 pass->blendmode = rf.blend;
1374 //if (rf.blend == 0) // Test to disable/hide different blend types
1375 // continue;
1376
1377 pass->color = Tex[j].colorIndex;
1378
1379 pass->opacity = transLookup[Tex[j].transid + texOffset];
1380
1381 pass->unlit = (rf.flags & RENDERFLAGS_UNLIT) != 0;
1382
1383 pass->cull = (rf.flags & RENDERFLAGS_TWOSIDED) == 0;
1384
1385 pass->billboard = (rf.flags & RENDERFLAGS_BILLBOARD) != 0;
1386
1387 // Use environmental reflection effects?
1388 pass->useEnvMap = (texunitlookup[Tex[j].texunit] == -1) && pass->billboard && rf.blend > 2; //&& rf.blend<5;
1389
1390 // Disable environmental mapping if its been unchecked.
1391 if (pass->useEnvMap && !video.useEnvMapping)
1392 pass->useEnvMap = false;
1393
1394 pass->noZWrite = (rf.flags & RENDERFLAGS_ZBUFFERED) != 0;
1395
1396 // ToDo: Work out the correct way to get the true/false of transparency
1397 pass->trans = (pass->blendmode > 0) && (pass->opacity > 0);
1398 // Transparency - not the correct way to get transparency
1399
1400 // Texture flags
1401 pass->swrap = (texdef[pass->tex].flags & TEXTURE_WRAPX) != 0; // Texture wrap X
1402 pass->twrap = (texdef[pass->tex].flags & TEXTURE_WRAPY) != 0; // Texture wrap Y
1403
1404 // tex[j].flags: Usually 16 for static textures, and 0 for animated textures.
1405 if ((Tex[j].flags & TEXTUREUNIT_STATIC) == 0)
1406 {
1407 pass->texanim = texanimlookup[Tex[j].texanimid + texOffset];
1408 }
1409
1410 rawPasses.push_back(pass);
1411 }
1412 g->close();
1413
1414 std::sort(rawPasses.begin(), rawPasses.end(), &WoWModel::sortPasses);
1415 passes = rawPasses;
1416}
1417
1419{
1420 if (mrp1->geoIndex == mrp2->geoIndex)
1421 return mrp1->specialTex < mrp2->specialTex;
1422 if (mrp1->blendmode == mrp2->blendmode)
1423 return (mrp1->geoIndex < mrp2->geoIndex);
1424 return mrp1->blendmode < mrp2->blendmode;
1425}
1426
1427void WoWModel::calcBones(ssize_t Anim, size_t time)
1428{
1429 // Reset all bones to 'false' which means they haven't been animated yet.
1430 for (auto& it : bones)
1431 {
1432 it.calc = false;
1433 }
1434
1435 // Character specific bone animation calculations.
1437 {
1438 // Animate the "core" rotations and transformations for the rest of the model to adopt into their transformations
1439 if (keyBoneLookup[BONE_ROOT] > -1)
1440 {
1441 for (int i = 0; i <= keyBoneLookup[BONE_ROOT]; i++)
1442 {
1443 bones[i].calcMatrix(bones, Anim, time);
1444 }
1445 }
1446
1447 // Find the close hands animation id
1448 int closeFistID = 0;
1449 /*
1450 for (size_t i=0; i<header.nAnimations; i++) {
1451 if (anims[i].animID==15) { // closed fist
1452 closeFistID = i;
1453 break;
1454 }
1455 }
1456 */
1457 // Alfred 2009.07.23 use animLookups to speedup
1458 if (animLookups.size() >= ANIMATION_HANDSCLOSED && animLookups[ANIMATION_HANDSCLOSED] > 0) // closed fist
1459 closeFistID = animLookups[ANIMATION_HANDSCLOSED];
1460
1461 // Animate key skeletal bones except the fingers which we do later.
1462 // -----
1463 size_t a, t;
1464
1465 // if we have a "secondary animation" selected, animate upper body using that.
1466 if (animManager->GetSecondaryID() > -1)
1467 {
1470 }
1471 else
1472 {
1473 a = Anim;
1474 t = time;
1475 }
1476
1477 for (size_t i = 0; i < animManager->GetSecondaryCount(); i++)
1478 {
1479 // only goto 5, otherwise it affects the hip/waist rotation for the lower-body.
1480 if (keyBoneLookup[i] > -1)
1481 bones[keyBoneLookup[i]].calcMatrix(bones, a, t);
1482 }
1483
1484 if (animManager->GetMouthID() > -1)
1485 {
1486 // Animate the head and jaw
1487 if (keyBoneLookup[BONE_HEAD] > -1)
1490 if (keyBoneLookup[BONE_JAW] > -1)
1493 }
1494 else
1495 {
1496 // Animate the head and jaw
1497 if (keyBoneLookup[BONE_HEAD] > -1)
1498 bones[keyBoneLookup[BONE_HEAD]].calcMatrix(bones, a, t);
1499 if (keyBoneLookup[BONE_JAW] > -1)
1500 bones[keyBoneLookup[BONE_JAW]].calcMatrix(bones, a, t);
1501 }
1502
1503 // still not sure what 18-26 bone lookups are but I think its more for things like wrist, etc which are not as visually obvious.
1504 for (size_t i = BONE_BTH; i < BONE_MAX; i++)
1505 {
1506 if (keyBoneLookup[i] > -1)
1507 bones[keyBoneLookup[i]].calcMatrix(bones, a, t);
1508 }
1509 // =====
1510
1512 {
1513 a = closeFistID;
1514 t = 1;
1515 }
1516 else
1517 {
1518 a = Anim;
1519 t = time;
1520 }
1521
1522 for (size_t i = 0; i < 5; i++)
1523 {
1524 if (keyBoneLookup[BONE_RFINGER1 + i] > -1)
1525 bones[keyBoneLookup[BONE_RFINGER1 + i]].calcMatrix(bones, a, t);
1526 }
1527
1529 {
1530 a = closeFistID;
1531 t = 1;
1532 }
1533 else
1534 {
1535 a = Anim;
1536 t = time;
1537 }
1538
1539 for (size_t i = 0; i < 5; i++)
1540 {
1541 if (keyBoneLookup[BONE_LFINGER1 + i] > -1)
1542 bones[keyBoneLookup[BONE_LFINGER1 + i]].calcMatrix(bones, a, t);
1543 }
1544 }
1545 else
1546 {
1547 for (ssize_t i = 0; i < keyBoneLookup[BONE_ROOT]; i++)
1548 {
1549 bones[i].calcMatrix(bones, Anim, time);
1550 }
1551
1552 // The following line fixes 'mounts' in that the character doesn't get rotated, but it also screws up the rotation for the entire model :(
1553 //bones[18].calcMatrix(bones, anim, time, false);
1554
1555 // Animate key skeletal bones except the fingers which we do later.
1556 // -----
1557 size_t a, t;
1558
1559 // if we have a "secondary animation" selected, animate upper body using that.
1560 if (animManager->GetSecondaryID() > -1)
1561 {
1564 }
1565 else
1566 {
1567 a = Anim;
1568 t = time;
1569 }
1570
1571 for (size_t i = 0; i < animManager->GetSecondaryCount(); i++)
1572 {
1573 // only goto 5, otherwise it affects the hip/waist rotation for the lower-body.
1574 if (keyBoneLookup[i] > -1)
1575 bones[keyBoneLookup[i]].calcMatrix(bones, a, t);
1576 }
1577
1578 if (animManager->GetMouthID() > -1)
1579 {
1580 // Animate the head and jaw
1581 if (keyBoneLookup[BONE_HEAD] > -1)
1584 if (keyBoneLookup[BONE_JAW] > -1)
1587 }
1588 else
1589 {
1590 // Animate the head and jaw
1591 if (keyBoneLookup[BONE_HEAD] > -1)
1592 bones[keyBoneLookup[BONE_HEAD]].calcMatrix(bones, a, t);
1593 if (keyBoneLookup[BONE_JAW] > -1)
1594 bones[keyBoneLookup[BONE_JAW]].calcMatrix(bones, a, t);
1595 }
1596
1597 // still not sure what 18-26 bone lookups are but I think its more for things like wrist, etc which are not as visually obvious.
1598 for (size_t i = BONE_ROOT; i < BONE_MAX; i++)
1599 {
1600 if (keyBoneLookup[i] > -1)
1601 bones[keyBoneLookup[i]].calcMatrix(bones, a, t);
1602 }
1603 }
1604
1605 // Animate everything thats left with the 'default' animation
1606 for (auto& it : bones)
1607 {
1608 it.calcMatrix(bones, Anim, time);
1609 }
1610}
1611
1612void WoWModel::animate(ssize_t Anim)
1613{
1614 size_t t;
1615
1616 const ModelAnimation& a = anims[Anim];
1617 int tmax = a.length;
1618 if (tmax == 0)
1619 tmax = 1;
1620
1621 if (isWMO == true)
1622 {
1623 t = globalTime;
1624 t %= tmax;
1625 }
1626 else
1627 t = animManager->GetFrame();
1628
1629 this->animtime = t;
1630 this->anim = Anim;
1631
1632 if (animBones) // && (!animManager->IsPaused() || !animManager->IsParticlePaused()))
1633 {
1634 calcBones(Anim, t);
1635 }
1636
1637 if (animGeometry)
1638 {
1639 if (video.supportVBO)
1640 {
1641 glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbuf);
1642 glBufferDataARB(GL_ARRAY_BUFFER_ARB, 2 * vbufsize, nullptr, GL_STREAM_DRAW_ARB);
1643
1644 vertices = static_cast<glm::vec3*>(glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY));
1645 }
1646
1647 // transform vertices
1648 auto ov_it = origVertices.begin();
1649 for (size_t i = 0; ov_it != origVertices.end(); ++i, ++ov_it)
1650 {
1651 //,k=0
1652 glm::vec3 v(0, 0, 0), n(0, 0, 0);
1653
1654 for (size_t b = 0; b < 4; b++)
1655 {
1656 if (ov_it->weights[b] > 0)
1657 {
1658 glm::vec3 tv = glm::vec3(bones[ov_it->bones[b]].mat * glm::vec4(ov_it->pos, 1.0f));
1659 glm::vec3 tn = glm::vec3(bones[ov_it->bones[b]].mrot * glm::vec4(ov_it->normal, 1.0f));
1660 v += tv * (static_cast<float>(ov_it->weights[b]) / 255.0f);
1661 n += tn * (static_cast<float>(ov_it->weights[b]) / 255.0f);
1662 }
1663 }
1664
1665 vertices[i] = v;
1666 if (video.supportVBO)
1667 vertices[origVertices.size() + i] = glm::normalize(n); // shouldn't these be normal by default?
1668 else
1669 normals[i] = n;
1670 }
1671
1672 // clear bind
1673 if (video.supportVBO)
1674 {
1675 glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
1676 }
1677 }
1678
1679 for (auto& light : lights)
1680 {
1681 if (light.parent >= 0)
1682 {
1683 light.tpos = glm::vec3(bones[light.parent].mat * glm::vec4(light.pos, 1.0f));
1684 light.tdir = glm::vec3(bones[light.parent].mrot * glm::vec4(light.dir, 1.0f));
1685 }
1686 }
1687
1688 for (auto& it : particleSystems)
1689 {
1690 // random time distribution for teh win ..?
1691 //int pt = a.timeStart + (t + (int)(tmax*particleSystems[i].tofs)) % tmax;
1692 it.setup(Anim, t);
1693 }
1694
1695 for (auto& it : ribbons)
1696 it.setup(Anim, t);
1697
1698 for (auto& it : texAnims)
1699 it.calc(Anim, t);
1700}
1701
1703{
1704 glPushMatrix();
1705
1706 glm::vec3 scaling = glm::vec3(scale_, scale_, scale_);
1707 if (mirrored_)
1708 {
1709 glFrontFace(GL_CW); // necessary when model is being mirrored or it appears inside-out
1710 scaling.y *= -1.0f;
1711 }
1712 else
1713 {
1714 glFrontFace(GL_CCW);
1715 }
1716
1717 // no need to scale if its already 100%
1718 // scaling manually set from model control panel
1719 if (scaling != glm::vec3(1.0f, 1.0f, 1.0f))
1720 glScalef(scaling.x, scaling.y, scaling.z);
1721
1722 if (pos_ != glm::vec3(0.0f, 0.0f, 0.0f))
1723 glTranslatef(pos_.x, pos_.y, pos_.z);
1724
1725
1726 if (rot_ != glm::vec3(0.0f, 0.0f, 0.0f))
1727 {
1728 glRotatef(rot_.x, 1.0f, 0.0f, 0.0f);
1729 glRotatef(rot_.y, 0.0f, 1.0f, 0.0f);
1730 glRotatef(rot_.z, 0.0f, 0.0f, 1.0f);
1731 }
1732
1733 // Use epsilon-based comparison for floating point
1734 if (showModel && glm::epsilonNotEqual(alpha_, 1.0f, 1e-6f))
1735 {
1736 glDisable(GL_COLOR_MATERIAL);
1737
1738 const float a[] = { 1.0f, 1.0f, 1.0f, alpha_ };
1739 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, a);
1740
1741 glEnable(GL_BLEND);
1742 //glDisable(GL_DEPTH_TEST);
1743 //glDepthMask(GL_FALSE);
1744 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1745 }
1746
1748 glDisable(GL_TEXTURE_2D);
1749 else
1750 glEnable(GL_TEXTURE_2D);
1751
1752 // assume these client states are enabled: GL_VERTEX_ARRAY, GL_NORMAL_ARRAY, GL_TEXTURE_COORD_ARRAY
1753 if (video.supportVBO && animated)
1754 {
1755 // bind / point to the vertex normals buffer
1756 if (animGeometry)
1757 {
1758 glNormalPointer(GL_FLOAT, 0, GL_BUFFER_OFFSET(vbufsize));
1759 }
1760 else
1761 {
1762 glBindBufferARB(GL_ARRAY_BUFFER_ARB, nbuf);
1763 glNormalPointer(GL_FLOAT, 0, nullptr);
1764 }
1765
1766 // Bind the vertex buffer
1767 glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbuf);
1768 glVertexPointer(3, GL_FLOAT, 0, nullptr);
1769 // Bind the texture coordinates buffer
1770 glBindBufferARB(GL_ARRAY_BUFFER_ARB, tbuf);
1771 glTexCoordPointer(2, GL_FLOAT, 0, nullptr);
1772 }
1773 else if (animated)
1774 {
1775 glVertexPointer(3, GL_FLOAT, 0, vertices);
1776 glNormalPointer(GL_FLOAT, 0, normals);
1777 glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
1778 }
1779
1780 // Display in wireframe mode?
1781 if (showWireframe)
1782 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1783
1784 // Render the various parts of the model.
1785 //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1786 for (const auto it : passes)
1787 {
1788 if (it->init())
1789 {
1790 it->render(animated);
1791 it->deinit();
1792 }
1793 }
1794
1795 if (showWireframe)
1796 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1797
1798 // clean bind
1799 if (video.supportVBO && animated)
1800 glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
1801
1802 // Use epsilon-based comparison for floating point
1803 if (showModel && glm::epsilonNotEqual(alpha_, 1.0f, 1e-6f))
1804 {
1805 const float a[] = {1.0f, 1.0f, 1.0f, 1.0f};
1806 glMaterialfv(GL_FRONT, GL_DIFFUSE, a);
1807
1808 glDisable(GL_BLEND);
1809 //glEnable(GL_DEPTH_TEST);
1810 //glDepthMask(GL_TRUE);
1811 glEnable(GL_COLOR_MATERIAL);
1812 }
1813
1814 glPopMatrix();
1815 // done with all render ops
1816}
1817
1818inline void WoWModel::draw()
1819{
1820 if (!ok)
1821 return;
1822
1823 if (!animated)
1824 {
1825 if (showModel)
1826 glCallList(dlist);
1827 }
1828 else
1829 {
1830 if (ind)
1831 {
1833 }
1834 else
1835 {
1836 if (!animcalc)
1837 {
1839 //animcalc = true; // Not sure what this is really for but it breaks WMO animation
1840 }
1841 }
1842
1843 if (showModel)
1844 drawModel();
1845 }
1846
1847 if (!video.useMasking && (showBounds || showBones))
1848 {
1849 glDisable(GL_LIGHTING);
1850 glDisable(GL_TEXTURE_2D);
1851
1852 if (showBounds)
1854
1855 if (showBones)
1856 drawBones();
1857
1858 glEnable(GL_TEXTURE_2D);
1859 glEnable(GL_LIGHTING);
1860 }
1861}
1862
1863// These aren't really needed in the model viewer.. only wowmapviewer
1864void WoWModel::lightsOn(GLuint lbase)
1865{
1866 // setup lights
1867 for (uint i = 0, l = lbase; i < lights.size(); i++)
1868 lights[i].setup(animtime, (GLuint)l++);
1869}
1870
1871// These aren't really needed in the model viewer.. only wowmapviewer
1872void WoWModel::lightsOff(GLuint lbase)
1873{
1874 for (uint i = 0, l = lbase; i < lights.size(); i++)
1875 glDisable((GLenum)l++);
1876}
1877
1878// Updates our particles within models.
1880{
1881 if (!ok || !showParticles || !GLOBALSETTINGS.bShowParticle)
1882 return;
1883
1884 for (auto& it : particleSystems)
1885 {
1886 it.update(dt);
1887 it.replaceParticleColors = replaceParticleColors;
1888 it.particleColorReplacements = particleColorReplacements;
1889 }
1890}
1891
1892// Draws the "bones" of models (skeletal animation)
1894{
1895 glDisable(GL_DEPTH_TEST);
1896 glBegin(GL_LINES);
1897 for (auto it : bones)
1898 {
1899 //for (size_t i=30; i<40; i++) {
1900 if (it.parent != -1)
1901 {
1902 glVertex3fv(glm::value_ptr(it.transPivot));
1903 glVertex3fv(glm::value_ptr(bones[it.parent].transPivot));
1904 }
1905 }
1906 glEnd();
1907 glEnable(GL_DEPTH_TEST);
1908}
1909
1910// Sets up the models attachments
1912{
1913 const int l = attLookup[id];
1914 if (l > -1)
1915 atts[l].setup();
1916}
1917
1918// Sets up the models attachments
1920{
1921 const int l = attLookup[id];
1922 if (l >= 0)
1923 atts[l].setup();
1924}
1925
1926// Draws the Bounding Volume, which is used for Collision detection.
1928{
1929 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1930 glBegin(GL_TRIANGLES);
1931 for (const unsigned int v : boundTris)
1932 {
1933 if (v < bounds.size())
1934 glVertex3fv(glm::value_ptr(bounds[v]));
1935 else
1936 glVertex3f(0, 0, 0);
1937 }
1938 glEnd();
1939 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1940}
1941
1942// Renders our particles into the pipeline.
1944{
1946 {
1947 glPushMatrix();
1948
1949 glm::vec3 scaling = glm::vec3(scale_, scale_, scale_);
1950 if (mirrored_)
1951 {
1952 glFrontFace(GL_CW); // necessary when model is being mirrored or it appears inside-out
1953 scaling.y *= -1.0f;
1954 }
1955 else
1956 {
1957 glFrontFace(GL_CCW);
1958 }
1959
1960 // no need to scale if its already 100%
1961 // scaling manually set from model control panel
1962 if (scaling != glm::vec3(1.0f, 1.0f, 1.0f))
1963 glScalef(scaling.x, scaling.y, scaling.z);
1964
1965 if (rot_ != glm::vec3(0.0f, 0.0f, 0.0f))
1966 glRotatef(rot_.y, 0.0f, 1.0f, 0.0f);
1967
1968 //glRotatef(45.0f, 1,0,0);
1969
1970 if (pos_ != glm::vec3(0.0f, 0.0f, 0.0f))
1971 glTranslatef(pos_.x, pos_.y, pos_.z);
1972
1973 // draw particle systems
1974 for (auto& it : particleSystems)
1975 it.draw();
1976
1977 // draw ribbons
1978 for (auto& it : ribbons)
1979 it.draw();
1980
1981 glPopMatrix();
1982 }
1983}
1984
1986{
1987 for (const auto it : *this)
1988 {
1989 if (it->slot() == slot)
1990 return it;
1991 }
1992
1993 return nullptr;
1994}
1995
1997{
1998 const auto* item = getItem(slot);
1999
2000 if (item == nullptr)
2001 return 0;
2002
2003 return item->id();
2004}
2005
2007{
2008 const auto* chest = getItem(CS_CHEST);
2009 if (chest == nullptr)
2010 return false;
2011
2012 const auto& item = items.getById(chest->id());
2013
2014 return item.type == IT_ROBE;
2015}
2016
2017void WoWModel::update(int dt) // (float dt)
2018{
2019 if (animated && animManager != nullptr)
2020 animManager->Tick(dt);
2021 updateEmitters((dt / 1000.0f));
2022}
2023
2025{
2026 for (const int specialTexture : specialTextures)
2027 {
2028 if (specialTexture == special)
2029 {
2032
2033 replaceTextures[special] = TEXTUREMANAGER.add(Tex);
2034 break;
2035 }
2036 }
2037}
2038
2039std::map<int, std::wstring> WoWModel::getAnimsMap()
2040{
2041 std::map<int, std::wstring> result;
2042
2043 static const std::map<int, std::wstring> AnimationNames =
2044 {
2045 {0, L"Stand"},
2046 {1, L"Death"},
2047 {2, L"Spell"},
2048 {3, L"Stop"},
2049 {4, L"Walk"},
2050 {5, L"Run"},
2051 {6, L"Dead"},
2052 {7, L"Rise"},
2053 {8, L"StandWound"},
2054 {9, L"CombatWound"},
2055 {10, L"CombatCritical"},
2056 {11, L"ShuffleLeft"},
2057 {12, L"ShuffleRight"},
2058 {13, L"Walkbackwards"},
2059 {14, L"Stun"},
2060 {15, L"HandsClosed"},
2061 {16, L"AttackUnarmed"},
2062 {17, L"Attack1H"},
2063 {18, L"Attack2H"},
2064 {19, L"Attack2HL"},
2065 {20, L"ParryUnarmed"},
2066 {21, L"Parry1H"},
2067 {22, L"Parry2H"},
2068 {23, L"Parry2HL"},
2069 {24, L"ShieldBlock"},
2070 {25, L"ReadyUnarmed"},
2071 {26, L"Ready1H"},
2072 {27, L"Ready2H"},
2073 {28, L"Ready2HL"},
2074 {29, L"ReadyBow"},
2075 {30, L"Dodge"},
2076 {31, L"SpellPrecast"},
2077 {32, L"SpellCast"},
2078 {33, L"SpellCastArea"},
2079 {34, L"NPCWelcome"},
2080 {35, L"NPCGoodbye"},
2081 {36, L"Block"},
2082 {37, L"JumpStart"},
2083 {38, L"Jump"},
2084 {39, L"JumpEnd"},
2085 {40, L"Fall"},
2086 {41, L"SwimIdle"},
2087 {42, L"Swim"},
2088 {43, L"SwimLeft"},
2089 {44, L"SwimRight"},
2090 {45, L"SwimBackwards"},
2091 {46, L"AttackBow"},
2092 {47, L"FireBow"},
2093 {48, L"ReadyRifle"},
2094 {49, L"AttackRifle"},
2095 {50, L"Loot"},
2096 {51, L"ReadySpellDirected"},
2097 {52, L"ReadySpellOmni"},
2098 {53, L"SpellCastDirected"},
2099 {54, L"SpellCastOmni"},
2100 {55, L"BattleRoar"},
2101 {56, L"ReadyAbility"},
2102 {57, L"Special1H"},
2103 {58, L"Special2H"},
2104 {59, L"ShieldBash"},
2105 {60, L"EmoteTalk"},
2106 {61, L"EmoteEat"},
2107 {62, L"EmoteWork"},
2108 {63, L"EmoteUseStanding"},
2109 {64, L"EmoteTalkExclamation"},
2110 {65, L"EmoteTalkQuestion"},
2111 {66, L"EmoteBow"},
2112 {67, L"EmoteWave"},
2113 {68, L"EmoteCheer"},
2114 {69, L"EmoteDance"},
2115 {70, L"EmoteLaugh"},
2116 {71, L"EmoteSleep"},
2117 {72, L"EmoteSitGround"},
2118 {73, L"EmoteRude"},
2119 {74, L"EmoteRoar"},
2120 {75, L"EmoteKneel"},
2121 {76, L"EmoteKiss"},
2122 {77, L"EmoteCry"},
2123 {78, L"EmoteChicken"},
2124 {79, L"EmoteBeg"},
2125 {80, L"EmoteApplaud"},
2126 {81, L"EmoteShout"},
2127 {82, L"EmoteFlex"},
2128 {83, L"EmoteShy"},
2129 {84, L"EmotePoint"},
2130 {85, L"Attack1HPierce"},
2131 {86, L"Attack2HLoosePierce"},
2132 {87, L"AttackOff"},
2133 {88, L"AttackOffPierce"},
2134 {89, L"Sheath"},
2135 {90, L"HipSheath"},
2136 {91, L"Mount"},
2137 {92, L"RunRight"},
2138 {93, L"RunLeft"},
2139 {94, L"MountSpecial"},
2140 {95, L"Kick"},
2141 {96, L"SitGroundDown"},
2142 {97, L"SitGround"},
2143 {98, L"SitGroundUp"},
2144 {99, L"SleepDown"},
2145 {100, L"Sleep"},
2146 {101, L"SleepUp"},
2147 {102, L"SitChairLow"},
2148 {103, L"SitChairMed"},
2149 {104, L"SitChairHigh"},
2150 {105, L"LoadBow"},
2151 {106, L"LoadRifle"},
2152 {107, L"AttackThrown"},
2153 {108, L"ReadyThrown"},
2154 {109, L"HoldBow"},
2155 {110, L"HoldRifle"},
2156 {111, L"HoldThrown"},
2157 {112, L"LoadThrown"},
2158 {113, L"EmoteSalute"},
2159 {114, L"KneelStart"},
2160 {115, L"KneelLoop"},
2161 {116, L"KneelEnd"},
2162 {117, L"AttackUnarmedOff"},
2163 {118, L"SpecialUnarmed"},
2164 {119, L"StealthWalk"},
2165 {120, L"StealthStand"},
2166 {121, L"Knockdown"},
2167 {122, L"EatingLoop"},
2168 {123, L"UseStandingLoop"},
2169 {124, L"ChannelCastDirected"},
2170 {125, L"ChannelCastOmni"},
2171 {126, L"Whirlwind"},
2172 {127, L"Birth"},
2173 {128, L"UseStandingStart"},
2174 {129, L"UseStandingEnd"},
2175 {130, L"CreatureSpecial"},
2176 {131, L"Drown"},
2177 {132, L"Drowned"},
2178 {133, L"FishingCast"},
2179 {134, L"FishingLoop"},
2180 {135, L"Fly"},
2181 {136, L"EmoteWorkNoSheathe"},
2182 {137, L"EmoteStunNoSheathe"},
2183 {138, L"EmoteUseStandingNoSheathe"},
2184 {139, L"SpellSleepDown"},
2185 {140, L"SpellKneelStart"},
2186 {141, L"SpellKneelLoop"},
2187 {142, L"SpellKneelEnd"},
2188 {143, L"Sprint"},
2189 {144, L"InFlight"},
2190 {145, L"Spawn"},
2191 {146, L"Close"},
2192 {147, L"Closed"},
2193 {148, L"Open"},
2194 {149, L"Opened"},
2195 {150, L"Destroy"},
2196 {151, L"Destroyed"},
2197 {152, L"Rebuild"},
2198 {153, L"Custom0"},
2199 {154, L"Custom1"},
2200 {155, L"Custom2"},
2201 {156, L"Custom3"},
2202 {157, L"Despawn"},
2203 {158, L"Hold"},
2204 {159, L"Decay"},
2205 {160, L"BowPull"},
2206 {161, L"BowRelease"},
2207 {162, L"ShipStart"},
2208 {163, L"ShipMoving"},
2209 {164, L"ShipStop"},
2210 {165, L"GroupArrow"},
2211 {166, L"Arrow"},
2212 {167, L"CorpseArrow"},
2213 {168, L"GuideArrow"},
2214 {169, L"Sway"},
2215 {170, L"DruidCatPounce"},
2216 {171, L"DruidCatRip"},
2217 {172, L"DruidCatRake"},
2218 {173, L"DruidCatRavage"},
2219 {174, L"DruidCatClaw"},
2220 {175, L"DruidCatCower"},
2221 {176, L"DruidBearSwipe"},
2222 {177, L"DruidBearBite"},
2223 {178, L"DruidBearMaul"},
2224 {179, L"DruidBearBash"},
2225 {180, L"DragonTail"},
2226 {181, L"DragonStomp"},
2227 {182, L"DragonSpit"},
2228 {183, L"DragonSpitHover"},
2229 {184, L"DragonSpitFly"},
2230 {185, L"EmoteYes"},
2231 {186, L"EmoteNo"},
2232 {187, L"JumpLandRun"},
2233 {188, L"LootHold"},
2234 {189, L"LootUp"},
2235 {190, L"StandHigh"},
2236 {191, L"Impact"},
2237 {192, L"LiftOff"},
2238 {193, L"Hover"},
2239 {194, L"SuccubusEntice"},
2240 {195, L"EmoteTrain"},
2241 {196, L"EmoteDead"},
2242 {197, L"EmoteDanceOnce"},
2243 {198, L"Deflect"},
2244 {199, L"EmoteEatNoSheathe"},
2245 {200, L"Land"},
2246 {201, L"Submerge"},
2247 {202, L"Submerged"},
2248 {203, L"Cannibalize"},
2249 {204, L"ArrowBirth"},
2250 {205, L"GroupArrowBirth"},
2251 {206, L"CorpseArrowBirth"},
2252 {207, L"GuideArrowBirth"},
2253 {208, L"EmoteTalkNoSheathe"},
2254 {209, L"EmotePointNoSheathe"},
2255 {210, L"EmoteSaluteNoSheathe"},
2256 {211, L"EmoteDanceSpecial"},
2257 {212, L"Mutilate"},
2258 {213, L"CustomSpell01"},
2259 {214, L"CustomSpell02"},
2260 {215, L"CustomSpell03"},
2261 {216, L"CustomSpell04"},
2262 {217, L"CustomSpell05"},
2263 {218, L"CustomSpell06"},
2264 {219, L"CustomSpell07"},
2265 {220, L"CustomSpell08"},
2266 {221, L"CustomSpell09"},
2267 {222, L"CustomSpell10"},
2268 {223, L"StealthRun"},
2269 {224, L"Emerge"},
2270 {225, L"Cower"},
2271 {226, L"Grab"},
2272 {227, L"GrabClosed"},
2273 {228, L"GrabThrown"},
2274 {229, L"FlyStand"},
2275 {230, L"FlyDeath"},
2276 {231, L"FlySpell"},
2277 {232, L"FlyStop"},
2278 {233, L"FlyWalk"},
2279 {234, L"FlyRun"},
2280 {235, L"FlyDead"},
2281 {236, L"FlyRise"},
2282 {237, L"FlyStandWound"},
2283 {238, L"FlyCombatWound"},
2284 {239, L"FlyCombatCritical"},
2285 {240, L"FlyShuffleLeft"},
2286 {241, L"FlyShuffleRight"},
2287 {242, L"FlyWalkbackwards"},
2288 {243, L"FlyStun"},
2289 {244, L"FlyHandsClosed"},
2290 {245, L"FlyAttackUnarmed"},
2291 {246, L"FlyAttack1H"},
2292 {247, L"FlyAttack2H"},
2293 {248, L"FlyAttack2HL"},
2294 {249, L"FlyParryUnarmed"},
2295 {250, L"FlyParry1H"},
2296 {251, L"FlyParry2H"},
2297 {252, L"FlyParry2HL"},
2298 {253, L"FlyShieldBlock"},
2299 {254, L"FlyReadyUnarmed"},
2300 {255, L"FlyReady1H"},
2301 {256, L"FlyReady2H"},
2302 {257, L"FlyReady2HL"},
2303 {258, L"FlyReadyBow"},
2304 {259, L"FlyDodge"},
2305 {260, L"FlySpellPrecast"},
2306 {261, L"FlySpellCast"},
2307 {262, L"FlySpellCastArea"},
2308 {263, L"FlyNPCWelcome"},
2309 {264, L"FlyNPCGoodbye"},
2310 {265, L"FlyBlock"},
2311 {266, L"FlyJumpStart"},
2312 {267, L"FlyJump"},
2313 {268, L"FlyJumpEnd"},
2314 {269, L"FlyFall"},
2315 {270, L"FlySwimIdle"},
2316 {271, L"FlySwim"},
2317 {272, L"FlySwimLeft"},
2318 {273, L"FlySwimRight"},
2319 {274, L"FlySwimBackwards"},
2320 {275, L"FlyAttackBow"},
2321 {276, L"FlyFireBow"},
2322 {277, L"FlyReadyRifle"},
2323 {278, L"FlyAttackRifle"},
2324 {279, L"FlyLoot"},
2325 {280, L"FlyReadySpellDirected"},
2326 {281, L"FlyReadySpellOmni"},
2327 {282, L"FlySpellCastDirected"},
2328 {283, L"FlySpellCastOmni"},
2329 {284, L"FlyBattleRoar"},
2330 {285, L"FlyReadyAbility"},
2331 {286, L"FlySpecial1H"},
2332 {287, L"FlySpecial2H"},
2333 {288, L"FlyShieldBash"},
2334 {289, L"FlyEmoteTalk"},
2335 {290, L"FlyEmoteEat"},
2336 {291, L"FlyEmoteWork"},
2337 {292, L"FlyEmoteUseStanding"},
2338 {293, L"FlyEmoteTalkExclamation"},
2339 {294, L"FlyEmoteTalkQuestion"},
2340 {295, L"FlyEmoteBow"},
2341 {296, L"FlyEmoteWave"},
2342 {297, L"FlyEmoteCheer"},
2343 {298, L"FlyEmoteDance"},
2344 {299, L"FlyEmoteLaugh"},
2345 {300, L"FlyEmoteSleep"},
2346 {301, L"FlyEmoteSitGround"},
2347 {302, L"FlyEmoteRude"},
2348 {303, L"FlyEmoteRoar"},
2349 {304, L"FlyEmoteKneel"},
2350 {305, L"FlyEmoteKiss"},
2351 {306, L"FlyEmoteCry"},
2352 {307, L"FlyEmoteChicken"},
2353 {308, L"FlyEmoteBeg"},
2354 {309, L"FlyEmoteApplaud"},
2355 {310, L"FlyEmoteShout"},
2356 {311, L"FlyEmoteFlex"},
2357 {312, L"FlyEmoteShy"},
2358 {313, L"FlyEmotePoint"},
2359 {314, L"FlyAttack1HPierce"},
2360 {315, L"FlyAttack2HLoosePierce"},
2361 {316, L"FlyAttackOff"},
2362 {317, L"FlyAttackOffPierce"},
2363 {318, L"FlySheath"},
2364 {319, L"FlyHipSheath"},
2365 {320, L"FlyMount"},
2366 {321, L"FlyRunRight"},
2367 {322, L"FlyRunLeft"},
2368 {323, L"FlyMountSpecial"},
2369 {324, L"FlyKick"},
2370 {325, L"FlySitGroundDown"},
2371 {326, L"FlySitGround"},
2372 {327, L"FlySitGroundUp"},
2373 {328, L"FlySleepDown"},
2374 {329, L"FlySleep"},
2375 {330, L"FlySleepUp"},
2376 {331, L"FlySitChairLow"},
2377 {332, L"FlySitChairMed"},
2378 {333, L"FlySitChairHigh"},
2379 {334, L"FlyLoadBow"},
2380 {335, L"FlyLoadRifle"},
2381 {336, L"FlyAttackThrown"},
2382 {337, L"FlyReadyThrown"},
2383 {338, L"FlyHoldBow"},
2384 {339, L"FlyHoldRifle"},
2385 {340, L"FlyHoldThrown"},
2386 {341, L"FlyLoadThrown"},
2387 {342, L"FlyEmoteSalute"},
2388 {343, L"FlyKneelStart"},
2389 {344, L"FlyKneelLoop"},
2390 {345, L"FlyKneelEnd"},
2391 {346, L"FlyAttackUnarmedOff"},
2392 {347, L"FlySpecialUnarmed"},
2393 {348, L"FlyStealthWalk"},
2394 {349, L"FlyStealthStand"},
2395 {350, L"FlyKnockdown"},
2396 {351, L"FlyEatingLoop"},
2397 {352, L"FlyUseStandingLoop"},
2398 {353, L"FlyChannelCastDirected"},
2399 {354, L"FlyChannelCastOmni"},
2400 {355, L"FlyWhirlwind"},
2401 {356, L"FlyBirth"},
2402 {357, L"FlyUseStandingStart"},
2403 {358, L"FlyUseStandingEnd"},
2404 {359, L"FlyCreatureSpecial"},
2405 {360, L"FlyDrown"},
2406 {361, L"FlyDrowned"},
2407 {362, L"FlyFishingCast"},
2408 {363, L"FlyFishingLoop"},
2409 {364, L"FlyFly"},
2410 {365, L"FlyEmoteWorkNoSheathe"},
2411 {366, L"FlyEmoteStunNoSheathe"},
2412 {367, L"FlyEmoteUseStandingNoSheathe"},
2413 {368, L"FlySpellSleepDown"},
2414 {369, L"FlySpellKneelStart"},
2415 {370, L"FlySpellKneelLoop"},
2416 {371, L"FlySpellKneelEnd"},
2417 {372, L"FlySprint"},
2418 {373, L"FlyInFlight"},
2419 {374, L"FlySpawn"},
2420 {375, L"FlyClose"},
2421 {376, L"FlyClosed"},
2422 {377, L"FlyOpen"},
2423 {378, L"FlyOpened"},
2424 {379, L"FlyDestroy"},
2425 {380, L"FlyDestroyed"},
2426 {381, L"FlyRebuild"},
2427 {382, L"FlyCustom0"},
2428 {383, L"FlyCustom1"},
2429 {384, L"FlyCustom2"},
2430 {385, L"FlyCustom3"},
2431 {386, L"FlyDespawn"},
2432 {387, L"FlyHold"},
2433 {388, L"FlyDecay"},
2434 {389, L"FlyBowPull"},
2435 {390, L"FlyBowRelease"},
2436 {391, L"FlyShipStart"},
2437 {392, L"FlyShipMoving"},
2438 {393, L"FlyShipStop"},
2439 {394, L"FlyGroupArrow"},
2440 {395, L"FlyArrow"},
2441 {396, L"FlyCorpseArrow"},
2442 {397, L"FlyGuideArrow"},
2443 {398, L"FlySway"},
2444 {399, L"FlyDruidCatPounce"},
2445 {400, L"FlyDruidCatRip"},
2446 {401, L"FlyDruidCatRake"},
2447 {402, L"FlyDruidCatRavage"},
2448 {403, L"FlyDruidCatClaw"},
2449 {404, L"FlyDruidCatCower"},
2450 {405, L"FlyDruidBearSwipe"},
2451 {406, L"FlyDruidBearBite"},
2452 {407, L"FlyDruidBearMaul"},
2453 {408, L"FlyDruidBearBash"},
2454 {409, L"FlyDragonTail"},
2455 {410, L"FlyDragonStomp"},
2456 {411, L"FlyDragonSpit"},
2457 {412, L"FlyDragonSpitHover"},
2458 {413, L"FlyDragonSpitFly"},
2459 {414, L"FlyEmoteYes"},
2460 {415, L"FlyEmoteNo"},
2461 {416, L"FlyJumpLandRun"},
2462 {417, L"FlyLootHold"},
2463 {418, L"FlyLootUp"},
2464 {419, L"FlyStandHigh"},
2465 {420, L"FlyImpact"},
2466 {421, L"FlyLiftOff"},
2467 {422, L"FlyHover"},
2468 {423, L"FlySuccubusEntice"},
2469 {424, L"FlyEmoteTrain"},
2470 {425, L"FlyEmoteDead"},
2471 {426, L"FlyEmoteDanceOnce"},
2472 {427, L"FlyDeflect"},
2473 {428, L"FlyEmoteEatNoSheathe"},
2474 {429, L"FlyLand"},
2475 {430, L"FlySubmerge"},
2476 {431, L"FlySubmerged"},
2477 {432, L"FlyCannibalize"},
2478 {433, L"FlyArrowBirth"},
2479 {434, L"FlyGroupArrowBirth"},
2480 {435, L"FlyCorpseArrowBirth"},
2481 {436, L"FlyGuideArrowBirth"},
2482 {437, L"FlyEmoteTalkNoSheathe"},
2483 {438, L"FlyEmotePointNoSheathe"},
2484 {439, L"FlyEmoteSaluteNoSheathe"},
2485 {440, L"FlyEmoteDanceSpecial"},
2486 {441, L"FlyMutilate"},
2487 {442, L"FlyCustomSpell01"},
2488 {443, L"FlyCustomSpell02"},
2489 {444, L"FlyCustomSpell03"},
2490 {445, L"FlyCustomSpell04"},
2491 {446, L"FlyCustomSpell05"},
2492 {447, L"FlyCustomSpell06"},
2493 {448, L"FlyCustomSpell07"},
2494 {449, L"FlyCustomSpell08"},
2495 {450, L"FlyCustomSpell09"},
2496 {451, L"FlyCustomSpell10"},
2497 {452, L"FlyStealthRun"},
2498 {453, L"FlyEmerge"},
2499 {454, L"FlyCower"},
2500 {455, L"FlyGrab"},
2501 {456, L"FlyGrabClosed"},
2502 {457, L"FlyGrabThrown"},
2503 {458, L"ToFly"},
2504 {459, L"ToHover"},
2505 {460, L"ToGround"},
2506 {461, L"FlyToFly"},
2507 {462, L"FlyToHover"},
2508 {463, L"FlyToGround"},
2509 {464, L"Settle"},
2510 {465, L"FlySettle"},
2511 {466, L"DeathStart"},
2512 {467, L"DeathLoop"},
2513 {468, L"DeathEnd"},
2514 {469, L"FlyDeathStart"},
2515 {470, L"FlyDeathLoop"},
2516 {471, L"FlyDeathEnd"},
2517 {472, L"DeathEndHold"},
2518 {473, L"FlyDeathEndHold"},
2519 {474, L"Strangulate"},
2520 {475, L"FlyStrangulate"},
2521 {476, L"ReadyJoust"},
2522 {477, L"LoadJoust"},
2523 {478, L"HoldJoust"},
2524 {479, L"FlyReadyJoust"},
2525 {480, L"FlyLoadJoust"},
2526 {481, L"FlyHoldJoust"},
2527 {482, L"AttackJoust"},
2528 {483, L"FlyAttackJoust"},
2529 {484, L"ReclinedMount"},
2530 {485, L"FlyReclinedMount"},
2531 {486, L"ToAltered"},
2532 {487, L"FromAltered"},
2533 {488, L"FlyToAltered"},
2534 {489, L"FlyFromAltered"},
2535 {490, L"InStocks"},
2536 {491, L"FlyInStocks"},
2537 {492, L"VehicleGrab"},
2538 {493, L"VehicleThrow"},
2539 {494, L"FlyVehicleGrab"},
2540 {495, L"FlyVehicleThrow"},
2541 {496, L"ToAlteredPostSwap"},
2542 {497, L"FromAlteredPostSwap"},
2543 {498, L"FlyToAlteredPostSwap"},
2544 {499, L"FlyFromAlteredPostSwap"},
2545 {500, L"ReclinedMountPassenger"},
2546 {501, L"FlyReclinedMountPassenger"},
2547 {502, L"Carry2H"},
2548 {503, L"Carried2H"},
2549 {504, L"FlyCarry2H"},
2550 {505, L"FlyCarried2H"},
2551 {506, L"EmoteSniff"},
2552 {507, L"EmoteFlySniff"},
2553 {508, L"AttackFist1H"},
2554 {509, L"FlyAttackFist1H"},
2555 {510, L"AttackFist1HOff"},
2556 {511, L"FlyAttackFist1HOff"},
2557 {512, L"ParryFist1H"},
2558 {513, L"FlyParryFist1H"},
2559 {514, L"ReadyFist1H"},
2560 {515, L"FlyReadyFist1H"},
2561 {516, L"SpecialFist1H"},
2562 {517, L"FlySpecialFist1H"},
2563 {518, L"EmoteReadStart"},
2564 {519, L"FlyEmoteReadStart"},
2565 {520, L"EmoteReadLoop"},
2566 {521, L"FlyEmoteReadLoop"},
2567 {522, L"EmoteReadEnd"},
2568 {523, L"FlyEmoteReadEnd"},
2569 {524, L"SwimRun"},
2570 {525, L"FlySwimRun"},
2571 {526, L"SwimWalk"},
2572 {527, L"FlySwimWalk"},
2573 {528, L"SwimWalkBackwards"},
2574 {529, L"FlySwimWalkBackwards"},
2575 {530, L"SwimSprint"},
2576 {531, L"FlySwimSprint"},
2577 {532, L"MountSwimIdle"},
2578 {533, L"FlyMountSwimIdle"},
2579 {534, L"MountSwimBackwards"},
2580 {535, L"FlyMountSwimBackwards"},
2581 {536, L"MountSwimLeft"},
2582 {537, L"FlyMountSwimLeft"},
2583 {538, L"MountSwimRight"},
2584 {539, L"FlyMountSwimRight"},
2585 {540, L"MountSwimRun"},
2586 {541, L"FlyMountSwimRun"},
2587 {542, L"MountSwimSprint"},
2588 {543, L"FlyMountSwimSprint"},
2589 {544, L"MountSwimWalk"},
2590 {545, L"FlyMountSwimWalk"},
2591 {546, L"MountSwimWalkBackwards"},
2592 {547, L"FlyMountSwimWalkBackwards"},
2593 {548, L"MountFlightIdle"},
2594 {549, L"FlyMountFlightIdle"},
2595 {550, L"MountFlightBackwards"},
2596 {551, L"FlyMountFlightBackwards"},
2597 {552, L"MountFlightLeft"},
2598 {553, L"FlyMountFlightLeft"},
2599 {554, L"MountFlightRight"},
2600 {555, L"FlyMountFlightRight"},
2601 {556, L"MountFlightRun"},
2602 {557, L"FlyMountFlightRun"},
2603 {558, L"MountFlightSprint"},
2604 {559, L"FlyMountFlightSprint"},
2605 {560, L"MountFlightWalk"},
2606 {561, L"FlyMountFlightWalk"},
2607 {562, L"MountFlightWalkBackwards"},
2608 {563, L"FlyMountFlightWalkBackwards"},
2609 {564, L"MountFlightStart"},
2610 {565, L"FlyMountFlightStart"},
2611 {566, L"MountSwimStart"},
2612 {567, L"FlyMountSwimStart"},
2613 {568, L"MountSwimLand"},
2614 {569, L"FlyMountSwimLand"},
2615 {570, L"MountSwimLandRun"},
2616 {571, L"FlyMountSwimLandRun"},
2617 {572, L"MountFlightLand"},
2618 {573, L"FlyMountFlightLand"},
2619 {574, L"MountFlightLandRun"},
2620 {575, L"FlyMountFlightLandRun"},
2621 {576, L"ReadyBlowDart"},
2622 {577, L"FlyReadyBlowDart"},
2623 {578, L"LoadBlowDart"},
2624 {579, L"FlyLoadBlowDart"},
2625 {580, L"HoldBlowDart"},
2626 {581, L"FlyHoldBlowDart"},
2627 {582, L"AttackBlowDart"},
2628 {583, L"FlyAttackBlowDart"},
2629 {584, L"CarriageMount"},
2630 {585, L"FlyCarriageMount"},
2631 {586, L"CarriagePassengerMount"},
2632 {587, L"FlyCarriagePassengerMount"},
2633 {588, L"CarriageMountAttack"},
2634 {589, L"FlyCarriageMountAttack"},
2635 {590, L"BarTendStand"},
2636 {591, L"FlyBarTendStand"},
2637 {592, L"BarServerWalk"},
2638 {593, L"FlyBarServerWalk"},
2639 {594, L"BarServerRun"},
2640 {595, L"FlyBarServerRun"},
2641 {596, L"BarServerShuffleLeft"},
2642 {597, L"FlyBarServerShuffleLeft"},
2643 {598, L"BarServerShuffleRight"},
2644 {599, L"FlyBarServerShuffleRight"},
2645 {600, L"BarTendEmoteTalk"},
2646 {601, L"FlyBarTendEmoteTalk"},
2647 {602, L"BarTendEmotePoint"},
2648 {603, L"FlyBarTendEmotePoint"},
2649 {604, L"BarServerStand"},
2650 {605, L"FlyBarServerStand"},
2651 {606, L"BarSweepWalk"},
2652 {607, L"FlyBarSweepWalk"},
2653 {608, L"BarSweepRun"},
2654 {609, L"FlyBarSweepRun"},
2655 {610, L"BarSweepShuffleLeft"},
2656 {611, L"FlyBarSweepShuffleLeft"},
2657 {612, L"BarSweepShuffleRight"},
2658 {613, L"FlyBarSweepShuffleRight"},
2659 {614, L"BarSweepEmoteTalk"},
2660 {615, L"FlyBarSweepEmoteTalk"},
2661 {616, L"BarPatronSitEmotePoint"},
2662 {617, L"FlyBarPatronSitEmotePoint"},
2663 {618, L"MountSelfIdle"},
2664 {619, L"FlyMountSelfIdle"},
2665 {620, L"MountSelfWalk"},
2666 {621, L"FlyMountSelfWalk"},
2667 {622, L"MountSelfRun"},
2668 {623, L"FlyMountSelfRun"},
2669 {624, L"MountSelfSprint"},
2670 {625, L"FlyMountSelfSprint"},
2671 {626, L"MountSelfRunLeft"},
2672 {627, L"FlyMountSelfRunLeft"},
2673 {628, L"MountSelfRunRight"},
2674 {629, L"FlyMountSelfRunRight"},
2675 {630, L"MountSelfShuffleLeft"},
2676 {631, L"FlyMountSelfShuffleLeft"},
2677 {632, L"MountSelfShuffleRight"},
2678 {633, L"FlyMountSelfShuffleRight"},
2679 {634, L"MountSelfWalkBackwards"},
2680 {635, L"FlyMountSelfWalkBackwards"},
2681 {636, L"MountSelfSpecial"},
2682 {637, L"FlyMountSelfSpecial"},
2683 {638, L"MountSelfJump"},
2684 {639, L"FlyMountSelfJump"},
2685 {640, L"MountSelfJumpStart"},
2686 {641, L"FlyMountSelfJumpStart"},
2687 {642, L"MountSelfJumpEnd"},
2688 {643, L"FlyMountSelfJumpEnd"},
2689 {644, L"MountSelfJumpLandRun"},
2690 {645, L"FlyMountSelfJumpLandRun"},
2691 {646, L"MountSelfStart"},
2692 {647, L"FlyMountSelfStart"},
2693 {648, L"MountSelfFall"},
2694 {649, L"FlyMountSelfFall"},
2695 {650, L"Stormstrike"},
2696 {651, L"FlyStormstrike"},
2697 {652, L"ReadyJoustNoSheathe"},
2698 {653, L"FlyReadyJoustNoSheathe"},
2699 {654, L"Slam"},
2700 {655, L"FlySlam"},
2701 {656, L"DeathStrike"},
2702 {657, L"FlyDeathStrike"},
2703 {658, L"SwimAttackUnarmed"},
2704 {659, L"FlySwimAttackUnarmed"},
2705 {660, L"SpinningKick"},
2706 {661, L"FlySpinningKick"},
2707 {662, L"RoundHouseKick"},
2708 {663, L"FlyRoundHouseKick"},
2709 {664, L"RollStart"},
2710 {665, L"FlyRollStart"},
2711 {666, L"Roll"},
2712 {667, L"FlyRoll"},
2713 {668, L"RollEnd"},
2714 {669, L"FlyRollEnd"},
2715 {670, L"PalmStrike"},
2716 {671, L"FlyPalmStrike"},
2717 {672, L"MonkOffenseAttackUnarmed"},
2718 {673, L"FlyMonkOffenseAttackUnarmed"},
2719 {674, L"MonkOffenseAttackUnarmedOff"},
2720 {675, L"FlyMonkOffenseAttackUnarmedOff"},
2721 {676, L"MonkOffenseParryUnarmed"},
2722 {677, L"FlyMonkOffenseParryUnarmed"},
2723 {678, L"MonkOffenseReadyUnarmed"},
2724 {679, L"FlyMonkOffenseReadyUnarmed"},
2725 {680, L"MonkOffenseSpecialUnarmed"},
2726 {681, L"FlyMonkOffenseSpecialUnarmed"},
2727 {682, L"MonkDefenseAttackUnarmed"},
2728 {683, L"FlyMonkDefenseAttackUnarmed"},
2729 {684, L"MonkDefenseAttackUnarmedOff"},
2730 {685, L"FlyMonkDefenseAttackUnarmedOff"},
2731 {686, L"MonkDefenseParryUnarmed"},
2732 {687, L"FlyMonkDefenseParryUnarmed"},
2733 {688, L"MonkDefenseReadyUnarmed"},
2734 {689, L"FlyMonkDefenseReadyUnarmed"},
2735 {690, L"MonkDefenseSpecialUnarmed"},
2736 {691, L"FlyMonkDefenseSpecialUnarmed"},
2737 {692, L"MonkHealAttackUnarmed"},
2738 {693, L"FlyMonkHealAttackUnarmed"},
2739 {694, L"MonkHealAttackUnarmedOff"},
2740 {695, L"FlyMonkHealAttackUnarmedOff"},
2741 {696, L"MonkHealParryUnarmed"},
2742 {697, L"FlyMonkHealParryUnarmed"},
2743 {698, L"MonkHealReadyUnarmed"},
2744 {699, L"FlyMonkHealReadyUnarmed"},
2745 {700, L"MonkHealSpecialUnarmed"},
2746 {701, L"FlyMonkHealSpecialUnarmed"},
2747 {702, L"FlyingKick"},
2748 {703, L"FlyFlyingKick"},
2749 {704, L"FlyingKickStart"},
2750 {705, L"FlyFlyingKickStart"},
2751 {706, L"FlyingKickEnd"},
2752 {707, L"FlyFlyingKickEnd"},
2753 {708, L"CraneStart"},
2754 {709, L"FlyCraneStart"},
2755 {710, L"CraneLoop"},
2756 {711, L"FlyCraneLoop"},
2757 {712, L"CraneEnd"},
2758 {713, L"FlyCraneEnd"},
2759 {714, L"Despawned"},
2760 {715, L"FlyDespawned"},
2761 {716, L"ThousandFists"},
2762 {717, L"FlyThousandFists"},
2763 {718, L"MonkHealReadySpellDirected"},
2764 {719, L"FlyMonkHealReadySpellDirected"},
2765 {720, L"MonkHealReadySpellOmni"},
2766 {721, L"FlyMonkHealReadySpellOmni"},
2767 {722, L"MonkHealSpellCastDirected"},
2768 {723, L"FlyMonkHealSpellCastDirected"},
2769 {724, L"MonkHealSpellCastOmni"},
2770 {725, L"FlyMonkHealSpellCastOmni"},
2771 {726, L"MonkHealChannelCastDirected"},
2772 {727, L"FlyMonkHealChannelCastDirected"},
2773 {728, L"MonkHealChannelCastOmni"},
2774 {729, L"FlyMonkHealChannelCastOmni"},
2775 {730, L"Torpedo"},
2776 {731, L"FlyTorpedo"},
2777 {732, L"Meditate"},
2778 {733, L"FlyMeditate"},
2779 {734, L"BreathOfFire"},
2780 {735, L"FlyBreathOfFire"},
2781 {736, L"RisingSunKick"},
2782 {737, L"FlyRisingSunKick"},
2783 {738, L"GroundKick"},
2784 {739, L"FlyGroundKick"},
2785 {740, L"KickBack"},
2786 {741, L"FlyKickBack"},
2787 {742, L"PetBattleStand"},
2788 {743, L"FlyPetBattleStand"},
2789 {744, L"PetBattleDeath"},
2790 {745, L"FlyPetBattleDeath"},
2791 {746, L"PetBattleRun"},
2792 {747, L"FlyPetBattleRun"},
2793 {748, L"PetBattleWound"},
2794 {749, L"FlyPetBattleWound"},
2795 {750, L"PetBattleAttack"},
2796 {751, L"FlyPetBattleAttack"},
2797 {752, L"PetBattleReadySpell"},
2798 {753, L"FlyPetBattleReadySpell"},
2799 {754, L"PetBattleSpellCast"},
2800 {755, L"FlyPetBattleSpellCast"},
2801 {756, L"PetBattleCustom0"},
2802 {757, L"FlyPetBattleCustom0"},
2803 {758, L"PetBattleCustom1"},
2804 {759, L"FlyPetBattleCustom1"},
2805 {760, L"PetBattleCustom2"},
2806 {761, L"FlyPetBattleCustom2"},
2807 {762, L"PetBattleCustom3"},
2808 {763, L"FlyPetBattleCustom3"},
2809 {764, L"PetBattleVictory"},
2810 {765, L"FlyPetBattleVictory"},
2811 {766, L"PetBattleLoss"},
2812 {767, L"FlyPetBattleLoss"},
2813 {768, L"PetBattleStun"},
2814 {769, L"FlyPetBattleStun"},
2815 {770, L"PetBattleDead"},
2816 {771, L"FlyPetBattleDead"},
2817 {772, L"PetBattleFreeze"},
2818 {773, L"FlyPetBattleFreeze"},
2819 {774, L"MonkOffenseAttackWeapon"},
2820 {775, L"FlyMonkOffenseAttackWeapon"},
2821 {776, L"BarTendEmoteWave"},
2822 {777, L"FlyBarTendEmoteWave"},
2823 {778, L"BarServerEmoteTalk"},
2824 {779, L"FlyBarServerEmoteTalk"},
2825 {780, L"BarServerEmoteWave"},
2826 {781, L"FlyBarServerEmoteWave"},
2827 {782, L"BarServerPourDrinks"},
2828 {783, L"FlyBarServerPourDrinks"},
2829 {784, L"BarServerPickup"},
2830 {785, L"FlyBarServerPickup"},
2831 {786, L"BarServerPutDown"},
2832 {787, L"FlyBarServerPutDown"},
2833 {788, L"BarSweepStand"},
2834 {789, L"FlyBarSweepStand"},
2835 {790, L"BarPatronSit"},
2836 {791, L"FlyBarPatronSit"},
2837 {792, L"BarPatronSitEmoteTalk"},
2838 {793, L"FlyBarPatronSitEmoteTalk"},
2839 {794, L"BarPatronStand"},
2840 {795, L"FlyBarPatronStand"},
2841 {796, L"BarPatronStandEmoteTalk"},
2842 {797, L"FlyBarPatronStandEmoteTalk"},
2843 {798, L"BarPatronStandEmotePoint"},
2844 {799, L"FlyBarPatronStandEmotePoint"},
2845 {800, L"CarrionSwarm"},
2846 {801, L"FlyCarrionSwarm"},
2847 {802, L"WheelLoop"},
2848 {803, L"FlyWheelLoop"},
2849 {804, L"StandCharacterCreate"},
2850 {805, L"FlyStandCharacterCreate"},
2851 {806, L"MountChopper"},
2852 {807, L"FlyMountChopper"},
2853 {808, L"FacePose"},
2854 {809, L"FlyFacePose"},
2855 {810, L"CombatAbility2HBig01"},
2856 {811, L"FlyCombatAbility2HBig01"},
2857 {812, L"CombatAbility2H01"},
2858 {813, L"FlyCombatAbility2H01"},
2859 {814, L"CombatWhirlwind"},
2860 {815, L"FlyCombatWhirlwind"},
2861 {816, L"CombatChargeLoop"},
2862 {817, L"FlyCombatChargeLoop"},
2863 {818, L"CombatAbility1H01"},
2864 {819, L"FlyCombatAbility1H01"},
2865 {820, L"CombatChargeEnd"},
2866 {821, L"FlyCombatChargeEnd"},
2867 {822, L"CombatAbility1H02"},
2868 {823, L"FlyCombatAbility1H02"},
2869 {824, L"CombatAbility1HBig01"},
2870 {825, L"FlyCombatAbility1HBig01"},
2871 {826, L"CombatAbility2H02"},
2872 {827, L"FlyCombatAbility2H02"},
2873 {828, L"ShaSpellPrecastBoth"},
2874 {829, L"FlyShaSpellPrecastBoth"},
2875 {830, L"ShaSpellCastBothFront"},
2876 {831, L"FlyShaSpellCastBothFront"},
2877 {832, L"ShaSpellCastLeftFront"},
2878 {833, L"FlyShaSpellCastLeftFront"},
2879 {834, L"ShaSpellCastRightFront"},
2880 {835, L"FlyShaSpellCastRightFront"},
2881 {836, L"ReadyCrossbow"},
2882 {837, L"FlyReadyCrossbow"},
2883 {838, L"LoadCrossbow"},
2884 {839, L"FlyLoadCrossbow"},
2885 {840, L"AttackCrossbow"},
2886 {841, L"FlyAttackCrossbow"},
2887 {842, L"HoldCrossbow"},
2888 {843, L"FlyHoldCrossbow"},
2889 {844, L"CombatAbility2HL01"},
2890 {845, L"FlyCombatAbility2HL01"},
2891 {846, L"CombatAbility2HL02"},
2892 {847, L"FlyCombatAbility2HL02"},
2893 {848, L"CombatAbility2HLBig01"},
2894 {849, L"FlyCombatAbility2HLBig01"},
2895 {850, L"CombatUnarmed01"},
2896 {851, L"FlyCombatUnarmed01"},
2897 {852, L"CombatStompLeft"},
2898 {853, L"FlyCombatStompLeft"},
2899 {854, L"CombatStompRight"},
2900 {855, L"FlyCombatStompRight"},
2901 {856, L"CombatLeapLoop"},
2902 {857, L"FlyCombatLeapLoop"},
2903 {858, L"CombatLeapEnd"},
2904 {859, L"FlyCombatLeapEnd"},
2905 {860, L"ShaReadySpellCast"},
2906 {861, L"FlyShaReadySpellCast"},
2907 {862, L"ShaSpellPrecastBothChannel"},
2908 {863, L"FlyShaSpellPrecastBothChannel"},
2909 {864, L"ShaSpellCastBothUp"},
2910 {865, L"FlyShaSpellCastBothUp"},
2911 {866, L"ShaSpellCastBothUpChannel"},
2912 {867, L"FlyShaSpellCastBothUpChannel"},
2913 {868, L"ShaSpellCastBothFrontChannel"},
2914 {869, L"FlyShaSpellCastBothFrontChannel"},
2915 {870, L"ShaSpellCastLeftFrontChannel"},
2916 {871, L"FlyShaSpellCastLeftFrontChannel"},
2917 {872, L"ShaSpellCastRightFrontChannel"},
2918 {873, L"FlyShaSpellCastRightFrontChannel"},
2919 {874, L"PriReadySpellCast"},
2920 {875, L"FlyPriReadySpellCast"},
2921 {876, L"PriSpellPrecastBoth"},
2922 {877, L"FlyPriSpellPrecastBoth"},
2923 {878, L"PriSpellPrecastBothChannel"},
2924 {879, L"FlyPriSpellPrecastBothChannel"},
2925 {880, L"PriSpellCastBothUp"},
2926 {881, L"FlyPriSpellCastBothUp"},
2927 {882, L"PriSpellCastBothFront"},
2928 {883, L"FlyPriSpellCastBothFront"},
2929 {884, L"PriSpellCastLeftFront"},
2930 {885, L"FlyPriSpellCastLeftFront"},
2931 {886, L"PriSpellCastRightFront"},
2932 {887, L"FlyPriSpellCastRightFront"},
2933 {888, L"PriSpellCastBothUpChannel"},
2934 {889, L"FlyPriSpellCastBothUpChannel"},
2935 {890, L"PriSpellCastBothFrontChannel"},
2936 {891, L"FlyPriSpellCastBothFrontChannel"},
2937 {892, L"PriSpellCastLeftFrontChannel"},
2938 {893, L"FlyPriSpellCastLeftFrontChannel"},
2939 {894, L"PriSpellCastRightFrontChannel"},
2940 {895, L"FlyPriSpellCastRightFrontChannel"},
2941 {896, L"MagReadySpellCast"},
2942 {897, L"FlyMagReadySpellCast"},
2943 {898, L"MagSpellPrecastBoth"},
2944 {899, L"FlyMagSpellPrecastBoth"},
2945 {900, L"MagSpellPrecastBothChannel"},
2946 {901, L"FlyMagSpellPrecastBothChannel"},
2947 {902, L"MagSpellCastBothUp"},
2948 {903, L"FlyMagSpellCastBothUp"},
2949 {904, L"MagSpellCastBothFront"},
2950 {905, L"FlyMagSpellCastBothFront"},
2951 {906, L"MagSpellCastLeftFront"},
2952 {907, L"FlyMagSpellCastLeftFront"},
2953 {908, L"MagSpellCastRightFront"},
2954 {909, L"FlyMagSpellCastRightFront"},
2955 {910, L"MagSpellCastBothUpChannel"},
2956 {911, L"FlyMagSpellCastBothUpChannel"},
2957 {912, L"MagSpellCastBothFrontChannel"},
2958 {913, L"FlyMagSpellCastBothFrontChannel"},
2959 {914, L"MagSpellCastLeftFrontChannel"},
2960 {915, L"FlyMagSpellCastLeftFrontChannel"},
2961 {916, L"MagSpellCastRightFrontChannel"},
2962 {917, L"FlyMagSpellCastRightFrontChannel"},
2963 {918, L"LocReadySpellCast"},
2964 {919, L"FlyLocReadySpellCast"},
2965 {920, L"LocSpellPrecastBoth"},
2966 {921, L"FlyLocSpellPrecastBoth"},
2967 {922, L"LocSpellPrecastBothChannel"},
2968 {923, L"FlyLocSpellPrecastBothChannel"},
2969 {924, L"LocSpellCastBothUp"},
2970 {925, L"FlyLocSpellCastBothUp"},
2971 {926, L"LocSpellCastBothFront"},
2972 {927, L"FlyLocSpellCastBothFront"},
2973 {928, L"LocSpellCastLeftFront"},
2974 {929, L"FlyLocSpellCastLeftFront"},
2975 {930, L"LocSpellCastRightFront"},
2976 {931, L"FlyLocSpellCastRightFront"},
2977 {932, L"LocSpellCastBothUpChannel"},
2978 {933, L"FlyLocSpellCastBothUpChannel"},
2979 {934, L"LocSpellCastBothFrontChannel"},
2980 {935, L"FlyLocSpellCastBothFrontChannel"},
2981 {936, L"LocSpellCastLeftFrontChannel"},
2982 {937, L"FlyLocSpellCastLeftFrontChannel"},
2983 {938, L"LocSpellCastRightFrontChannel"},
2984 {939, L"FlyLocSpellCastRightFrontChannel"},
2985 {940, L"DruReadySpellCast"},
2986 {941, L"FlyDruReadySpellCast"},
2987 {942, L"DruSpellPrecastBoth"},
2988 {943, L"FlyDruSpellPrecastBoth"},
2989 {944, L"DruSpellPrecastBothChannel"},
2990 {945, L"FlyDruSpellPrecastBothChannel"},
2991 {946, L"DruSpellCastBothUp"},
2992 {947, L"FlyDruSpellCastBothUp"},
2993 {948, L"DruSpellCastBothFront"},
2994 {949, L"FlyDruSpellCastBothFront"},
2995 {950, L"DruSpellCastLeftFront"},
2996 {951, L"FlyDruSpellCastLeftFront"},
2997 {952, L"DruSpellCastRightFront"},
2998 {953, L"FlyDruSpellCastRightFront"},
2999 {954, L"DruSpellCastBothUpChannel"},
3000 {955, L"FlyDruSpellCastBothUpChannel"},
3001 {956, L"DruSpellCastBothFrontChannel"},
3002 {957, L"FlyDruSpellCastBothFrontChannel"},
3003 {958, L"DruSpellCastLeftFrontChannel"},
3004 {959, L"FlyDruSpellCastLeftFrontChannel"},
3005 {960, L"DruSpellCastRightFrontChannel"},
3006 {961, L"FlyDruSpellCastRightFrontChannel"},
3007 {962, L"ArtMainLoop"},
3008 {963, L"FlyArtMainLoop"},
3009 {964, L"ArtDualLoop"},
3010 {965, L"FlyArtDualLoop"},
3011 {966, L"ArtFistsLoop"},
3012 {967, L"FlyArtFistsLoop"},
3013 {968, L"ArtBowLoop"},
3014 {969, L"FlyArtBowLoop"},
3015 {970, L"CombatAbility1H01Off"},
3016 {971, L"FlyCombatAbility1H01Off"},
3017 {972, L"CombatAbility1H02Off"},
3018 {973, L"FlyCombatAbility1H02Off"},
3019 {974, L"CombatFuriousStrike01"},
3020 {975, L"FlyCombatFuriousStrike01"},
3021 {976, L"CombatFuriousStrike02"},
3022 {977, L"FlyCombatFuriousStrike02"},
3023 {978, L"CombatFuriousStrikes"},
3024 {979, L"FlyCombatFuriousStrikes"},
3025 {980, L"CombatReadySpellCast"},
3026 {981, L"FlyCombatReadySpellCast"},
3027 {982, L"CombatShieldThrow"},
3028 {983, L"FlyCombatShieldThrow"},
3029 {984, L"PalSpellCast1HUp"},
3030 {985, L"FlyPalSpellCast1HUp"},
3031 {986, L"CombatReadyPostSpellCast"},
3032 {987, L"FlyCombatReadyPostSpellCast"},
3033 {988, L"PriReadyPostSpellCast"},
3034 {989, L"FlyPriReadyPostSpellCast"},
3035 {990, L"DHCombatRun"},
3036 {991, L"FlyDHCombatRun"},
3037 {992, L"CombatShieldBash"},
3038 {993, L"FlyCombatShieldBash"},
3039 {994, L"CombatThrow"},
3040 {995, L"FlyCombatThrow"},
3041 {996, L"CombatAbility1HPierce"},
3042 {997, L"FlyCombatAbility1HPierce"},
3043 {998, L"CombatAbility1HOffPierce"},
3044 {999, L"FlyCombatAbility1HOffPierce"},
3045 {1000, L"CombatMutilate"},
3046 {1001, L"FlyCombatMutilate"},
3047 {1002, L"CombatBladeStorm"},
3048 {1003, L"FlyCombatBladeStorm"},
3049 {1004, L"CombatFinishingMove"},
3050 {1005, L"FlyCombatFinishingMove"},
3051 {1006, L"CombatLeapStart"},
3052 {1007, L"FlyCombatLeapStart"},
3053 {1008, L"GlvThrowMain"},
3054 {1009, L"FlyGlvThrowMain"},
3055 {1010, L"GlvThrownOff"},
3056 {1011, L"FlyGlvThrownOff"},
3057 {1012, L"DHCombatSprint"},
3058 {1013, L"FlyDHCombatSprint"},
3059 {1014, L"CombatAbilityGlv01"},
3060 {1015, L"FlyCombatAbilityGlv01"},
3061 {1016, L"CombatAbilityGlv02"},
3062 {1017, L"FlyCombatAbilityGlv02"},
3063 {1018, L"CombatAbilityGlvOff01"},
3064 {1019, L"FlyCombatAbilityGlvOff01"},
3065 {1020, L"CombatAbilityGlvOff02"},
3066 {1021, L"FlyCombatAbilityGlvOff02"},
3067 {1022, L"CombatAbilityGlvBig01"},
3068 {1023, L"FlyCombatAbilityGlvBig01"},
3069 {1024, L"CombatAbilityGlvBig02"},
3070 {1025, L"FlyCombatAbilityGlvBig02"},
3071 {1026, L"ReadyGlv"},
3072 {1027, L"FlyReadyGlv"},
3073 {1028, L"CombatAbilityGlvBig03"},
3074 {1029, L"FlyCombatAbilityGlvBig03"},
3075 {1030, L"DoubleJumpStart"},
3076 {1031, L"FlyDoubleJumpStart"},
3077 {1032, L"DoubleJump"},
3078 {1033, L"FlyDoubleJump"},
3079 {1034, L"CombatEviscerate"},
3080 {1035, L"FlyCombatEviscerate"},
3081 {1036, L"DoubleJumpLandRun"},
3082 {1037, L"FlyDoubleJumpLandRun"},
3083 {1038, L"BackFlipStart"},
3084 {1039, L"FlyBackFlipStart"},
3085 {1040, L"BackFlipLoop"},
3086 {1041, L"FlyBackFlipLoop"},
3087 {1042, L"FelRushLoop"},
3088 {1043, L"FlyFelRushLoop"},
3089 {1044, L"FelRushEnd"},
3090 {1045, L"FlyFelRushEnd"},
3091 {1046, L"DHToAlteredStart"},
3092 {1047, L"FlyDHToAlteredStart"},
3093 {1048, L"DHToAlteredEnd"},
3094 {1049, L"FlyDHToAlteredEnd"},
3095 {1050, L"DHGlide"},
3096 {1051, L"FlyDHGlide"},
3097 {1052, L"FanOfKnives"},
3098 {1053, L"FlyFanOfKnives"},
3099 {1054, L"SingleJumpStart"},
3100 {1055, L"FlySingleJumpStart"},
3101 {1056, L"DHBladeDance1"},
3102 {1057, L"FlyDHBladeDance1"},
3103 {1058, L"DHBladeDance2"},
3104 {1059, L"FlyDHBladeDance2"},
3105 {1060, L"DHBladeDance3"},
3106 {1061, L"FlyDHBladeDance3"},
3107 {1062, L"DHMeteorStrike"},
3108 {1063, L"FlyDHMeteorStrike"},
3109 {1064, L"CombatExecute"},
3110 {1065, L"FlyCombatExecute"},
3111 {1066, L"ArtLoop"},
3112 {1067, L"FlyArtLoop"},
3113 {1068, L"ParryGlv"},
3114 {1069, L"FlyParryGlv"},
3115 {1070, L"CombatUnarmed02"},
3116 {1071, L"FlyCombatUnarmed02"},
3117 {1072, L"CombatPistolShot"},
3118 {1073, L"FlyCombatPistolShot"},
3119 {1074, L"CombatPistolShotOff"},
3120 {1075, L"FlyCombatPistolShotOff"},
3121 {1076, L"Monk2HLIdle"},
3122 {1077, L"FlyMonk2HLIdle"},
3123 {1078, L"ArtShieldLoop"},
3124 {1079, L"FlyArtShieldLoop"},
3125 {1080, L"CombatAbility2H03"},
3126 {1081, L"FlyCombatAbility2H03"},
3127 {1082, L"CombatStomp"},
3128 {1083, L"FlyCombatStomp"},
3129 {1084, L"CombatRoar"},
3130 {1085, L"FlyCombatRoar"},
3131 {1086, L"PalReadySpellCast"},
3132 {1087, L"FlyPalReadySpellCast"},
3133 {1088, L"PalSpellPrecastRight"},
3134 {1089, L"FlyPalSpellPrecastRight"},
3135 {1090, L"PalSpellPrecastRightChannel"},
3136 {1091, L"FlyPalSpellPrecastRightChannel"},
3137 {1092, L"PalSpellCastRightFront"},
3138 {1093, L"FlyPalSpellCastRightFront"},
3139 {1094, L"ShaSpellCastBothOut"},
3140 {1095, L"FlyShaSpellCastBothOut"},
3141 {1096, L"AttackWeapon"},
3142 {1097, L"FlyAttackWeapon"},
3143 {1098, L"ReadyWeapon"},
3144 {1099, L"FlyReadyWeapon"},
3145 {1100, L"AttackWeaponOff"},
3146 {1101, L"FlyAttackWeaponOff"},
3147 {1102, L"SpecialDual"},
3148 {1103, L"FlySpecialDual"},
3149 {1104, L"DkCast1HFront"},
3150 {1105, L"FlyDkCast1HFront"},
3151 {1106, L"CastStrongRight"},
3152 {1107, L"FlyCastStrongRight"},
3153 {1108, L"CastStrongLeft"},
3154 {1109, L"FlyCastStrongLeft"},
3155 {1110, L"CastCurseRight"},
3156 {1111, L"FlyCastCurseRight"},
3157 {1112, L"CastCurseLeft"},
3158 {1113, L"FlyCastCurseLeft"},
3159 {1114, L"CastSweepRight"},
3160 {1115, L"FlyCastSweepRight"},
3161 {1116, L"CastSweepLeft"},
3162 {1117, L"FlyCastSweepLeft"},
3163 {1118, L"CastStrongUpLeft"},
3164 {1119, L"FlyCastStrongUpLeft"},
3165 {1120, L"CastTwistUpBoth"},
3166 {1121, L"FlyCastTwistUpBoth"},
3167 {1122, L"CastOutStrong"},
3168 {1123, L"FlyCastOutStrong"},
3169 {1124, L"DrumLoop"},
3170 {1125, L"FlyDrumLoop"},
3171 {1126, L"ParryWeapon"},
3172 {1127, L"FlyParryWeapon"},
3173 {1128, L"ReadyFL"},
3174 {1129, L"FlyReadyFL"},
3175 {1130, L"AttackFL"},
3176 {1131, L"FlyAttackFL"},
3177 {1132, L"AttackFLOff"},
3178 {1133, L"FlyAttackFLOff"},
3179 {1134, L"ParryFL"},
3180 {1135, L"FlyParryFL"},
3181 {1136, L"SpecialFL"},
3182 {1137, L"FlySpecialFL"},
3183 {1138, L"PriHoverForward"},
3184 {1139, L"FlyPriHoverForward"},
3185 {1140, L"PriHoverBackward"},
3186 {1141, L"FlyPriHoverBackward"},
3187 {1142, L"PriHoverRight"},
3188 {1143, L"FlyPriHoverRight"},
3189 {1144, L"PriHoverLeft"},
3190 {1145, L"FlyPriHoverLeft"},
3191 {1146, L"RunBackwards"},
3192 {1147, L"FlyRunBackwards"},
3193 {1148, L"CastStrongUpRight"},
3194 {1149, L"FlyCastStrongUpRight"},
3195 {1150, L"WAWalk"},
3196 {1151, L"FlyWAWalk"},
3197 {1152, L"WARun"},
3198 {1153, L"FlyWARun"},
3199 {1154, L"WADrunkStand"},
3200 {1155, L"FlyWADrunkStand"},
3201 {1156, L"WADrunkShuffleLeft"},
3202 {1157, L"FlyWADrunkShuffleLeft"},
3203 {1158, L"WADrunkShuffleRight"},
3204 {1159, L"FlyWADrunkShuffleRight"},
3205 {1160, L"WADrunkWalk"},
3206 {1161, L"FlyWADrunkWalk"},
3207 {1162, L"WADrunkWalkBackwards"},
3208 {1163, L"FlyWADrunkWalkBackwards"},
3209 {1164, L"WADrunkWound"},
3210 {1165, L"FlyWADrunkWound"},
3211 {1166, L"WADrunkTalk"},
3212 {1167, L"FlyWADrunkTalk"},
3213 {1168, L"WATrance01"},
3214 {1169, L"FlyWATrance01"},
3215 {1170, L"WATrance02"},
3216 {1171, L"FlyWATrance02"},
3217 {1172, L"WAChant01"},
3218 {1173, L"FlyWAChant01"},
3219 {1174, L"WAChant02"},
3220 {1175, L"FlyWAChant02"},
3221 {1176, L"WAChant03"},
3222 {1177, L"FlyWAChant03"},
3223 {1178, L"WAHang01"},
3224 {1179, L"FlyWAHang01"},
3225 {1180, L"WAHang02"},
3226 {1181, L"FlyWAHang02"},
3227 {1182, L"WASummon01"},
3228 {1183, L"FlyWASummon01"},
3229 {1184, L"WASummon02"},
3230 {1185, L"FlyWASummon02"},
3231 {1186, L"WABeggarTalk"},
3232 {1187, L"FlyWABeggarTalk"},
3233 {1188, L"WABeggarStand"},
3234 {1189, L"FlyWABeggarStand"},
3235 {1190, L"WABeggarPoint"},
3236 {1191, L"FlyWABeggarPoint"},
3237 {1192, L"WABeggarBeg"},
3238 {1193, L"FlyWABeggarBeg"},
3239 {1194, L"WASit01"},
3240 {1195, L"FlyWASit01"},
3241 {1196, L"WASit02"},
3242 {1197, L"FlyWASit02"},
3243 {1198, L"WASit03"},
3244 {1199, L"FlyWASit03"},
3245 {1200, L"WACrierStand01"},
3246 {1201, L"FlyWACrierStand01"},
3247 {1202, L"WACrierStand02"},
3248 {1203, L"FlyWACrierStand02"},
3249 {1204, L"WACrierStand03"},
3250 {1205, L"FlyWACrierStand03"},
3251 {1206, L"WACrierTalk"},
3252 {1207, L"FlyWACrierTalk"},
3253 {1208, L"WACrateHold"},
3254 {1209, L"FlyWACrateHold"},
3255 {1210, L"WABarrelHold"},
3256 {1211, L"FlyWABarrelHold"},
3257 {1212, L"WASackHold"},
3258 {1213, L"FlyWASackHold"},
3259 {1214, L"WAWheelBarrowStand"},
3260 {1215, L"FlyWAWheelBarrowStand"},
3261 {1216, L"WAWheelBarrowWalk"},
3262 {1217, L"FlyWAWheelBarrowWalk"},
3263 {1218, L"WAWheelBarrowRun"},
3264 {1219, L"FlyWAWheelBarrowRun"},
3265 {1220, L"WAHammerLoop"},
3266 {1221, L"FlyWAHammerLoop"},
3267 {1222, L"WACrankLoop"},
3268 {1223, L"FlyWACrankLoop"},
3269 {1224, L"WAPourStart"},
3270 {1225, L"FlyWAPourStart"},
3271 {1226, L"WAPourLoop"},
3272 {1227, L"FlyWAPourLoop"},
3273 {1228, L"WAPourEnd"},
3274 {1229, L"FlyWAPourEnd"},
3275 {1230, L"WAEmotePour"},
3276 {1231, L"FlyWAEmotePour"},
3277 {1232, L"WARowingStandRight"},
3278 {1233, L"FlyWARowingStandRight"},
3279 {1234, L"WARowingStandLeft"},
3280 {1235, L"FlyWARowingStandLeft"},
3281 {1236, L"WARowingRight"},
3282 {1237, L"FlyWARowingRight"},
3283 {1238, L"WARowingLeft"},
3284 {1239, L"FlyWARowingLeft"},
3285 {1240, L"WAGuardStand01"},
3286 {1241, L"FlyWAGuardStand01"},
3287 {1242, L"WAGuardStand02"},
3288 {1243, L"FlyWAGuardStand02"},
3289 {1244, L"WAGuardStand03"},
3290 {1245, L"FlyWAGuardStand03"},
3291 {1246, L"WAGuardStand04"},
3292 {1247, L"FlyWAGuardStand04"},
3293 {1248, L"WAFreezing01"},
3294 {1249, L"FlyWAFreezing01"},
3295 {1250, L"WAFreezing02"},
3296 {1251, L"FlyWAFreezing02"},
3297 {1252, L"WAVendorStand01"},
3298 {1253, L"FlyWAVendorStand01"},
3299 {1254, L"WAVendorStand02"},
3300 {1255, L"FlyWAVendorStand02"},
3301 {1256, L"WAVendorStand03"},
3302 {1257, L"FlyWAVendorStand03"},
3303 {1258, L"WAVendorTalk"},
3304 {1259, L"FlyWAVendorTalk"},
3305 {1260, L"WALean01"},
3306 {1261, L"FlyWALean01"},
3307 {1262, L"WALean02"},
3308 {1263, L"FlyWALean02"},
3309 {1264, L"WALean03"},
3310 {1265, L"FlyWALean03"},
3311 {1266, L"WALeanTalk"},
3312 {1267, L"FlyWALeanTalk"},
3313 {1268, L"WABoatWheel"},
3314 {1269, L"FlyWABoatWheel"},
3315 {1270, L"WASmithLoop"},
3316 {1271, L"FlyWASmithLoop"},
3317 {1272, L"WAScrubbing"},
3318 {1273, L"FlyWAScrubbing"},
3319 {1274, L"WAWeaponSharpen"},
3320 {1275, L"FlyWAWeaponSharpen"},
3321 {1276, L"WAStirring"},
3322 {1277, L"FlyWAStirring"},
3323 {1278, L"WAPerch01"},
3324 {1279, L"FlyWAPerch01"},
3325 {1280, L"WAPerch02"},
3326 {1281, L"FlyWAPerch02"},
3327 {1282, L"HoldWeapon"},
3328 {1283, L"FlyHoldWeapon"},
3329 {1284, L"WABarrelWalk"},
3330 {1285, L"FlyWABarrelWalk"},
3331 {1286, L"WAPourHold"},
3332 {1287, L"FlyWAPourHold"},
3333 {1288, L"CastStrong"},
3334 {1289, L"FlyCastStrong"},
3335 {1290, L"CastCurse"},
3336 {1291, L"FlyCastCurse"},
3337 {1292, L"CastSweep"},
3338 {1293, L"FlyCastSweep"},
3339 {1294, L"CastStrongUp"},
3340 {1295, L"FlyCastStrongUp"},
3341 {1296, L"WABoatWheelStand"},
3342 {1297, L"FlyWABoatWheelStand"},
3343 {1298, L"WASmithStand"},
3344 {1299, L"FlyWASmithStand"},
3345 {1300, L"WACrankStand"},
3346 {1301, L"FlyWACrankStand"},
3347 {1302, L"WAPourWalk"},
3348 {1303, L"FlyWAPourWalk"},
3349 {1304, L"FalconeerStart"},
3350 {1305, L"FlyFalconeerStart"},
3351 {1306, L"FalconeerLoop"},
3352 {1307, L"FlyFalconeerLoop"},
3353 {1308, L"FalconeerEnd"},
3354 {1309, L"FlyFalconeerEnd"},
3355 {1310, L"WADrunkDrink"},
3356 {1311, L"FlyWADrunkDrink"},
3357 {1312, L"WAStandEat"},
3358 {1313, L"FlyWAStandEat"},
3359 {1314, L"WAStandDrink"},
3360 {1315, L"FlyWAStandDrink"},
3361 {1316, L"WABound01"},
3362 {1317, L"FlyWABound01"},
3363 {1318, L"WABound02"},
3364 {1319, L"FlyWABound02"},
3365 {1320, L"CombatAbility1H03Off"},
3366 {1321, L"FlyCombatAbility1H03Off"},
3367 {1322, L"CombatAbilityDualWield01"},
3368 {1323, L"FlyCombatAbilityDualWield01"},
3369 {1324, L"WACradle01"},
3370 {1325, L"FlyWACradle01"},
3371 {1326, L"LocSummon"},
3372 {1327, L"FlyLocSummon"},
3373 {1328, L"LoadWeapon"},
3374 {1329, L"FlyLoadWeapon"},
3375 {1330, L"ArtOffLoop"},
3376 {1331, L"FlyArtOffLoop"},
3377 {1332, L"WADead01"},
3378 {1333, L"FlyWADead01"},
3379 {1334, L"WADead02"},
3380 {1335, L"FlyWADead02"},
3381 {1336, L"WADead03"},
3382 {1337, L"FlyWADead03"},
3383 {1338, L"WADead04"},
3384 {1339, L"FlyWADead04"},
3385 {1340, L"WADead05"},
3386 {1341, L"FlyWADead05"},
3387 {1342, L"WADead06"},
3388 {1343, L"FlyWADead06"},
3389 {1344, L"WADead07"},
3390 {1345, L"FlyWADead07"},
3391 {1346, L"GiantRun"},
3392 {1347, L"FlyGiantRun"},
3393 {1348, L"BarTendEmoteCheer"},
3394 {1349, L"FlyBarTendEmoteCheer"},
3395 {1350, L"BarTendEmoteTalkQuestion"},
3396 {1351, L"FlyBarTendEmoteTalkQuestion"},
3397 {1352, L"BarTendEmoteTalkExclamation"},
3398 {1353, L"FlyBarTendEmoteTalkExclamation"},
3399 {1354, L"BarTendWalk"},
3400 {1355, L"FlyBarTendWalk"},
3401 {1356, L"BartendShuffleLeft"},
3402 {1357, L"FlyBartendShuffleLeft"},
3403 {1358, L"BarTendShuffleRight"},
3404 {1359, L"FlyBarTendShuffleRight"},
3405 {1360, L"BarTendCustomSpell01"},
3406 {1361, L"FlyBarTendCustomSpell01"},
3407 {1362, L"BarTendCustomSpell02"},
3408 {1363, L"FlyBarTendCustomSpell02"},
3409 {1364, L"BarTendCustomSpell03"},
3410 {1365, L"FlyBarTendCustomSpell03"},
3411 {1366, L"BarServerEmoteCheer"},
3412 {1367, L"FlyBarServerEmoteCheer"},
3413 {1368, L"BarServerEmoteTalkQuestion"},
3414 {1369, L"FlyBarServerEmoteTalkQuestion"},
3415 {1370, L"BarServerEmoteTalkExclamation"},
3416 {1371, L"FlyBarServerEmoteTalkExclamation"},
3417 {1372, L"BarServerCustomSpell01"},
3418 {1373, L"FlyBarServerCustomSpell01"},
3419 {1374, L"BarServerCustomSpell02"},
3420 {1375, L"FlyBarServerCustomSpell02"},
3421 {1376, L"BarServerCustomSpell03"},
3422 {1377, L"FlyBarServerCustomSpell03"},
3423 {1378, L"BarPatronEmoteDrink"},
3424 {1379, L"FlyBarPatronEmoteDrink"},
3425 {1380, L"BarPatronEmoteCheer"},
3426 {1381, L"FlyBarPatronEmoteCheer"},
3427 {1382, L"BarPatronCustomSpell01"},
3428 {1383, L"FlyBarPatronCustomSpell01"},
3429 {1384, L"BarPatronCustomSpell02"},
3430 {1385, L"FlyBarPatronCustomSpell02"},
3431 {1386, L"BarPatronCustomSpell03"},
3432 {1387, L"FlyBarPatronCustomSpell03"},
3433 {1388, L"HoldDart"},
3434 {1389, L"FlyHoldDart"},
3435 {1390, L"ReadyDart"},
3436 {1391, L"FlyReadyDart"},
3437 {1392, L"AttackDart"},
3438 {1393, L"FlyAttackDart"},
3439 {1394, L"LoadDart"},
3440 {1395, L"FlyLoadDart"},
3441 {1396, L"WADartTargetStand"},
3442 {1397, L"FlyWADartTargetStand"},
3443 {1398, L"WADartTargetEmoteTalk"},
3444 {1399, L"FlyWADartTargetEmoteTalk"},
3445 {1400, L"BarPatronSitEmoteCheer"},
3446 {1401, L"FlyBarPatronSitEmoteCheer"},
3447 {1402, L"BarPatronSitCustomSpell01"},
3448 {1403, L"FlyBarPatronSitCustomSpell01"},
3449 {1404, L"BarPatronSitCustomSpell02"},
3450 {1405, L"FlyBarPatronSitCustomSpell02"},
3451 {1406, L"BarPatronSitCustomSpell03"},
3452 {1407, L"FlyBarPatronSitCustomSpell03"},
3453 {1408, L"BarPianoStand"},
3454 {1409, L"FlyBarPianoStand"},
3455 {1410, L"BarPianoEmoteTalk"},
3456 {1411, L"FlyBarPianoEmoteTalk"},
3457 {1412, L"WAHearthSit"},
3458 {1413, L"FlyWAHearthSit"},
3459 {1414, L"WAHearthSitEmoteCry"},
3460 {1415, L"FlyWAHearthSitEmoteCry"},
3461 {1416, L"WAHearthSitEmoteCheer"},
3462 {1417, L"FlyWAHearthSitEmoteCheer"},
3463 {1418, L"WAHearthSitCustomSpell01"},
3464 {1419, L"FlyWAHearthSitCustomSpell01"},
3465 {1420, L"WAHearthSitCustomSpell02"},
3466 {1421, L"FlyWAHearthSitCustomSpell02"},
3467 {1422, L"WAHearthSitCustomSpell03"},
3468 {1423, L"FlyWAHearthSitCustomSpell03"},
3469 {1424, L"WAHearthStand"},
3470 {1425, L"FlyWAHearthStand"},
3471 {1426, L"WAHearthStandEmoteCheer"},
3472 {1427, L"FlyWAHearthStandEmoteCheer"},
3473 {1428, L"WAHearthStandEmoteTalk"},
3474 {1429, L"FlyWAHearthStandEmoteTalk"},
3475 {1430, L"WAHearthStandCustomSpell01"},
3476 {1431, L"FlyWAHearthStandCustomSpell01"},
3477 {1432, L"WAHearthStandCustomSpell02"},
3478 {1433, L"FlyWAHearthStandCustomSpell02"},
3479 {1434, L"WAHearthStandCustomSpell03"},
3480 {1435, L"FlyWAHearthStandCustomSpell03"},
3481 {1436, L"WAScribeStart"},
3482 {1437, L"FlyWAScribeStart"},
3483 {1438, L"WAScribeLoop"},
3484 {1439, L"FlyWAScribeLoop"},
3485 {1440, L"WAScribeEnd"},
3486 {1441, L"FlyWAScribeEnd"},
3487 {1442, L"WAEmoteScribe"},
3488 {1443, L"FlyWAEmoteScribe"},
3489 {1444, L"Haymaker"},
3490 {1445, L"FlyHaymaker"},
3491 {1446, L"HaymakerPrecast"},
3492 {1447, L"FlyHaymakerPrecast"},
3493 {1448, L"ChannelCastOmniUp"},
3494 {1449, L"FlyChannelCastOmniUp "},
3495 {1450, L"DHJumpLandRun "},
3496 {1451, L"FlyDHJumpLandRun "},
3497 {1452, L"Cinematic01 "},
3498 {1453, L"FlyCinematic01 "},
3499 {1454, L"Cinematic02 "},
3500 {1455, L"FlyCinematic02 "},
3501 {1456, L"Cinematic03 "},
3502 {1457, L"FlyCinematic03 "},
3503 {1458, L"Cinematic04 "},
3504 {1459, L"FlyCinematic04 "},
3505 {1460, L"Cinematic05 "},
3506 {1461, L"FlyCinematic05 "},
3507 {1462, L"Cinematic06 "},
3508 {1463, L"FlyCinematic06 "},
3509 {1464, L"Cinematic07 "},
3510 {1465, L"FlyCinematic07 "},
3511 {1466, L"Cinematic08 "},
3512 {1467, L"FlyCinematic08 "},
3513 {1468, L"Cinematic09 "},
3514 {1469, L"FlyCinematic09 "},
3515 {1470, L"Cinematic10 "},
3516 {1471, L"FlyCinematic10"},
3517 {1472, L"TakeOffStart "},
3518 {1473, L"FlyTakeOffStart "},
3519 {1474, L"TakeOffFinish "},
3520 {1475, L"FlyTakeOffFinish "},
3521 {1476, L"LandStart "},
3522 {1477, L"FlyLandStart "},
3523 {1478, L"LandFinish "},
3524 {1479, L"FlyLandFinish"},
3525 {1480, L"WAWalkTalk "},
3526 {1481, L"FlyWAWalkTalk "},
3527 {1482, L"WAPerch03 "},
3528 {1483, L"FlyWAPerch03"},
3529 {1484, L"CarriageMountMoving "},
3530 {1485, L"FlyCarriageMountMoving"},
3531 {1486, L"TakeOffFinishFly "},
3532 {1487, L"FlyTakeOffFinishFly "},
3533 {1488, L"CombatAbility2HBig02 "},
3534 {1489, L"FlyCombatAbility2HBig02 "},
3535 {1490, L"MountWide "},
3536 {1491, L"FlyMountWide"},
3537 {1492, L"EmoteTalkSubdued "},
3538 {1493, L"FlyEmoteTalkSubdued"},
3539 {1494, L"WASit04 "},
3540 {1495, L"FlyWASit04"},
3541 {1496, L"MountSummon "},
3542 {1497, L"FlyMountSummon"},
3543 {1498, L"EmoteSelfie"},
3544 {1499, L"FlyEmoteSelfie"},
3545 {1500, L"CustomSpell11"},
3546 {1501, L"FlyCustomSpell11"},
3547 {1502, L"CustomSpell12"},
3548 {1503, L"FlyCustomSpell12"},
3549 {1504, L"CustomSpell13"},
3550 {1505, L"FlyCustomSpell13"},
3551 {1506, L"CustomSpell14"},
3552 {1507, L"FlyCustomSpell14"},
3553 {1508, L"CustomSpell15"},
3554 {1509, L"FlyCustomSpell15"},
3555 {1510, L"CustomSpell16"},
3556 {1511, L"FlyCustomSpell16"},
3557 {1512, L"CustomSpell17"},
3558 {1513, L"FlyCustomSpell17"},
3559 {1514, L"CustomSpell18"},
3560 {1515, L"FlyCustomSpell18"},
3561 {1516, L"CustomSpell19"},
3562 {1517, L"FlyCustomSpell19"},
3563 {1518, L"CustomSpell20"},
3564 {1519, L"FlyCustomSpell20"},
3565 {1520, L"AdvFlyLeft"},
3566 {1521, L"FlyAdvFlyLeft"},
3567 {1522, L"AdvFlyRight"},
3568 {1523, L"FlyAdvFlyRight"},
3569 {1524, L"AdvFlyForward"},
3570 {1525, L"FlyAdvFlyForward"},
3571 {1526, L"AdvFlyBackward"},
3572 {1527, L"FlyAdvFlyBackward"},
3573 {1528, L"AdvFlyUp"},
3574 {1529, L"FlyAdvFlyUp"},
3575 {1530, L"AdvFlyDown"},
3576 {1531, L"FlyAdvFlyDown"},
3577 {1532, L"AdvFlyForwardGlide"},
3578 {1533, L"FlyAdvFlyForwardGlide"},
3579 {1534, L"AdvFlyRoll"},
3580 {1535, L"FlyAdvFlyRoll"},
3581 {1536, L"ProfCookingLoop"},
3582 {1537, L"FlyProfCookingLoop"},
3583 {1538, L"ProfCookingStart"},
3584 {1539, L"FlyProfCookingStart"},
3585 {1540, L"ProfCookingEnd"},
3586 {1541, L"FlyProfCookingEnd"},
3587 {1542, L"WACurious"},
3588 {1543, L"FlyWACurious"},
3589 {1544, L"WAAlert"},
3590 {1545, L"FlyWAAlert"},
3591 {1546, L"WAInvestigate"},
3592 {1547, L"FlyWAInvestigate"},
3593 {1548, L"WAInteraction"},
3594 {1549, L"FlyWAInteraction"},
3595 {1550, L"WAThreaten"},
3596 {1551, L"FlyWAThreaten"},
3597 {1552, L"WAReact01"},
3598 {1553, L"FlyWAReact01"},
3599 {1554, L"WAReact02"},
3600 {1555, L"FlyWAReact02"},
3601 {1556, L"AdvFlyRollStart"},
3602 {1557, L"FlyAdvFlyRollStart"},
3603 {1558, L"AdvFlyRollEnd"},
3604 {1559, L"FlyAdvFlyRollEnd"},
3605 {1560, L"EmpBreathPrecast"},
3606 {1561, L"FlyEmpBreathPrecast"},
3607 {1562, L"EmpBreathPrecastChannel"},
3608 {1563, L"FlyEmpBreathPrecastChannel"},
3609 {1564, L"EmpBreathSpellCast"},
3610 {1565, L"FlyEmpBreathSpellCast"},
3611 {1566, L"EmpBreathSpellCastChannel"},
3612 {1567, L"FlyEmpBreathSpellCastChannel"},
3613 {1568, L"DracFlyBreathTakeoffStart"},
3614 {1569, L"FlyDracFlyBreathTakeoffStart"},
3615 {1570, L"DracFlyBreathTakeoffFinish"},
3616 {1571, L"FlyDracFlyBreathTakeoffFinish"},
3617 {1572, L"DracFlyBreath"},
3618 {1573, L"FlyDracFlyBreath"},
3619 {1574, L"DracFlyBreathLandStart"},
3620 {1575, L"FlyDracFlyBreathLandStart"},
3621 {1576, L"DracFlyBreathLandFinish"},
3622 {1577, L"FlyDracFlyBreathLandFinish"},
3623 {1578, L"DracAirDashLeft"},
3624 {1579, L"FlyDracAirDashLeft"},
3625 {1580, L"DracAirDashForward"},
3626 {1581, L"FlyDracAirDashForward"},
3627 {1582, L"DracAirDashBackward"},
3628 {1583, L"FlyDracAirDashBackward"},
3629 {1584, L"DracAirDashRight"},
3630 {1585, L"FlyDracAirDashRight"},
3631 {1586, L"LivingWorldProximityEnter"},
3632 {1587, L"FlyLivingWorldProximityEnter"},
3633 {1588, L"AdvFlyDownEnd"},
3634 {1589, L"FlyAdvFlyDownEnd"},
3635 {1590, L"LivingWorldProximityLoop"},
3636 {1591, L"FlyLivingWorldProximityLoop"},
3637 {1592, L"LivingWorldProximityLeave"},
3638 {1593, L"FlyLivingWorldProximityLeave"},
3639 {1594, L"EmpAirBarragePrecast"},
3640 {1595, L"FlyEmpAirBarragePrecast"},
3641 {1596, L"EmpAirBarragePrecastChannel"},
3642 {1597, L"FlyEmpAirBarragePrecastChannel"},
3643 {1598, L"EmpAirBarrageSpellCast"},
3644 {1599, L"FlyEmpAirBarrageSpellCast"},
3645 {1600, L"DracClawSwipeLeft"},
3646 {1601, L"FlyDracClawSwipeLeft"},
3647 {1602, L"DracClawSwipeRight"},
3648 {1603, L"FlyDracClawSwipeRight"},
3649 {1604, L"DracHoverIdle"},
3650 {1605, L"FlyDracHoverIdle"},
3651 {1606, L"DracHoverLeft"},
3652 {1607, L"FlyDracHoverLeft"},
3653 {1608, L"DracHoverRight"},
3654 {1609, L"FlyDracHoverRight"},
3655 {1610, L"DracHoverBackward"},
3656 {1611, L"FlyDracHoverBackward"},
3657 {1612, L"DracHoverForward"},
3658 {1613, L"FlyDracHoverForward"},
3659 {1614, L"DracAttackWings"},
3660 {1615, L"FlyDracAttackWings"},
3661 {1616, L"DracAttackTail"},
3662 {1617, L"FlyDracAttackTail"},
3663 {1618, L"AdvFlyStart"},
3664 {1619, L"FlyAdvFlyStart"},
3665 {1620, L"AdvFlyLand"},
3666 {1621, L"FlyAdvFlyLand"},
3667 {1622, L"AdvFlyLandRun"},
3668 {1623, L"FlyAdvFlyLandRun"},
3669 {1624, L"AdvFlyStrafeLeft"},
3670 {1625, L"FlyAdvFlyStrafeLeft"},
3671 {1626, L"AdvFlyStrafeRight"},
3672 {1627, L"FlyAdvFlyStrafeRight"},
3673 {1628, L"AdvFlyIdle"},
3674 {1629, L"FlyAdvFlyIdle"},
3675 {1630, L"AdvFlyRollRight"},
3676 {1631, L"FlyAdvFlyRollRight"},
3677 {1632, L"AdvFlyRollRightEnd"},
3678 {1633, L"FlyAdvFlyRollRightEnd"},
3679 {1634, L"AdvFlyRollLeft"},
3680 {1635, L"FlyAdvFlyRollLeft"},
3681 {1636, L"AdvFlyRollLeftEnd"},
3682 {1637, L"FlyAdvFlyRollLeftEnd"},
3683 {1638, L"AdvFlyFlap"},
3684 {1639, L"FlyAdvFlyFlap"},
3685 {1640, L"DracHoverDracClawSwipeLeft"},
3686 {1641, L"FlyDracHoverDracClawSwipeLeft"},
3687 {1642, L"DracHoverDracClawSwipeRight"},
3688 {1643, L"FlyDracHoverDracClawSwipeRight"},
3689 {1644, L"DracHoverDracAttackWings"},
3690 {1645, L"FlyDracHoverDracAttackWings"},
3691 {1646, L"DracHoverReadySpellOmni"},
3692 {1647, L"FlyDracHoverReadySpellOmni"},
3693 {1648, L"DracHoverSpellCastOmni"},
3694 {1649, L"FlyDracHoverSpellCastOmni"},
3695 {1650, L"DracHoverChannelSpellOmni"},
3696 {1651, L"FlyDracHoverChannelSpellOmni"},
3697 {1652, L"DracHoverReadySpellDirected"},
3698 {1653, L"FlyDracHoverReadySpellDirected"},
3699 {1654, L"DracHoverChannelSpellDirected"},
3700 {1655, L"FlyDracHoverChannelSpellDirected"},
3701 {1656, L"DracHoverSpellCastDirected"},
3702 {1657, L"FlyDracHoverSpellCastDirected"},
3703 {1658, L"DracHoverCastOutStrong"},
3704 {1659, L"FlyDracHoverCastOutStrong"},
3705 {1660, L"DracHoverBattleRoar"},
3706 {1661, L"FlyDracHoverBattleRoar"},
3707 {1662, L"DracHoverEmpBreathSpellCast"},
3708 {1663, L"FlyDracHoverEmpBreathSpellCast"},
3709 {1664, L"DracHoverEmpBreathSpellCastChannel"},
3710 {1665, L"FlyDracHoverEmpBreathSpellCastChannel"},
3711 {1666, L"LivingWorldTimeOfDayEnter"},
3712 {1667, L"FlyLivingWorldTimeOfDayEnter"},
3713 {1668, L"LivingWorldTimeOfDayLoop"},
3714 {1669, L"FlyLivingWorldTimeOfDayLoop"},
3715 {1670, L"LivingWorldTimeOfDayLeave"},
3716 {1671, L"FlyLivingWorldTimeOfDayLeave"},
3717 {1672, L"LivingWorldWeatherEnter"},
3718 {1673, L"FlyLivingWorldWeatherEnter"},
3719 {1674, L"LivingWorldWeatherLoop"},
3720 {1675, L"FlyLivingWorldWeatherLoop"},
3721 {1676, L"LivingWorldWeatherLeave"},
3722 {1677, L"FlyLivingWorldWeatherLeave"},
3723 {1678, L"AdvFlyDownStart"},
3724 {1679, L"FlyAdvFlyDownStart"},
3725 {1680, L"AdvFlyFlapBig"},
3726 {1681, L"FlyAdvFlyFlapBig"},
3727 {1682, L"DracHoverReadyUnarmed"},
3728 {1683, L"FlyDracHoverReadyUnarmed"},
3729 {1684, L"DracHoverAttackUnarmed"},
3730 {1685, L"FlyDracHoverAttackUnarmed"},
3731 {1686, L"DracHoverParryUnarmed"},
3732 {1687, L"FlyDracHoverParryUnarmed"},
3733 {1688, L"DracHoverCombatWound"},
3734 {1689, L"FlyDracHoverCombatWound"},
3735 {1690, L"DracHoverCombatCritical"},
3736 {1691, L"FlyDracHoverCombatCritical"},
3737 {1692, L"DracHoverAttackTail"},
3738 {1693, L"FlyDracHoverAttackTail"},
3739 {1694, L"Glide"},
3740 {1695, L"FlyGlide"},
3741 {1696, L"GlideEnd"},
3742 {1697, L"FlyGlideEnd"},
3743 {1698, L"DracClawSwipe"},
3744 {1699, L"FlyDracClawSwipe"},
3745 {1700, L"DracHoverDracClawSwipe"},
3746 {1701, L"FlyDracHoverDracClawSwipe"},
3747 {1702, L"AdvFlyFlapUp"},
3748 {1703, L"FlyAdvFlyFlapUp"},
3749 {1704, L"AdvFlySlowFall"},
3750 {1705, L"FlyAdvFlySlowFall"},
3751 {1706, L"AdvFlyFlapFoward"},
3752 {1707, L"FlyAdvFlyFlapFoward"},
3753 {1708, L"DracSpellCastWings"},
3754 {1709, L"FlyDracSpellCastWings"},
3755 {1710, L"DracHoverDracSpellCastWings"},
3756 {1711, L"FlyDracHoverDracSpellCastWings"},
3757 {1712, L"DracAirDashVertical"},
3758 {1713, L"FlyDracAirDashVertical"},
3759 {1714, L"DracAirDashRefresh"},
3760 {1715, L"FlyDracAirDashRefresh"},
3761 {1716, L"SkinningLoop"},
3762 {1717, L"FlySkinningLoop"},
3763 {1718, L"SkinningStart"},
3764 {1719, L"FlySkinningStart"},
3765 {1720, L"SkinningEnd"},
3766 {1721, L"FlySkinningEnd"},
3767 {1722, L"AdvFlyForwardGlideSlow"},
3768 {1723, L"FlyAdvFlyForwardGlideSlow"},
3769 {1724, L"AdvFlyForwardGlideFast"},
3770 {1725, L"FlyAdvFlyForwardGlideFast"},
3771 {1726, L"AdvFlySecondFlapUp"},
3772 {1727, L"FlyAdvFlySecondFlapUp"},
3773 {1728, L"FloatIdle"},
3774 {1729, L"FlyFloatIdle"},
3775 {1730, L"FloatWalk"},
3776 {1731, L"FlyFloatWalk"},
3777 {1732, L"CinematicTalk"},
3778 {1733, L"FlyCinematicTalk"},
3779 {1734, L"CinematicWAGuardEmoteSlam01"},
3780 {1735, L"FlyCinematicWAGuardEmoteSlam01"},
3781 {1736, L"WABlowHorn"},
3782 {1737, L"FlyWABlowHorn"},
3783 {1738, L"MountExtraWide"},
3784 {1739, L"FlyMountExtraWide"},
3785 {1740, L"WA2HIdle"},
3786 {1741, L"FlyWA2HIdle"},
3787 {1742, L"HerbalismLoop"},
3788 {1743, L"FlyHerbalismLoop"},
3789 {1744, L"CookingLoop"},
3790 {1745, L"FlyCookingLoop"},
3791 {1746, L"WAWeaponSharpenNoSheathe"},
3792 {1747, L"FlyWAWeaponSharpenNoSheathe"},
3793 {1748, L"CinematicDeath"},
3794 {1749, L"FlyCinematicDeath"},
3795 {1750, L"CinematicDeathPose"},
3796 {1751, L"FlyCinematicDeathPose"},
3797 {1752, L"EmpSlamPrecast"},
3798 {1753, L"FlyEmpSlamPrecast"},
3799 {1754, L"EmpSlamPrecastChannel"},
3800 {1755, L"FlyEmpSlamPrecastChannel"},
3801 {1756, L"EmpSlamSpellCast"},
3802 {1757, L"FlyEmpSlamSpellCast"},
3803 {1758, L"Climb"},
3804 {1759, L"FlyClimb"},
3805 {1760, L"ClimbStart"},
3806 {1761, L"FlyClimbStart"},
3807 {1762, L"ClimbEnd"},
3808 {1763, L"FlyClimbEnd"},
3809 {1764, L"MountLeanLeft"},
3810 {1765, L"FlyMountLeanLeft"},
3811 {1766, L"MountLeanRight"},
3812 {1767, L"FlyMountLeanRight"},
3813 {1768, L"MountDive"},
3814 {1769, L"FlyMountDive"},
3815 {1770, L"MountCrouch"},
3816 {1771, L"FlyMountCrouch"}
3817 };
3818
3819 if (animated && anims.size() > 0)
3820 {
3821 for (const auto& entry : AnimationNames)
3822 {
3823 result[entry.first] = entry.second;
3824 }
3825
3826 if (!result.empty())
3827 {
3828 LOG_INFO << "Found " << result.size() << " animations for model";
3829 }
3830 }
3831 return result;
3832}
3833
3834void WoWModel::save(pugi::xml_node& parentNode)
3835{
3836 pugi::xml_node node = parentNode.append_child("model");
3837 node.append_child("file").append_attribute("name") = modelname.c_str();
3838 cd.save(node);
3839}
3840
3841void WoWModel::load(const std::string& file)
3842{
3843 cd.load(file);
3844}
3845
3847{
3848 for (size_t i = 0; i < TEXTURE_MAX; i++)
3849 {
3850 if (specialTextures[i] == texnum)
3851 return true;
3852 }
3853 return false;
3854}
3855
3857{
3858 std::string result = "";
3859
3860 static std::map<CharGeosets, std::string> groups =
3861 {
3862 {CG_SKIN_OR_HAIR, "Skin or Hair"},
3863 {CG_FACE_1, "Face 1"},
3864 {CG_FACE_2, "Face 2"},
3865 {CG_FACE_3, "Face 3"},
3866 {CG_GLOVES, "Bracers"},
3867 {CG_BOOTS, "Boots"},
3868 {CG_EARS, "Ears"},
3869 {CG_SLEEVES, "Sleeves"},
3870 {CG_KNEEPADS, "Kneepads"},
3871 {CG_CHEST, "Chest"},
3872 {CG_PANTS, "Pants"},
3873 {CG_TABARD, "Tabard"},
3874 {CG_TROUSERS, "Trousers"},
3875 {CG_DH_LOINCLOTH, "Demon Hunter Loincloth"},
3876 {CG_CLOAK, "Cloak"},
3877 {CG_EYEGLOW, "Eye Glow"},
3878 {CG_BELT, "Belt"},
3879 {CG_BONE, "Bone"},
3880 {CG_FEET, "Feet"},
3881 {CG_GEOSET2100, "Geoset2100"},
3882 {CG_TORSO, "Torso"},
3883 {CG_HAND_ATTACHMENT, "Hand Attachment"},
3884 {CG_HEAD_ATTACHMENT, "Head Attachment"},
3885 {CG_DH_BLINDFOLDS, "Demon Hunter Blindfolds"},
3886 {CG_GEOSET2600, "Geoset2600"},
3887 {CG_GEOSET2700, "Geoset2700"},
3888 {CG_GEOSET2800, "Geoset2800"},
3889 {CG_MECHAGNOME_ARMS_OR_HANDS, "Mechagnome Arms or Hands"},
3890 {CG_MECHAGNOME_LEGS, "Mechagnome Legs"},
3891 {CG_MECHAGNOME_FEET, "Mechagnome Feet"},
3892 {CG_FACE, "Face"},
3893 {CG_EYES, "Eyes"},
3894 {CG_EYEBROWS, "Eyebrows"},
3895 {CG_EARRINGS, "Earrings"},
3896 {CG_NECKLACE, "Necklace"},
3897 {CG_HEADDRESS, "Headdress"},
3898 {CG_TAILS, "Tails"},
3899 {CG_VINES, "Vines"},
3900 {CG_TUSKS, "Tusks"},
3901 {CG_NOSES, "Noses"},
3902 {CG_HAIR_DECORATION, "Hair Decoration"},
3903 {CG_HORN_DECORATION, "Horn Decoration"}
3904 };
3905
3906 const auto it = groups.find(cg);
3907 if (it != groups.end())
3908 result = it->second;
3909
3910 return result;
3911}
3912
3913void WoWModel::showGeoset(uint geosetindex, bool value)
3914{
3915 if (geosetindex < geosets.size())
3916 geosets[geosetindex]->display = value;
3917}
3918
3920{
3921 bool result = false;
3922
3923 if (geosetindex < geosets.size())
3924 result = geosets[geosetindex]->display;
3925
3926 return result;
3927}
3928
3930{
3931 const int a = static_cast<int>(group) * 100;
3932 const int b = (static_cast<int>(group) + 1) * 100;
3933 const int geosetID = a + val;
3934
3935 // This loop must be done only on first geosets (original ones) in case of merged models
3936 // This is why rawGeosets.size() is used as a stop criteria even if we are looping over
3937 // geosets member
3938 for (uint i = 0; i < rawGeosets.size(); i++)
3939 {
3940 const int id = geosets[i]->id;
3941 if (id > a && id < b)
3942 showGeoset(i, (id == geosetID));
3943 }
3944 /*
3945 for (auto it : mergedModels)
3946 it->setGeosetGroupDisplay(group, val);
3947 */
3948}
3949
3950void WoWModel::setCreatureGeosetData(std::set<GeosetNum> cgd)
3951{
3952 // Hide geosets that were set by old creatureGeosetData:
3953 if (creatureGeosetData.size() > 0)
3955
3956 creatureGeosetData = cgd;
3957
3958 if (cgd.size() == 0 && creatureGeosetDataID == 0)
3959 return;
3960
3961 int geomax = 900;
3962
3963 if (cgd.size() > 0)
3964 {
3965 geomax = *cgd.rbegin() + 1; // highest value in set
3966 // We should only be dealing with geosets below 900, but just in case
3967 // Blizzard changes this, we'll set the max higher and log that it's happened:
3968 if (geomax > 900)
3969 {
3970 LOG_ERROR << "setCreatureGeosetData value of " << geomax <<
3971 " detected. We were assuming the maximum was 899.";
3972 geomax = ((geomax / 100) + 1) * 100; // round the max up to the next 100 (next geoset group)
3973 }
3974 else
3975 geomax = 900;
3976 }
3977
3978 // If creatureGeosetData is used , or creatureGeosetDataID > 0, then
3979 // we switch off ALL geosets from 1 to 899 that aren't specified by it:
3980 for (uint i = 0; i < rawGeosets.size(); i++)
3981 {
3982 int id = geosets[i]->id;
3983 if (id > 0 && id < geomax)
3984 showGeoset(i, cgd.count(id) > 0);
3985 }
3986}
3987
3988WoWModel* WoWModel::mergeModel(std::string name, int type, bool noRefresh)
3989{
3990 std::replace(name.begin(), name.end(), '\\', '/');
3991
3992 LOG_INFO << __FUNCTION__ << name.c_str();
3993 const auto it = std::find_if(std::begin(mergedModels), std::end(mergedModels),
3994 [&](const WoWModel* m) { return m->gamefile->fullname() == name; });
3995
3996 if (it != mergedModels.end())
3997 return *it;
3998
3999 WoWModel* m = new WoWModel(GAMEDIRECTORY.getFile(name), true);
4000
4001 if (!m->ok)
4002 {
4003 delete m;
4004 return nullptr;
4005 }
4006
4007 m->mergedModelType = type;
4008 return mergeModel(m, type, noRefresh);
4009}
4010
4011WoWModel* WoWModel::mergeModel(uint fileID, int type, bool noRefresh)
4012{
4013 LOG_INFO << __FUNCTION__ << fileID;
4014 const auto it = std::find_if(std::begin(mergedModels), std::end(mergedModels),
4015 [&](const WoWModel* m) { return m->gamefile->fileDataId() == fileID; });
4016 if (it != mergedModels.end())
4017 return *it;
4018
4019 WoWModel* m = new WoWModel(GAMEDIRECTORY.getFile(fileID), true);
4020
4021 if (!m->ok)
4022 {
4023 delete m;
4024 return nullptr;
4025 }
4026
4027 m->mergedModelType = type;
4028 return mergeModel(m, type, noRefresh);
4029}
4030
4031WoWModel* WoWModel::mergeModel(WoWModel* m, int type, bool noRefresh)
4032{
4033 LOG_INFO << __FUNCTION__ << m;
4034 m->mergedModelType = type;
4035 const auto it = mergedModels.insert(m);
4036 if (it.second == true && !noRefresh) // new element inserted
4038 return m;
4039}
4040
4042{
4043 for (const auto it : mergedModels)
4044 {
4045 if (it->gamefile->fileDataId() == fileID)
4046 return it;
4047 }
4048 return nullptr;
4049}
4050
4052{
4053 /*
4054 LOG_INFO << __FUNCTION__;
4055 for (auto it : mergedModels)
4056 LOG_INFO << it->name() << it->gamefile->fullname();
4057 */
4058
4059 // first reinit this model with original data
4062 passes = rawPasses;
4064 textures.resize(TEXTURE_MAX);
4067
4068 uint mergeIndex = 0;
4069 for (const auto modelsIt : mergedModels)
4070 {
4071 const uint nbVertices = static_cast<uint>(origVertices.size());
4072 const uint nbIndices = static_cast<uint>(indices.size());
4073 const uint nbGeosets = static_cast<uint>(geosets.size());
4074
4075 // reinit merged model as well, just in case
4076 modelsIt->origVertices = modelsIt->rawVertices;
4077 modelsIt->indices = modelsIt->rawIndices;
4078 modelsIt->passes = modelsIt->rawPasses;
4079 modelsIt->restoreRawGeosets();
4080
4081 mergeIndex++;
4082
4083 for (auto it : modelsIt->geosets)
4084 {
4085 it->istart += nbIndices;
4086 it->vstart += nbVertices;
4087 geosets.push_back(it);
4088 }
4089
4090 // build bone correspondence table
4091 uint32 nbBonesInNewModel = modelsIt->header.nBones;
4092 int16* boneConvertTable = new int16[nbBonesInNewModel];
4093
4094 for (uint i = 0; i < nbBonesInNewModel; ++i)
4095 boneConvertTable[i] = i;
4096
4097 for (uint i = 0; i < nbBonesInNewModel; ++i)
4098 {
4099 glm::vec3 pivot = modelsIt->bones[i].pivot;
4100 for (uint b = 0; b < bones.size(); ++b)
4101 {
4102 if (glm::all(glm::epsilonEqual(bones[b].pivot, pivot, glm::vec3(0.0001f))) &&
4103 (bones[b].boneDef.unknown == modelsIt->bones[i].boneDef.unknown))
4104 {
4105 boneConvertTable[i] = b;
4106 break;
4107 }
4108 }
4109 }
4110
4111#ifdef DEBUG_DH_SUPPORT
4112 for (uint i = 0; i < nbBonesInNewModel; ++i)
4113 LOG_INFO << i << "=>" << boneConvertTable[i];
4114#endif
4115
4116 // change bone from new model to character one
4117 for (auto& it : modelsIt->origVertices)
4118 {
4119 for (uint i = 0; i < 4; ++i)
4120 {
4121 if (it.weights[i] > 0)
4122 it.bones[i] = boneConvertTable[it.bones[i]];
4123 }
4124 }
4125
4126 delete[] boneConvertTable;
4127
4128 origVertices.reserve(origVertices.size() + modelsIt->origVertices.size());
4129 origVertices.insert(origVertices.end(), modelsIt->origVertices.begin(), modelsIt->origVertices.end());
4130
4131 indices.reserve(indices.size() + modelsIt->indices.size());
4132
4133 for (const auto& it : modelsIt->indices)
4134 indices.push_back(it + nbVertices);
4135
4136 // retrieve tex id associated to model hands (needed for DH)
4138 for (const auto it : passes)
4139 {
4140 if (geosets[it->geoIndex]->id / 100 == 23)
4141 handTex = it->tex;
4142 }
4143
4144 for (const auto it : modelsIt->passes)
4145 {
4146 ModelRenderPass* p = new ModelRenderPass(*it);
4147 p->model = this;
4148 p->geoIndex += nbGeosets;
4149 if (geosets[it->geoIndex]->id / 100 != 23) // don't copy texture for hands
4150 p->tex += (mergeIndex * TEXTURE_MAX);
4151 else
4152 p->tex = handTex; // use regular model texture instead
4153
4154 passes.push_back(p);
4155 }
4156
4157#ifdef DEBUG_DH_SUPPORT
4158 LOG_INFO << "---- FINAL ----";
4159 LOG_INFO << "nbGeosets =" << geosets.size();
4160 LOG_INFO << "nbVertices =" << origVertices.size();
4161 LOG_INFO << "nbIndices =" << indices.size();
4162 LOG_INFO << "nbPasses =" << passes.size();
4163#endif
4164
4165 // add model textures
4166 for (const auto it : modelsIt->textures)
4167 {
4169 textures.push_back(TEXTUREMANAGER.add(GAMEDIRECTORY.getFile(TEXTUREMANAGER.get(it))));
4170 else
4172 }
4173
4174 for (auto it : modelsIt->specialTextures)
4175 {
4176 if (it == -1 || it == TEXTURE_SKIN_EXTRA) // if texture type is TEXTURE_SKIN_EXTRA use parent texture
4177 specialTextures.push_back(it);
4178 else
4179 specialTextures.push_back(it + (mergeIndex * TEXTURE_MAX));
4180 }
4181
4182 for (auto it : modelsIt->replaceTextures)
4183 replaceTextures.push_back(it);
4184 }
4185
4186 delete[] vertices;
4187 delete[] normals;
4188
4189 vertices = new glm::vec3[origVertices.size()];
4190 normals = new glm::vec3[origVertices.size()];
4191
4192 uint i = 0;
4193 for (auto& ov_it : origVertices)
4194 {
4195 // Set the data for our vertices, normals from the model data
4196 vertices[i] = ov_it.pos;
4197 normals[i] = glm::normalize(ov_it.normal);
4198 ++i;
4199 }
4200
4201 const size_t size = (origVertices.size() * sizeof(float));
4202 vbufsize = (3 * size); // we multiple by 3 for the x, y, z positions of the vertex
4203
4204 if (video.supportVBO)
4205 {
4206 glDeleteBuffersARB(1, &nbuf);
4207 glDeleteBuffersARB(1, &vbuf);
4208
4209 // Vert buffer
4210 glGenBuffersARB(1, &vbuf);
4211 glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbuf);
4212 glBufferDataARB(GL_ARRAY_BUFFER_ARB, vbufsize, vertices, GL_STATIC_DRAW_ARB);
4213 delete[] vertices;
4214 vertices = nullptr;
4215
4216 // normals buffer
4217 glGenBuffersARB(1, &nbuf);
4218 glBindBufferARB(GL_ARRAY_BUFFER_ARB, nbuf);
4219 glBufferDataARB(GL_ARRAY_BUFFER_ARB, vbufsize, normals, GL_STATIC_DRAW_ARB);
4220 delete[] normals;
4221 normals = nullptr;
4222
4223 // clean bind
4224 glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
4225 }
4226}
4227
4228void WoWModel::unmergeModel(std::string name)
4229{
4230 std::replace(name.begin(), name.end(), '\\', '/');
4231 LOG_INFO << __FUNCTION__ << name.c_str();
4232 const auto it = std::find_if(std::begin(mergedModels),
4233 std::end(mergedModels),
4234 [&](const WoWModel* m) { return m->gamefile->fullname() == name; });
4235
4236 if (it != mergedModels.end())
4237 {
4238 WoWModel* m = *it;
4239 unmergeModel(m);
4240 delete m;
4241 }
4242}
4243
4245{
4246 LOG_INFO << __FUNCTION__ << fileID;
4247 const auto it = std::find_if(std::begin(mergedModels),
4248 std::end(mergedModels),
4249 [&](const WoWModel* m) { return m->gamefile->fileDataId() == fileID; });
4250
4251 if (it != mergedModels.end())
4252 {
4253 WoWModel* m = *it;
4254 unmergeModel(m);
4255 delete m;
4256 }
4257}
4258
4260{
4261 LOG_INFO << __FUNCTION__ << m->name();
4262 mergedModels.erase(m);
4264}
4265
4267{
4268 // apply chardetails customization
4269 cd.refresh();
4270
4271 // if no race info found, simply update geosets
4272 if (infos.raceID == -1)
4273 return;
4274
4275 const auto headItemId = getItemId(CS_HEAD);
4276
4277 if (headItemId != -1 && cd.autoHideGeosetsForHeadItems)
4278 {
4279 // Resolve: ItemModifiedAppearance(ItemID) -> ItemAppearanceID -> ItemAppearance -> ItemDisplayInfoID
4280 // -> ItemDisplayInfo -> HelmetGeosetVis1/2 -> HelmetGeosetData(RaceID, HelmetGeosetVisDataID) -> HideGeosetGroup
4281 uint32_t helmetGeosetVisDataID = 0;
4282
4283 if (const DB2Table* imaTbl = WOWDB.getTable("ItemModifiedAppearance"))
4284 {
4285 for (const auto& imaRow : *imaTbl)
4286 {
4287 if (static_cast<int>(imaRow.getUInt("ItemID")) == headItemId)
4288 {
4289 const uint32_t itemAppearanceID = imaRow.getUInt("ItemAppearanceID");
4290 if (const DB2Table* iaTbl = WOWDB.getTable("ItemAppearance"))
4291 {
4292 DB2Row iaRow = iaTbl->getRow(itemAppearanceID);
4293 if (iaRow)
4294 {
4295 const uint32_t displayInfoID = iaRow.getUInt("ItemDisplayInfoID");
4296 if (const DB2Table* idiTbl = WOWDB.getTable("ItemDisplayInfo"))
4297 {
4298 DB2Row idiRow = idiTbl->getRow(displayInfoID);
4299 if (idiRow)
4300 {
4301 helmetGeosetVisDataID = (infos.sexID == 0)
4302 ? idiRow.getUInt("HelmetGeosetVis1")
4303 : idiRow.getUInt("HelmetGeosetVis2");
4304 }
4305 }
4306 }
4307 }
4308 break;
4309 }
4310 }
4311 }
4312
4313 if (helmetGeosetVisDataID != 0)
4314 {
4315 if (const DB2Table* hgdTbl = WOWDB.getTable("HelmetGeosetData"))
4316 {
4317 for (const auto& hgdRow : *hgdTbl)
4318 {
4319 if (static_cast<int>(hgdRow.getUInt("RaceID")) == infos.raceID &&
4320 hgdRow.getUInt("HelmetGeosetVisDataID") == helmetGeosetVisDataID)
4321 {
4322 setGeosetGroupDisplay(static_cast<CharGeosets>(hgdRow.getUInt("HideGeosetGroup")), 0);
4323 }
4324 }
4325 }
4326 }
4327 }
4328
4329 // reset char texture
4331 for (const auto& t : cd.textures)
4332 {
4333 if (t.type != 1)
4334 {
4335 updateTextureList(GAMEDIRECTORY.getFile(t.fileId), t.type);
4336 }
4337 else
4338 {
4339 tex.addLayer(GAMEDIRECTORY.getFile(t.fileId), t.region, t.layer, t.blendMode);
4340 }
4341 }
4342
4343 //refresh equipment
4344 for (auto* it : *this)
4345 it->refresh();
4346
4347 LOG_INFO << "Current Equipment :"
4348 << "Head" << getItemId(CS_HEAD)
4349 << "Shoulder" << getItemId(CS_SHOULDER)
4350 << "Shirt" << getItemId(CS_SHIRT)
4351 << "Chest" << getItemId(CS_CHEST)
4352 << "Belt" << getItemId(CS_BELT)
4353 << "Legs" << getItemId(CS_PANTS)
4354 << "Boots" << getItemId(CS_BOOTS)
4355 << "Bracers" << getItemId(CS_BRACERS)
4356 << "Gloves" << getItemId(CS_GLOVES)
4357 << "Cape" << getItemId(CS_CAPE)
4358 << "Right Hand" << getItemId(CS_HAND_RIGHT)
4359 << "Left Hand" << getItemId(CS_HAND_LEFT)
4360 << "Quiver" << getItemId(CS_QUIVER)
4361 << "Tabard" << getItemId(CS_TABARD);
4362
4363 // gloves - this is so gloves have preference over shirt sleeves.
4364 if (cd.geosets[CG_GLOVES] > 1)
4365 cd.geosets[CG_SLEEVES] = 0;
4366
4367 // If model is one of these races, show the feet (don't wear boots)
4369
4370 // finalize character texture
4371 const GLuint charTex = 0;
4372 tex.compose(charTex);
4373
4374 // set replacable textures
4375 replaceTextures[TEXTURE_SKIN] = charTex;
4376
4377 // refresh merged models (must happen BEFORE geoset display changes,
4378 // because refreshMerging() resets geosets from rawGeosets)
4380
4381 // Apply geoset display states after merging so they aren't overwritten
4382 for (const auto geo : cd.geosets)
4383 setGeosetGroupDisplay(static_cast<CharGeosets>(geo.first), geo.second);
4384
4385 // Group 32 (CG_FACE / HeadSwap): enable ALL variants by default (matches wow.export)
4386 {
4387 constexpr int faceA = static_cast<int>(CG_FACE) * 100;
4388 constexpr int faceB = (static_cast<int>(CG_FACE) + 1) * 100;
4389 for (uint i = 0; i < rawGeosets.size(); i++)
4390 {
4391 const int id = geosets[i]->id;
4392 if (id >= faceA && id < faceB)
4393 showGeoset(i, true);
4394 }
4395 }
4396
4397 // Eye Glow Geosets are ID 1701, 1702, etc.
4398 const int egt = static_cast<int>(cd.eyeGlowType);
4399 const int egtId = CG_EYEGLOW * 100 + egt + 1; // CG_EYEGLOW = 17
4400 for (size_t i = 0; i < rawGeosets.size(); i++)
4401 {
4402 const int id = geosets[i]->id;
4403 if ((int)(id / 100) == CG_EYEGLOW) // geosets 1700..1799
4404 showGeoset(static_cast<uint>(i), (id == egtId));
4405 }
4406}
4407
4409{
4410 if (specialTextures[Tex] == TEXTURE_SKIN)
4411 return "Body.blp";
4412 else
4413 return TEXTUREMANAGER.get(getGLTexture(Tex));
4414}
4415
4417{
4418 if (Tex >= specialTextures.size())
4420
4421 if (specialTextures[Tex] == -1)
4422 return textures[Tex];
4423 else
4424 return replaceTextures[specialTextures[Tex]];
4425}
4426
4428{
4429 const auto rawCount = rawGeosets.size();
4430
4431 // Only save display state for raw geosets (merged model geosets will be discarded)
4432 std::vector<bool> geosetDisplayStatus;
4433 geosetDisplayStatus.reserve(rawCount);
4434 for (size_t i = 0; i < rawCount && i < geosets.size(); i++)
4435 geosetDisplayStatus.push_back(geosets[i]->display);
4436
4437 for (const auto it : geosets)
4438 delete it;
4439
4440 geosets.clear();
4441
4442 for (const auto it : rawGeosets)
4443 {
4444 auto* geo = new ModelGeosetHD(*it);
4445 geosets.push_back(geo);
4446 }
4447
4448 for (size_t i = 0; i < geosetDisplayStatus.size(); i++)
4449 geosets[i]->display = geosetDisplayStatus[i];
4450}
4451
4453{
4454 for (uint i = 0; i < geosets.size(); i++)
4455 showGeoset(i, false);
4456}
4457
4458std::ostream& operator<<(std::ostream& out, const WoWModel& m)
4459{
4460 out << "<m2>" << endl;
4461 out << " <info>" << endl;
4462 out << " <modelname>" << m.modelname.c_str() << "</modelname>" << endl;
4463 out << " </info>" << endl;
4464 out << " <header>" << endl;
4465 // out << " <id>" << m.header.id << "</id>" << endl;
4466 out << " <nameLength>" << m.header.nameLength << "</nameLength>" << endl;
4467 out << " <nameOfs>" << m.header.nameOfs << "</nameOfs>" << endl;
4468 // out << " <name>" << f.getBuffer()+m.header.nameOfs << "</name>" << endl; // @TODO
4469 out << " <GlobalModelFlags>" << m.header.GlobalModelFlags << "</GlobalModelFlags>" << endl;
4470 out << " <nGlobalSequences>" << m.header.nGlobalSequences << "</nGlobalSequences>" << endl;
4471 out << " <ofsGlobalSequences>" << m.header.ofsGlobalSequences << "</ofsGlobalSequences>" << endl;
4472 out << " <nAnimations>" << m.header.nAnimations << "</nAnimations>" << endl;
4473 out << " <ofsAnimations>" << m.header.ofsAnimations << "</ofsAnimations>" << endl;
4474 out << " <nAnimationLookup>" << m.header.nAnimationLookup << "</nAnimationLookup>" << endl;
4475 out << " <ofsAnimationLookup>" << m.header.ofsAnimationLookup << "</ofsAnimationLookup>" << endl;
4476 out << " <nBones>" << m.header.nBones << "</nBones>" << endl;
4477 out << " <ofsBones>" << m.header.ofsBones << "</ofsBones>" << endl;
4478 out << " <nKeyBoneLookup>" << m.header.nKeyBoneLookup << "</nKeyBoneLookup>" << endl;
4479 out << " <ofsKeyBoneLookup>" << m.header.ofsKeyBoneLookup << "</ofsKeyBoneLookup>" << endl;
4480 out << " <nVertices>" << m.header.nVertices << "</nVertices>" << endl;
4481 out << " <ofsVertices>" << m.header.ofsVertices << "</ofsVertices>" << endl;
4482 out << " <nViews>" << m.header.nViews << "</nViews>" << endl;
4483 out << " <lodname>" << m.lodname.c_str() << "</lodname>" << endl;
4484 out << " <nColors>" << m.header.nColors << "</nColors>" << endl;
4485 out << " <ofsColors>" << m.header.ofsColors << "</ofsColors>" << endl;
4486 out << " <nTextures>" << m.header.nTextures << "</nTextures>" << endl;
4487 out << " <ofsTextures>" << m.header.ofsTextures << "</ofsTextures>" << endl;
4488 out << " <nTransparency>" << m.header.nTransparency << "</nTransparency>" << endl;
4489 out << " <ofsTransparency>" << m.header.ofsTransparency << "</ofsTransparency>" << endl;
4490 out << " <nTexAnims>" << m.header.nTexAnims << "</nTexAnims>" << endl;
4491 out << " <ofsTexAnims>" << m.header.ofsTexAnims << "</ofsTexAnims>" << endl;
4492 out << " <nTexReplace>" << m.header.nTexReplace << "</nTexReplace>" << endl;
4493 out << " <ofsTexReplace>" << m.header.ofsTexReplace << "</ofsTexReplace>" << endl;
4494 out << " <nTexFlags>" << m.header.nTexFlags << "</nTexFlags>" << endl;
4495 out << " <ofsTexFlags>" << m.header.ofsTexFlags << "</ofsTexFlags>" << endl;
4496 out << " <nBoneLookup>" << m.header.nBoneLookup << "</nBoneLookup>" << endl;
4497 out << " <ofsBoneLookup>" << m.header.ofsBoneLookup << "</ofsBoneLookup>" << endl;
4498 out << " <nTexLookup>" << m.header.nTexLookup << "</nTexLookup>" << endl;
4499 out << " <ofsTexLookup>" << m.header.ofsTexLookup << "</ofsTexLookup>" << endl;
4500 out << " <nTexUnitLookup>" << m.header.nTexUnitLookup << "</nTexUnitLookup>" << endl;
4501 out << " <ofsTexUnitLookup>" << m.header.ofsTexUnitLookup << "</ofsTexUnitLookup>" << endl;
4502 out << " <nTransparencyLookup>" << m.header.nTransparencyLookup << "</nTransparencyLookup>" << endl;
4503 out << " <ofsTransparencyLookup>" << m.header.ofsTransparencyLookup << "</ofsTransparencyLookup>" << endl;
4504 out << " <nTexAnimLookup>" << m.header.nTexAnimLookup << "</nTexAnimLookup>" << endl;
4505 out << " <ofsTexAnimLookup>" << m.header.ofsTexAnimLookup << "</ofsTexAnimLookup>" << endl;
4506 out << " <collisionSphere>" << endl;
4507 out << " <min>" << m.header.collisionSphere.min.x << " " << m.header.collisionSphere.min.y << " " << m.header.
4508 collisionSphere.min.z << "</min>" << endl;
4509 out << " <max>" << m.header.collisionSphere.max.x << " " << m.header.collisionSphere.max.y << " " << m.header.
4510 collisionSphere.max.z << "</max>" << endl;
4511 out << " <radius>" << m.header.collisionSphere.radius << "</radius>" << endl;
4512 out << " </collisionSphere>" << endl;
4513 out << " <boundSphere>" << endl;
4514 out << " <min>" << m.header.boundSphere.min.x << " " << m.header.boundSphere.min.y << " " << m.header.
4515 boundSphere.min.z << "</min>" << endl;
4516 out << " <min>" << m.header.boundSphere.max.x << " " << m.header.boundSphere.max.y << " " << m.header.
4517 boundSphere.max.z << "</min>" << endl;
4518 out << " <radius>" << m.header.boundSphere.radius << "</radius>" << endl;
4519 out << " </boundSphere>" << endl;
4520 out << " <nBoundingTriangles>" << m.header.nBoundingTriangles << "</nBoundingTriangles>" << endl;
4521 out << " <ofsBoundingTriangles>" << m.header.ofsBoundingTriangles << "</ofsBoundingTriangles>" << endl;
4522 out << " <nBoundingVertices>" << m.header.nBoundingVertices << "</nBoundingVertices>" << endl;
4523 out << " <ofsBoundingVertices>" << m.header.ofsBoundingVertices << "</ofsBoundingVertices>" << endl;
4524 out << " <nBoundingNormals>" << m.header.nBoundingNormals << "</nBoundingNormals>" << endl;
4525 out << " <ofsBoundingNormals>" << m.header.ofsBoundingNormals << "</ofsBoundingNormals>" << endl;
4526 out << " <nAttachments>" << m.header.nAttachments << "</nAttachments>" << endl;
4527 out << " <ofsAttachments>" << m.header.ofsAttachments << "</ofsAttachments>" << endl;
4528 out << " <nAttachLookup>" << m.header.nAttachLookup << "</nAttachLookup>" << endl;
4529 out << " <ofsAttachLookup>" << m.header.ofsAttachLookup << "</ofsAttachLookup>" << endl;
4530 out << " <nEvents>" << m.header.nEvents << "</nEvents>" << endl;
4531 out << " <ofsEvents>" << m.header.ofsEvents << "</ofsEvents>" << endl;
4532 out << " <nLights>" << m.header.nLights << "</nLights>" << endl;
4533 out << " <ofsLights>" << m.header.ofsLights << "</ofsLights>" << endl;
4534 out << " <nCameras>" << m.header.nCameras << "</nCameras>" << endl;
4535 out << " <ofsCameras>" << m.header.ofsCameras << "</ofsCameras>" << endl;
4536 out << " <nCameraLookup>" << m.header.nCameraLookup << "</nCameraLookup>" << endl;
4537 out << " <ofsCameraLookup>" << m.header.ofsCameraLookup << "</ofsCameraLookup>" << endl;
4538 out << " <nRibbonEmitters>" << m.header.nRibbonEmitters << "</nRibbonEmitters>" << endl;
4539 out << " <ofsRibbonEmitters>" << m.header.ofsRibbonEmitters << "</ofsRibbonEmitters>" << endl;
4540 out << " <nParticleEmitters>" << m.header.nParticleEmitters << "</nParticleEmitters>" << endl;
4541 out << " <ofsParticleEmitters>" << m.header.ofsParticleEmitters << "</ofsParticleEmitters>" << endl;
4542 out << " </header>" << endl;
4543
4544 out << " <SkeletonAndAnimation>" << endl;
4545
4546 out << " <GlobalSequences size=\"" << m.globalSequences.size() << "\">" << endl;
4547 for (const unsigned int globalSequence : m.globalSequences)
4548 out << "<Sequence>" << globalSequence << "</Sequence>" << endl;
4549 out << " </GlobalSequences>" << endl;
4550
4551 out << " <Animations size=\"" << m.anims.size() << "\">" << endl;
4552 for (size_t i = 0; i < m.anims.size(); i++)
4553 {
4554 out << " <Animation id=\"" << i << "\">" << endl;
4555 out << " <animID>" << m.anims[i].animID << "</animID>" << endl;
4556 std::string strName = AnimMapper::getAnimName(m.anims[i].animID);
4557 out << " <animName>" << strName << "</animName>" << endl;
4558 out << " <length>" << m.anims[i].length << "</length>" << endl;
4559 out << " <moveSpeed>" << m.anims[i].moveSpeed << "</moveSpeed>" << endl;
4560 out << " <flags>" << m.anims[i].flags << "</flags>" << endl;
4561 out << " <probability>" << m.anims[i].probability << "</probability>" << endl;
4562 out << " <d1>" << m.anims[i].d1 << "</d1>" << endl;
4563 out << " <d2>" << m.anims[i].d2 << "</d2>" << endl;
4564 out << " <playSpeed>" << m.anims[i].playSpeed << "</playSpeed>" << endl;
4565 out << " <boxA>" << m.anims[i].boundSphere.min.x << " " << m.anims[i].boundSphere.min.y << " " << m.anims[
4566 i].boundSphere.min.z << "</boxA>" << endl;
4567 out << " <boxA>" << m.anims[i].boundSphere.max.x << " " << m.anims[i].boundSphere.max.y << " " << m.anims[
4568 i].boundSphere.max.z << "</boxA>" << endl;
4569 out << " <rad>" << m.anims[i].boundSphere.radius << "</rad>" << endl;
4570 out << " <NextAnimation>" << m.anims[i].NextAnimation << "</NextAnimation>" << endl;
4571 out << " <Index>" << m.anims[i].Index << "</Index>" << endl;
4572 out << " </Animation>" << endl;
4573 }
4574 out << " </Animations>" << endl;
4575
4576 out << " <AnimationLookups size=\"" << m.animLookups.size() << "\">" << endl;
4577 for (size_t i = 0; i < m.animLookups.size(); i++)
4578 out << " <AnimationLookup id=\"" << i << "\">" << m.animLookups[i] << "</AnimationLookup>" << endl;
4579 out << " </AnimationLookups>" << endl;
4580
4581 out << " <Bones size=\"" << m.bones.size() << "\">" << endl;
4582 for (size_t i = 0; i < m.bones.size(); i++)
4583 {
4584 out << " <Bone id=\"" << i << "\">" << endl;
4585 out << " <keyboneid>" << m.bones[i].boneDef.keyboneid << "</keyboneid>" << endl;
4586 out << " <billboard>" << m.bones[i].billboard << "</billboard>" << endl;
4587 out << " <parent>" << m.bones[i].boneDef.parent << "</parent>" << endl;
4588 out << " <geoid>" << m.bones[i].boneDef.geoid << "</geoid>" << endl;
4589 out << " <unknown>" << m.bones[i].boneDef.unknown << "</unknown>" << endl;
4590#if 1 // too huge
4591 // AB translation
4592 out << " <trans>" << endl;
4593 out << m.bones[i].trans;
4594 out << " </trans>" << endl;
4595 // AB rotation
4596 out << " <rot>" << endl;
4597 out << m.bones[i].rot;
4598 out << " </rot>" << endl;
4599 // AB scaling
4600 out << " <scale>" << endl;
4601 out << m.bones[i].scale;
4602 out << " </scale>" << endl;
4603#endif
4604 out << " <pivot>" << m.bones[i].boneDef.pivot.x << " " << m.bones[i].boneDef.pivot.y << " " << m.bones[i].
4605 boneDef.pivot.z << "</pivot>" << endl;
4606 out << " </Bone>" << endl;
4607 }
4608 out << " </Bones>" << endl;
4609
4610 // out << " <BoneLookups size=\"" << m.header.nBoneLookup << "\">" << endl;
4611 // uint16 *boneLookup = (uint16 *)(f.getBuffer() + m.header.ofsBoneLookup);
4612 // for(size_t i=0; i<m.header.nBoneLookup; i++) {
4613 // out << " <BoneLookup id=\"" << i << "\">" << boneLookup[i] << "</BoneLookup>" << endl;
4614 // }
4615 // out << " </BoneLookups>" << endl;
4616
4617 out << " <KeyBoneLookups size=\"" << m.header.nKeyBoneLookup << "\">" << endl;
4618 for (size_t i = 0; i < m.header.nKeyBoneLookup; i++)
4619 out << " <KeyBoneLookup id=\"" << i << "\">" << m.keyBoneLookup[i] << "</KeyBoneLookup>" << endl;
4620 out << " </KeyBoneLookups>" << endl;
4621
4622 out << " </SkeletonAndAnimation>" << endl;
4623
4624 out << " <GeometryAndRendering>" << endl;
4625
4626 // out << " <Vertices size=\"" << m.header.nVertices << "\">" << endl;
4627 // ModelVertex *verts = (ModelVertex*)(f.getBuffer() + m.header.ofsVertices);
4628 // for(uint32 i=0; i<m.header.nVertices; i++) {
4629 // out << " <Vertice id=\"" << i << "\">" << endl;
4630 // out << " <pos>" << verts[i].pos << "</pos>" << endl; // TODO
4631 // out << " </Vertice>" << endl;
4632 // }
4633 out << " </Vertices>" << endl; // TODO
4634 out << " <Views>" << endl;
4635
4636 // out << " <Indices size=\"" << view->nIndex << "\">" << endl;
4637 // out << " </Indices>" << endl; // TODO
4638 // out << " <Triangles size=\""<< view->nTris << "\">" << endl;
4639 // out << " </Triangles>" << endl; // TODO
4640 // out << " <Properties size=\"" << view->nProps << "\">" << endl;
4641 // out << " </Properties>" << endl; // TODO
4642 // out << " <Subs size=\"" << view->nSub << "\">" << endl;
4643 // out << " </Subs>" << endl; // TODO
4644
4645 out << " <RenderPasses size=\"" << m.passes.size() << "\">" << endl;
4646 for (size_t i = 0; i < m.passes.size(); i++)
4647 {
4648 out << " <RenderPass id=\"" << i << "\">" << endl;
4649 const ModelRenderPass* p = m.passes[i];
4650 const ModelGeosetHD* geoset = m.geosets[p->geoIndex];
4651 out << " <indexStart>" << geoset->istart << "</indexStart>" << endl;
4652 out << " <indexCount>" << geoset->icount << "</indexCount>" << endl;
4653 out << " <vertexStart>" << geoset->vstart << "</vertexStart>" << endl;
4654 out << " <vertexEnd>" << geoset->vstart + geoset->vcount << "</vertexEnd>" << endl;
4655 out << " <tex>" << p->tex << "</tex>" << endl;
4657 out << " <texName>" << TEXTUREMANAGER.get(p->tex) << "</texName>" << endl;
4658 out << " <useTex2>" << p->useTex2 << "</useTex2>" << endl;
4659 out << " <useEnvMap>" << p->useEnvMap << "</useEnvMap>" << endl;
4660 out << " <cull>" << p->cull << "</cull>" << endl;
4661 out << " <trans>" << p->trans << "</trans>" << endl;
4662 out << " <unlit>" << p->unlit << "</unlit>" << endl;
4663 out << " <noZWrite>" << p->noZWrite << "</noZWrite>" << endl;
4664 out << " <billboard>" << p->billboard << "</billboard>" << endl;
4665 out << " <texanim>" << p->texanim << "</texanim>" << endl;
4666 out << " <color>" << p->color << "</color>" << endl;
4667 out << " <opacity>" << p->opacity << "</opacity>" << endl;
4668 out << " <blendmode>" << p->blendmode << "</blendmode>" << endl;
4669 out << " <geoset>" << geoset->id << "</geoset>" << endl;
4670 out << " <swrap>" << p->swrap << "</swrap>" << endl;
4671 out << " <twrap>" << p->twrap << "</twrap>" << endl;
4672 out << " <ocol>" << p->ocol.x << " " << p->ocol.y << " " << p->ocol.z << " " << p->ocol.w << "</ocol>" <<
4673 endl;
4674 out << " <ecol>" << p->ecol.x << " " << p->ecol.y << " " << p->ecol.z << " " << p->ecol.w << "</ecol>" <<
4675 endl;
4676 out << " </RenderPass>" << endl;
4677 }
4678 out << " </RenderPasses>" << endl;
4679
4680 out << " <Geosets size=\"" << m.geosets.size() << "\">" << endl;
4681 for (size_t i = 0; i < m.geosets.size(); i++)
4682 {
4683 out << " <Geoset id=\"" << i << "\">" << endl;
4684 out << " <id>" << m.geosets[i]->id << "</id>" << endl;
4685 out << " <vstart>" << m.geosets[i]->vstart << "</vstart>" << endl;
4686 out << " <vcount>" << m.geosets[i]->vcount << "</vcount>" << endl;
4687 out << " <istart>" << m.geosets[i]->istart << "</istart>" << endl;
4688 out << " <icount>" << m.geosets[i]->icount << "</icount>" << endl;
4689 out << " <nSkinnedBones>" << m.geosets[i]->nSkinnedBones << "</nSkinnedBones>" << endl;
4690 out << " <StartBones>" << m.geosets[i]->StartBones << "</StartBones>" << endl;
4691 out << " <rootBone>" << m.geosets[i]->rootBone << "</rootBone>" << endl;
4692 out << " <nBones>" << m.geosets[i]->nBones << "</nBones>" << endl;
4693 out << " <BoundingBox>" << m.geosets[i]->BoundingBox[0].x << " " << m.geosets[i]->BoundingBox[0].y << " "
4694 << m.geosets[i]->BoundingBox[0].z << "</BoundingBox>" << endl;
4695 out << " <BoundingBox>" << m.geosets[i]->BoundingBox[1].x << " " << m.geosets[i]->BoundingBox[1].y << " "
4696 << m.geosets[i]->BoundingBox[1].z << "</BoundingBox>" << endl;
4697 out << " <radius>" << m.geosets[i]->radius << "</radius>" << endl;
4698 out << " </Geoset>" << endl;
4699 }
4700 out << " </Geosets>" << endl;
4701
4702 // ModelTexUnit *tex = (ModelTexUnit*)(g.getBuffer() + view->ofsTex);
4703 // out << " <TexUnits size=\"" << view->nTex << "\">" << endl;
4704 // for (size_t i=0; i<view->nTex; i++) {
4705 // out << " <TexUnit id=\"" << i << "\">" << endl;
4706 // out << " <flags>" << tex[i].flags << "</flags>" << endl;
4707 // out << " <shading>" << tex[i].shading << "</shading>" << endl;
4708 // out << " <op>" << tex[i].op << "</op>" << endl;
4709 // out << " <op2>" << tex[i].op2 << "</op2>" << endl;
4710 // out << " <colorIndex>" << tex[i].colorIndex << "</colorIndex>" << endl;
4711 // out << " <flagsIndex>" << tex[i].flagsIndex << "</flagsIndex>" << endl;
4712 // out << " <texunit>" << tex[i].texunit << "</texunit>" << endl;
4713 // out << " <mode>" << tex[i].mode << "</mode>" << endl;
4714 // out << " <textureid>" << tex[i].textureid << "</textureid>" << endl;
4715 // out << " <texunit2>" << tex[i].texunit2 << "</texunit2>" << endl;
4716 // out << " <transid>" << tex[i].transid << "</transid>" << endl;
4717 // out << " <texanimid>" << tex[i].texanimid << "</texanimid>" << endl;
4718 // out << " </TexUnit>" << endl;
4719 // }
4720 // out << " </TexUnits>" << endl;
4721
4722 out << " </Views>" << endl;
4723
4724 out << " <RenderFlags></RenderFlags>" << endl;
4725
4726 out << " <Colors size=\"" << m.colors.size() << "\">" << endl;
4727 for (uint i = 0; i < m.colors.size(); i++)
4728 {
4729 out << " <Color id=\"" << i << "\">" << endl;
4730 // AB color
4731 out << " <color>" << endl;
4732 out << m.colors[i].color;
4733 out << " </color>" << endl;
4734 // AB opacity
4735 out << " <opacity>" << endl;
4736 out << m.colors[i].opacity;
4737 out << " </opacity>" << endl;
4738 out << " </Color>" << endl;
4739 }
4740 out << " </Colors>" << endl;
4741
4742 out << " <Transparency size=\"" << m.transparency.size() << "\">" << endl;
4743 for (uint i = 0; i < m.transparency.size(); i++)
4744 {
4745 out << " <Tran id=\"" << i << "\">" << endl;
4746 // AB trans
4747 out << " <trans>" << endl;
4748 out << m.transparency[i].trans;
4749 out << " </trans>" << endl;
4750 out << " </Tran>" << endl;
4751 }
4752 out << " </Transparency>" << endl;
4753
4754 out << " <TransparencyLookup></TransparencyLookup>" << endl;
4755
4756 // ModelTextureDef *texdef = (ModelTextureDef*)(f.getBuffer() + m.header.ofsTextures);
4757 // out << " <Textures size=\"" << m.header.nTextures << "\">" << endl;
4758 // for(size_t i=0; i<m.header.nTextures; i++) {
4759 // out << " <Texture id=\"" << i << "\">" << endl;
4760 // out << " <type>" << texdef[i].type << "</type>" << endl;
4761 // out << " <flags>" << texdef[i].flags << "</flags>" << endl;
4762 // //out << " <nameLen>" << texdef[i].nameLen << "</nameLen>" << endl;
4763 // //out << " <nameOfs>" << texdef[i].nameOfs << "</nameOfs>" << endl;
4764 // if (texdef[i].type == TEXTURE_FILENAME)
4765 // out << " <name>" << f.getBuffer()+texdef[i].nameOfs << "</name>" << endl;
4766 // out << " </Texture>" << endl;
4767 // }
4768 // out << " </Textures>" << endl;
4769
4770 // out << " <TexLookups size=\"" << m.header.nTexLookup << "\">" << endl;
4771 // uint16 *texLookup = (uint16 *)(f.getBuffer() + m.header.ofsTexLookup);
4772 // for(size_t i=0; i<m.header.nTexLookup; i++) {
4773 // out << " <TexLookup id=\"" << i << "\">" << texLookup[i] << "</TexLookup>" << endl;
4774 // }
4775 // out << " </TexLookups>" << endl;
4776
4777 out << " <ReplacableTextureLookup></ReplacableTextureLookup>" << endl;
4778
4779 out << " </GeometryAndRendering>" << endl;
4780
4781 out << " <Effects>" << endl;
4782
4783 out << " <TexAnims size=\"" << m.texAnims.size() << "\">" << endl;
4784 for (uint i = 0; i < m.texAnims.size(); i++)
4785 {
4786 out << " <TexAnim id=\"" << i << "\">" << endl;
4787 // AB trans
4788 out << " <trans>" << endl;
4789 out << m.texAnims[i].trans;
4790 out << " </trans>" << endl;
4791 // AB rot
4792 out << " <rot>" << endl;
4793 out << m.texAnims[i].rot;
4794 out << " </rot>" << endl;
4795 // AB scale
4796 out << " <scale>" << endl;
4797 out << m.texAnims[i].scale;
4798 out << " </scale>" << endl;
4799 out << " </TexAnim>" << endl;
4800 }
4801 out << " </TexAnims>" << endl;
4802
4803 out << " <RibbonEmitters></RibbonEmitters>" << endl; // TODO
4804
4805 out << " <Particles size=\"" << m.header.nParticleEmitters << "\">" << endl;
4806 for (size_t i = 0; i < m.particleSystems.size(); i++)
4807 {
4808 out << " <Particle id=\"" << i << "\">" << endl;
4809 out << m.particleSystems[i];
4810 out << " </Particle>" << endl;
4811 }
4812 out << " </Particles>" << endl;
4813
4814 out << " </Effects>" << endl;
4815
4816 out << " <Miscellaneous>" << endl;
4817
4818 out << " <BoundingVolumes></BoundingVolumes>" << endl;
4819 out << " <Lights></Lights>" << endl;
4820 out << " <Cameras></Cameras>" << endl;
4821
4822 out << " <Attachments size=\"" << m.header.nAttachments << "\">" << endl;
4823 for (size_t i = 0; i < m.header.nAttachments; i++)
4824 {
4825 out << " <Attachment id=\"" << i << "\">" << endl;
4826 out << " <id>" << m.atts[i].id << "</id>" << endl;
4827 out << " <bone>" << m.atts[i].bone << "</bone>" << endl;
4828 out << " <pos>" << m.atts[i].pos.x << " " << m.atts[i].pos.y << " " << m.atts[i].pos.z << "</pos>" << endl;
4829 out << " </Attachment>" << endl;
4830 }
4831 out << " </Attachments>" << endl;
4832
4833 out << " <AttachLookups size=\"" << m.header.nAttachLookup << "\">" << endl;
4834 // int16 *attachLookup = (int16 *)(f.getBuffer() + m.header.ofsAttachLookup);
4835 // for(size_t i=0; i<m.header.nAttachLookup; i++) {
4836 // out << " <AttachLookup id=\"" << i << "\">" << attachLookup[i] << "</AttachLookup>" << endl;
4837 // }
4838 // out << " </AttachLookups>" << endl;
4839
4840 out << " <Events size=\"" << m.events.size() << "\">" << endl;
4841 for (size_t i = 0; i < m.events.size(); i++)
4842 {
4843 out << " <Event id=\"" << i << "\">" << endl;
4844 out << m.events[i];
4845 out << " </Event>" << endl;
4846 }
4847 out << " </Events>" << endl;
4848
4849 out << " </Miscellaneous>" << endl;
4850
4851 // out << " <>" << m.header. << "</>" << endl;
4852 out << " <TextureLists>" << endl;
4853 for (const auto it : m.passes)
4854 {
4855 const GLuint tex = m.getGLTexture(it->tex);
4857 out << " <TextureList id=\"" << tex << "\">" << TEXTUREMANAGER.get(tex) << "</TextureList>"
4858 << endl;
4859 }
4860 out << " </TextureLists>" << endl;
4861
4862 out << "</m2>" << endl;
4863
4864 return out;
4865}
#define GAMEDIRECTORY
Definition Game.h:9
#define GLOBALSETTINGS
#define LOG_ERROR
Definition Logger.h:11
#define LOG_INFO
Definition Logger.h:10
TextureManager TEXTUREMANAGER
#define WOWDB
Definition WoWDatabase.h:65
TextureFlags
Definition WoWModel.cpp:33
@ TEXTURE_WRAPX
Definition WoWModel.cpp:34
@ TEXTURE_WRAPY
Definition WoWModel.cpp:35
void glGetAll()
Definition WoWModel.cpp:62
#define GL_BUFFER_OFFSET(i)
Definition WoWModel.cpp:30
void glInitAll()
Definition WoWModel.cpp:93
std::ostream & operator<<(std::ostream &out, const WoWModel &m)
@ TEXTURE_MAX
Maximum number of texture slots per model.
Definition WoWModel.h:42
size_t globalTime
Global clock for global-sequence animations.
Definition animated.cpp:3
Manages animation playback for a WoWModel, supporting up to 4 queued animations, a secondary (upper-b...
Definition AnimManager.h:24
ssize_t GetSecondaryID()
Definition AnimManager.h:63
size_t GetSecondaryFrame()
Definition AnimManager.h:64
ssize_t GetMouthID()
Definition AnimManager.h:76
size_t GetSecondaryCount()
Definition AnimManager.h:66
int Tick(int time)
size_t GetFrame()
Definition AnimManager.h:95
size_t GetMouthFrame()
Definition AnimManager.h:77
void setModel(Displayable *newmodel)
EyeGlowTypes eyeGlowType
Definition CharDetails.h:91
void save(pugi::xml_node &parentNode)
void reset(WoWModel *m=nullptr)
bool autoHideGeosetsForHeadItems
Definition CharDetails.h:93
std::map< uint, uint > geosets
Definition CharDetails.h:97
std::vector< TextureCustomization > textures
Definition CharDetails.h:98
void load(const std::string &filepath)
void reset(unsigned int _layoutSizeId)
void compose(GLuint texID)
void addLayer(GameFile *file, int region, int layer, int blendMode=1)
std::string name() const
Definition Component.cpp:52
void setName(const std::string &name)
Definition Component.cpp:46
Lightweight handle to a single row in a DB2Table.
Definition DB2Table.h:27
uint32_t getUInt(const std::string &field, unsigned int arrayIndex=0) const
Definition DB2Table.cpp:17
Provides typed, field-name-based access to records in a WDC DB2 file.
Definition DB2Table.h:50
Attachment * attachment
Definition displayable.h:34
Abstract base class representing a file within the game data archive.
Definition GameFile.h:12
bool open(bool useMemoryBuffer=true)
Open the file, optionally loading into a memory buffer.
Definition GameFile.cpp:41
size_t getSize()
Total size of the file in bytes.
Definition GameFile.cpp:134
unsigned char * getBuffer() const
Pointer to the start of the internal buffer.
Definition GameFile.cpp:144
bool close()
Close the file and release the internal buffer.
Definition GameFile.cpp:74
virtual size_t read(void *dest, size_t bytes)
Read bytes from the file into dest.
Definition GameFile.cpp:5
const std::string & fullname() const
Definition GameFile.h:56
bool setChunk(std::string chunkName, bool resetToStart=true)
Switch the active read window to the named chunk.
Definition GameFile.cpp:100
int fileDataId()
Definition GameFile.h:57
bool isChunked()
True if the file has been parsed into named chunks.
Definition GameFile.h:69
bool isEof()
True if the read pointer has reached the end of the file.
Definition GameFile.cpp:24
const ItemRecord & getById(int id) const
Definition database.cpp:97
Reference-counted item stored in a Manager.
Definition manager.h:15
void setItemName(std::string name)
Definition manager.h:38
virtual void del(IDTYPE id)
Definition manager.h:62
void dump()
Definition manager.h:142
IDTYPE get(const std::string &name)
Definition manager.h:105
Extended geoset with 32-bit index start to support HD models with > 65535 indices.
Represents a single render pass (material + geometry) for an M2 model geoset.
static const uint16 INVALID_TEX
Sentinel value for an unset texture.
WoWModel * model
Owning model.
bool twrap
Texture wrapping modes (S and T).
uint16 tex
Texture index.
int16 specialTex
Material property indices.
bool billboard
Render state flags.
glm::vec4 ecol
Output and emissive colours.
int geoIndex
Geoset index this pass draws.
int sexID
0 = male, 1 = female.
Definition RaceInfos.h:14
bool barefeet
Whether the race shows bare feet by default.
Definition RaceInfos.h:17
int textureLayoutID
Texture layout ID for compositing.
Definition RaceInfos.h:15
static bool getRaceInfosForFileID(int, RaceInfos &)
Populate a RaceInfos struct from a model file ID.
int raceID
Race ID (-1 = invalid).
Definition RaceInfos.h:13
virtual GLuint add(GameFile *)
bool supportVBO
Definition video.h:99
bool useEnvMapping
Definition video.h:108
bool useMasking
Definition video.h:107
Represents an equipped item on a character model.
Definition WoWItem.h:39
Core WoW .m2 model: geometry, animation, textures, and character data.
Definition WoWModel.h:50
float alpha_
Definition WoWModel.h:159
std::vector< ModelAnimation > anims
Definition WoWModel.h:178
std::vector< uint16 > boundTris
Definition WoWModel.h:79
GLuint getGLTexture(uint16 tex) const
std::vector< GLuint > replaceTextures
Definition WoWModel.h:64
int16 attLookup[ATT_MAX]
Definition WoWModel.h:209
void calcBones(ssize_t anim, size_t time)
uint creatureGeosetDataID
Definition WoWModel.h:219
glm::vec2 * texCoords
Definition WoWModel.h:137
void animate(ssize_t anim)
size_t anim
Definition WoWModel.h:185
size_t currentAnim
Definition WoWModel.h:183
WoWModel * mergeModel(std::string name, int type=1, bool noRefresh=false)
glm::vec3 * vertices
Definition WoWModel.h:138
WoWItem * getItem(CharSlots slot)
bool animBones
Definition WoWModel.h:104
GLuint vbuf
Definition WoWModel.h:52
bool ind
Definition WoWModel.h:167
std::map< int, std::wstring > getAnimsMap()
std::vector< TextureAnim > texAnims
Definition WoWModel.h:98
void displayHeader(ModelHeader &a_header)
Definition WoWModel.cpp:249
void readAnimsFromFile(GameFile *f, std::vector< AFID > &afids, modelAnimData &data, uint32 nAnimations, uint32 ofsAnimation, uint32 nAnimationLookup, uint32 ofsAnimationLookup)
Definition WoWModel.cpp:903
std::vector< ModelColor > colors
Definition WoWModel.h:99
bool showBones
Definition WoWModel.h:153
std::vector< uint32 > rawIndices
Definition WoWModel.h:87
void save(pugi::xml_node &parentNode)
int mergedModelType
Definition WoWModel.h:117
std::vector< glm::vec3 > bounds
Definition WoWModel.h:80
std::vector< ModelRenderPass * > rawPasses
Definition WoWModel.h:88
ModelType modelType
Definition WoWModel.h:212
std::vector< int > specialTextures
Definition WoWModel.h:63
std::vector< ModelEvent > events
Definition WoWModel.h:102
void initAnimated()
Definition WoWModel.cpp:965
int16 keyBoneLookup[BONE_MAX]
Definition WoWModel.h:210
void lightsOn(GLuint lbase)
void hideAllGeosets()
WoWModel(GameFile *file, bool forceAnim=false)
Definition WoWModel.cpp:117
RaceInfos infos
Definition WoWModel.h:215
static const size_t ATT_MAX
Definition WoWModel.h:208
bool isWMO
Definition WoWModel.h:170
bool showTexture
Definition WoWModel.h:158
glm::vec3 pos_
Definition WoWModel.h:163
int getItemId(CharSlots slot)
bool animated
Definition WoWModel.h:172
std::vector< uint32 > indices
Definition WoWModel.h:139
ModelHeader header
Definition WoWModel.h:217
bool replaceParticleColors
Definition WoWModel.h:115
GLuint tbuf
Definition WoWModel.h:52
void initRaceInfos()
Definition WoWModel.cpp:842
std::vector< AFID > readAFIDSFromFile(GameFile *f)
Definition WoWModel.cpp:885
bool canSetTextureFromFile(int texnum)
void updateTextureList(GameFile *tex, int special)
void drawBones()
bool isMount
Definition WoWModel.h:171
void dumpTextureStatus()
Definition WoWModel.cpp:38
bool isWearingARobe()
bool hasParticles
Definition WoWModel.h:169
GLuint dlist
Definition WoWModel.h:56
void load(const std::string &filepath)
std::vector< Bone > bones
Definition WoWModel.h:181
bool isAnimated()
Definition WoWModel.cpp:323
std::vector< uint32 > skinFileIDs
Definition WoWModel.h:90
bool mirrored_
Definition WoWModel.h:221
std::vector< ModelVertex > rawVertices
Definition WoWModel.h:86
std::string modelname
Definition WoWModel.h:145
CharTexture tex
Definition WoWModel.h:194
std::vector< uint > replacableParticleColorIDs
Definition WoWModel.h:114
std::set< WoWModel * > mergedModels
Definition WoWModel.h:83
bool showParticles
Definition WoWModel.h:156
void setupAtt(int id)
bool forceAnim
Definition WoWModel.h:57
std::vector< ModelRenderPass * > passes
Definition WoWModel.h:148
std::vector< uint32 > globalSequences
Definition WoWModel.h:95
float rad
Definition WoWModel.h:175
std::string getNameForTex(uint16 tex)
void drawModel()
bool showWireframe
Definition WoWModel.h:155
float trans
Definition WoWModel.h:176
bool animGeometry
Definition WoWModel.h:104
static bool sortPasses(ModelRenderPass *mrp1, ModelRenderPass *mrp2)
std::vector< RibbonEmitter > ribbons
Definition WoWModel.h:97
std::vector< TXID > readTXIDSFromFile(GameFile *f)
Definition WoWModel.cpp:869
void setLOD(int index)
void setCreatureGeosetData(std::set< GeosetNum > cgd)
CharModelDetails charModelDetails
Definition WoWModel.h:213
GLuint nbuf
Definition WoWModel.h:52
bool ok
Definition WoWModel.h:166
glm::vec3 rot_
Definition WoWModel.h:164
std::vector< GLuint > textures
Definition WoWModel.h:62
void update(int dt)
bool isGeosetDisplayed(uint geosetindex)
void updateEmitters(float dt)
glm::vec3 * normals
Definition WoWModel.h:136
std::vector< int16 > animLookups
Definition WoWModel.h:179
void unmergeModel(std::string name)
float scale_
Definition WoWModel.h:160
std::vector< ModelGeosetHD * > rawGeosets
Definition WoWModel.h:89
static std::string getCGGroupName(CharGeosets cg)
void drawBoundingVolume()
bool showBounds
Definition WoWModel.h:154
void drawParticles()
void refresh()
size_t animtime
Definition WoWModel.h:185
std::vector< ModelLight > lights
Definition WoWModel.h:101
void initCommon()
Definition WoWModel.cpp:411
bool showModel
Definition WoWModel.h:157
CharDetails cd
Definition WoWModel.h:214
std::vector< particleColorSet > particleColorReplacements
Definition WoWModel.h:130
WoWModel * getMergedModel(uint fileID)
void restoreRawGeosets()
std::vector< ParticleSystem > particleSystems
Definition WoWModel.h:96
void lightsOff(GLuint lbase)
std::vector< ModelAttachment > atts
Definition WoWModel.h:207
void initStatic()
Definition WoWModel.cpp:825
void setupAtt2(int id)
std::vector< ModelGeosetHD * > geosets
Definition WoWModel.h:149
AnimManager * animManager
Definition WoWModel.h:180
std::set< GeosetNum > creatureGeosetData
Definition WoWModel.h:218
friend class ModelRenderPass
Definition WoWModel.h:223
size_t vbufsize
Definition WoWModel.h:53
void draw()
std::string lodname
Definition WoWModel.h:146
bool hasCamera
Definition WoWModel.h:168
std::vector< ModelTransparency > transparency
Definition WoWModel.h:100
void setGeosetGroupDisplay(CharGeosets group, int val)
void refreshMerging()
std::vector< ModelVertex > origVertices
Definition WoWModel.h:132
bool animcalc
Definition WoWModel.h:184
void showGeoset(uint geosetindex, bool value)
std::vector< ModelCamera > cam
Definition WoWModel.h:144
GameFile * gamefile
Definition WoWModel.h:112
Holds per-model animation metadata: index-to-id mapping, external anim files, and global sequences.
Definition animated.h:18
std::vector< uint32 > globalSequences
Global sequence durations.
Definition animated.h:22
std::map< int16, std::pair< GameFile *, GameFile * > > animfiles
Maps anim ID to (anim file, skel file) pair.
Definition animated.h:21
std::map< uint, int16 > animIndexToAnimId
Maps animation index to animation ID.
Definition animated.h:20
ItemDatabase items
Definition database.cpp:7
@ RENDERFLAGS_ZBUFFERED
@ RENDERFLAGS_BILLBOARD
@ RENDERFLAGS_UNLIT
@ RENDERFLAGS_TWOSIDED
@ MODELBONE_BILLBOARD
@ TEXTUREUNIT_STATIC
@ ANIMATION_HANDSCLOSED
std::string getAnimName(uint32_t animId)
std::vector< std::string > split(const std::string &s, char delimiter)
Split a string by a single-character delimiter.
Animation file ID entry mapping anim/sub-anim to a CASC file ID (AFID chunk).
Definition CASCChunks.h:14
uint32 fileId
CASC file data ID.
Definition CASCChunks.h:17
On-disk particle emitter definition in an M2 file.
An animation sequence entry in the M2 model (block B).
On-disk attachment point definition in an M2 file.
An attachment point on a WoW model (weapon, shoulder, helmet, etc.).
void init(ModelAttachmentDef &mad)
Initialise from an on-disk attachment definition.
WoWModel * model
The model this attachment belongs to.
On-disk bone definition (block E) in an M2 file.
AnimationBlock translation
AnimationBlock rotation
AnimationBlock scaling
An animated camera defined within an M2 model (e.g. portrait or character-info camera).
Definition ModelCamera.h:17
void initv10(GameFile *f, ModelCameraDefV10 &mcd, std::vector< uint32 > &global, std::string modelname)
Initialise from a v10 camera definition block.
void init(GameFile *f, ModelCameraDef &mcd, std::vector< uint32 > &global, std::string modelname)
Initialise from an M2 camera definition block.
AnimationBlock opacity
A submesh/geoset within a model LOD view (one material + draw call).
M2 model file header — offsets and counts for all data blocks.
uint32 ofsVertices
uint32 ofsTexReplace
uint32 ofsEvents
uint32 nTextures
uint32 nAttachments
uint32 ofsAnimationLookup
uint32 nBoundingNormals
uint32 nameLength
uint32 nViews
uint32 ofsTexLookup
uint32 nTexFlags
uint32 ofsAnimations
uint32 ofsTextures
uint32 nRibbonEmitters
uint32 nBoundingVertices
uint32 nameOfs
uint32 nBones
uint32 ofsTransparencyLookup
uint32 nCameras
uint32 nBoundingTriangles
uint32 ofsAttachments
uint32 nTransparencyLookup
uint32 ofsCameras
uint32 nCameraLookup
uint32 ofsTexAnims
uint32 ofsBoundingNormals
uint32 nTexLookup
uint32 ofsBones
uint32 ofsLights
uint32 ofsGlobalSequences
uint32 nColors
Sphere collisionSphere
uint32 ofsTransparency
uint32 ofsTexAnimLookup
uint32 nTexAnimLookup
uint32 nTexUnitLookup
uint32 ofsColors
uint32 ofsParticleEmitters
uint32 ofsCameraLookup
uint32 nAttachLookup
uint32 ofsAttachLookup
uint32 nAnimations
uint32 nVertices
uint32 nAnimationLookup
uint32 nTransparency
Sphere boundSphere
uint32 ofsTexFlags
uint32 nGlobalSequences
uint32 nKeyBoneLookup
uint32 nTexAnims
uint32 ofsBoneLookup
uint32 nTexReplace
uint32 nParticleEmitters
uint8 version[4]
uint32 GlobalModelFlags
uint32 ofsRibbonEmitters
uint32 nBoneLookup
uint32 ofsKeyBoneLookup
uint32 ofsBoundingVertices
uint32 ofsBoundingTriangles
uint32 ofsTexUnitLookup
Lod part, A texture unit (sub of material)
A vertex as stored in the M2 file (position, bone weights, normal, texcoord).
glm::vec3 pos
Lod part,.
uint32 ofsSub
uint32 ofsTris
uint32 nTex
uint32 nSub
char id[4]
uint32 ofsTex
uint32 ofsIndex
uint32 nTris
Skeleton attachment data header (SKA1 chunk).
Definition CASCChunks.h:33
uint32 ofsAttachLookup
Offset to attachment lookup table.
Definition CASCChunks.h:37
uint32 nAttachments
Number of attachments.
Definition CASCChunks.h:34
uint32 nAttachLookup
Number of attachment lookup entries.
Definition CASCChunks.h:36
uint32 ofsAttachments
Offset to attachments.
Definition CASCChunks.h:35
Skeleton bone data header (SKB1 chunk).
Definition CASCChunks.h:42
uint32 ofsBones
Offset to bones.
Definition CASCChunks.h:44
uint32 nKeyBoneLookup
Number of key bone lookup entries.
Definition CASCChunks.h:45
uint32 ofsKeyBoneLookup
Offset to key bone lookup table.
Definition CASCChunks.h:46
uint32 nBones
Number of bones.
Definition CASCChunks.h:43
Skeleton parent data chunk (SKPD).
Definition CASCChunks.h:51
uint32 parentFileId
File data ID of the parent skeleton.
Definition CASCChunks.h:53
Skeleton sequence data header (SKS1 chunk).
Definition CASCChunks.h:22
uint32 nAnimationLookup
Number of animation lookup entries.
Definition CASCChunks.h:27
uint32 nAnimations
Number of animations.
Definition CASCChunks.h:25
uint32 ofsAnimations
Offset to animations.
Definition CASCChunks.h:26
uint32 ofsGlobalSequences
Offset to global sequences.
Definition CASCChunks.h:24
uint32 nGlobalSequences
Number of global sequences.
Definition CASCChunks.h:23
uint32 ofsAnimationLookup
Offset to animation lookup table.
Definition CASCChunks.h:28
glm::vec3 max
Maximum corner of the bounding box.
glm::vec3 min
Minimum corner of the bounding box.
float radius
Bounding sphere radius.
Texture file ID entry (TXID chunk).
Definition CASCChunks.h:58
unsigned int uint
Definition types.h:36
int16_t int16
Definition types.h:33
uint16_t uint16
Definition types.h:32
uint32_t uint32
Definition types.h:34
VideoSettings video
Definition video.cpp:38
@ IT_ROBE
Definition wow_enums.h:252
CharGeosets
Character geoset group identifiers (mesh IDs for body/armour regions).
Definition wow_enums.h:26
@ CG_VINES
Definition wow_enums.h:65
@ CG_TAILS
Definition wow_enums.h:64
@ CG_TUSKS
Definition wow_enums.h:66
@ CG_HORN_DECORATION
Definition wow_enums.h:69
@ CG_HEAD_ATTACHMENT
Definition wow_enums.h:50
@ CG_TORSO
Definition wow_enums.h:48
@ CG_KNEEPADS
Definition wow_enums.h:36
@ CG_TROUSERS
Definition wow_enums.h:40
@ CG_HEADDRESS
Definition wow_enums.h:63
@ CG_MECHAGNOME_FEET
Definition wow_enums.h:57
@ CG_BONE
Definition wow_enums.h:45
@ CG_MECHAGNOME_ARMS_OR_HANDS
Definition wow_enums.h:55
@ CG_MECHAGNOME_LEGS
Definition wow_enums.h:56
@ CG_NOSES
Definition wow_enums.h:67
@ CG_BELT
Definition wow_enums.h:44
@ CG_NECKLACE
Definition wow_enums.h:62
@ CG_FACE_1
Definition wow_enums.h:28
@ CG_EARS
Definition wow_enums.h:34
@ CG_FACE
Definition wow_enums.h:58
@ CG_GEOSET2600
Definition wow_enums.h:52
@ CG_SKIN_OR_HAIR
Definition wow_enums.h:27
@ CG_GEOSET2700
Definition wow_enums.h:53
@ CG_EYEGLOW
Definition wow_enums.h:43
@ CG_TABARD
Definition wow_enums.h:39
@ CG_SLEEVES
Definition wow_enums.h:35
@ CG_FEET
Definition wow_enums.h:46
@ CG_CLOAK
Definition wow_enums.h:42
@ CG_GEOSET2100
Definition wow_enums.h:47
@ CG_GLOVES
Definition wow_enums.h:31
@ CG_DH_BLINDFOLDS
Definition wow_enums.h:51
@ CG_HAND_ATTACHMENT
Definition wow_enums.h:49
@ CG_EYES
Definition wow_enums.h:59
@ CG_FACE_3
Definition wow_enums.h:30
@ CG_FACE_2
Definition wow_enums.h:29
@ CG_PANTS
Definition wow_enums.h:38
@ CG_DH_LOINCLOTH
Definition wow_enums.h:41
@ CG_GEOSET2800
Definition wow_enums.h:54
@ CG_EARRINGS
Definition wow_enums.h:61
@ CG_BOOTS
Definition wow_enums.h:32
@ CG_EYEBROWS
Definition wow_enums.h:60
@ CG_CHEST
Definition wow_enums.h:37
@ CG_HAIR_DECORATION
Definition wow_enums.h:68
@ MT_NORMAL
Definition wow_enums.h:213
CharSlots
Character equipment slot indices.
Definition wow_enums.h:5
@ CS_BRACERS
Definition wow_enums.h:13
@ CS_QUIVER
Definition wow_enums.h:19
@ CS_HAND_LEFT
Definition wow_enums.h:16
@ CS_BELT
Definition wow_enums.h:9
@ CS_CAPE
Definition wow_enums.h:17
@ CS_SHOULDER
Definition wow_enums.h:7
@ CS_PANTS
Definition wow_enums.h:11
@ CS_TABARD
Definition wow_enums.h:18
@ CS_CHEST
Definition wow_enums.h:12
@ CS_HAND_RIGHT
Definition wow_enums.h:15
@ CS_HEAD
Definition wow_enums.h:6
@ CS_BOOTS
Definition wow_enums.h:8
@ CS_SHIRT
Definition wow_enums.h:10
@ CS_GLOVES
Definition wow_enums.h:14
@ TEXTURE_WEAPON_BLADE
Definition wow_enums.h:313
@ TEXTURE_SKIN_EXTRA
Definition wow_enums.h:318
@ TEXTURE_SKIN
Definition wow_enums.h:311
@ TEXTURE_FILENAME
Definition wow_enums.h:310
@ BONE_ROOT
Definition wow_enums.h:197
@ BONE_MAX
Definition wow_enums.h:207
@ BONE_HEAD
Definition wow_enums.h:176
@ BONE_LFINGER1
Definition wow_enums.h:184
@ BONE_RFINGER1
Definition wow_enums.h:178
@ BONE_BTH
Definition wow_enums.h:189
@ BONE_JAW
Definition wow_enums.h:177