Talk:M2: Difference between revisions

From wowdev
Jump to navigation Jump to search
 
(83 intermediate revisions by 15 users not shown)
Line 2: Line 2:
--[[User:Schlumpf|schlumpf_]] 00:53, 23 August 2008 (CEST)
--[[User:Schlumpf|schlumpf_]] 00:53, 23 August 2008 (CEST)


== On animations ==
== Deleted Blocks ==
=== old (schlumpf) ===
=== Block D ===
'''Offset Type Name Description'''
''0x000'' uint16 InterpolationType How the values are interpolated. (0=none, 1=linear, 2=hermite)
''0x002'' uint16 GlobalSequenceID If its made with a global sequence, its in here. Id is it is, -1 if not.
''0x004'' uint32 nTimestamps The number of timestamps. Is the same as the number of keys, most of the time.
''0x008'' uint32 ofsTimestamps Offset to the timestamps in the file.
''0x00C'' uint32 nKeys Number of the keys.
''0x010'' uint32 ofsKeys Values of the block.


The offsets point to a sub-structure now. Its like this:
*'''nD records of (int16, int16) starting at ofsD'''
'''Offset Type Name Description'''
''0x000'' uint32 Number It tells us where the values actually are.
''0x004'' uint32 Offset The offset to the data.


The easiest form is if you get a Number == 1 block straight as in single-value AnimationBlocks in some blocks. Particles have only such AnimationBlocks. It just gives you another offset where the data is. Its in this file at the given offset. But if you get a whole list with a lot of stupid values (such as offsets that can't be in the M2 and the .anims), you have a little problem. I don't know why, but Blizzard made some crap here. Most likely to get us confused. Or there is more information I know nothing of.
Maybe a lookup table for animations? Since the numbers happen to be in fixed positions. The first short seems to increase with the position for models with all animations (like characters), the second seems to be flags or a modifier? Or something.


What helped me so far was this "simple" conversion: Take nTimestamps / nKeys, substract 1, divide by 3. So out of 0x22 (34) you will get 11. This is the offset to the real sub-structure (if you start with the one pointed to at 0). This offset is then into one of the [[M2/WotLK/.anim|.anim]] files. I think its just "if the first file is full, we will take the next one". I am not sure about this. I have been sleepless for 2 days now and cba to look even more into these blocks.


The data at the final offset can be of any structure. It can be one float, a short, an integer but too a Vector of three floats or a quaternion which is made in four floats or shorts, depending on the occurence of the main AnimationBlock.
Contain indices into the texture animations list, or -1 meaning a static texture.




Actually I'd say that we may have gotten this structure:
'''Offset Type Name Description'''
''0x000'' uint16 InterpolationType How the values are interpolated. (0=none, 1=linear, 2=hermite)
''0x002'' uint16 GlobalSequenceID If its made with a global sequence, its in here. Id is it is, -1 if not.
''0x004'' uint32 offsetIntoList1 (This - 1) / 3 is the index into the list at offsetToTimestampsOfs.
''0x008'' uint32 offsetToTimestampsOfs Offset to the list which holds the offset to the data of the timestamps.
''0x00C'' uint32 offsetIntoList2 (This - 1) / 3 is the index into the list at offsetToKeysOfs.
''0x010'' uint32 offsetToKeysOfs Offset to the list which holds the offset to the data of the keys.


What the fuck, Blizzard?
== Render Flags ==


I think the structure is as follows (based on the description by Bartflast, below):
''Please forgive any ignorance I show here as I'm not a professional programmer. I'm just trying to help improve Wow Model Viewer, which has very few staff left and these pages have been invaluable for understanding it :I just wanted to mention something that confused me and I'm not sure if it needs correcting...''
'''Offset Type Name Description'''
''0x000'' uint16 InterpolationType How the values are interpolated. (0=none, 1=linear, 2=hermite)
  ''0x002'' uint16 GlobalSequenceID If its made with a global sequence, its in here. Id is it is, -1 if not.
''0x004'' uint32 numberOfTimestampPairs Number of timestamp count/offset pairs
''0x008'' uint32 offsetToTimestampPairs Offset to the list which holds the timestamp pairs
''0x00C'' uint32 numberOfKeyFramePairs  Number of key frame count/offset pairs
''0x010'' uint32 offsetToKeyFramePairs  Offset to the list which holds the key frame pairs


The list of timestamp pairs are, again, references to the actual lists of timestamps... double indirection at least... so yes, WTF Blizzard. I'm pretty sure that the timestamp data from each list is an integer because the data seems to be laid out in a fashion that the timestamp lists and keyframe lists are interleaved within the file. For example:
The Render Flags section of this page has two different tables for blend modes, with no mention of what the difference is. Is one out of date? The settings they recommend are different for a few modes, but they agree on others. I also tried them out in WMV and found that blend mode 1 is not working right with the settings from the tables (sections are appearing completely invisible when they shouldn't be) so maybe it's out of date in both, or there's something I'm not understanding. Perhaps it's meant to be used in conjunction with GL_ALPHA_TEST or glAlphaFunc() or something.


<pre>
Thanks for all your work here! :)
[2009-01-29 08:57:33,953] DEBUG (M2AnimationBlock.java:70) - timelines Count/Offset[7,0x1340]
[2009-01-29 08:57:33,953] DEBUG (M2AnimationBlock.java:72) -  timeline #0[13,0x66d0]
[2009-01-29 08:57:33,953] DEBUG (M2AnimationBlock.java:72) -  timeline #1[20,0x8240]
[2009-01-29 08:57:33,953] DEBUG (M2AnimationBlock.java:72) -  timeline #2[0,0x0]
[2009-01-29 08:57:33,953] DEBUG (M2AnimationBlock.java:72) -  timeline #3[18,0xa2d0]
[2009-01-29 08:57:33,953] DEBUG (M2AnimationBlock.java:72) -  timeline #4[39,0xbd30]
[2009-01-29 08:57:33,953] DEBUG (M2AnimationBlock.java:72) -  timeline #5[0,0x0]
[2009-01-29 08:57:33,953] DEBUG (M2AnimationBlock.java:72) -  timeline #6[15,0xff00]
[2009-01-29 08:57:33,953] DEBUG (M2AnimationBlock.java:74) - keyFrames Count/Offset[7,0x1380]
[2009-01-29 08:57:33,953] DEBUG (M2AnimationBlock.java:76) -  keyFrame #0[13,0x6710]
[2009-01-29 08:57:33,953] DEBUG (M2AnimationBlock.java:76) -  keyFrame #1[20,0x8290]
[2009-01-29 08:57:33,953] DEBUG (M2AnimationBlock.java:76) -  keyFrame #2[0,0x0]
[2009-01-29 08:57:33,953] DEBUG (M2AnimationBlock.java:76) -  keyFrame #3[18,0xa320]
[2009-01-29 08:57:33,953] DEBUG (M2AnimationBlock.java:76) -  keyFrame #4[39,0xbdd0]
[2009-01-29 08:57:33,953] DEBUG (M2AnimationBlock.java:76) -  keyFrame #5[0,0x0]
[2009-01-29 08:57:33,953] DEBUG (M2AnimationBlock.java:76) -  keyFrame #6[15,0xff40]
</pre>
<p>
So, in the above example, timeline #0 has 13 timeline entries in it.  If each timeline entry were 4 bytes (an int:32), the next "logical" place to put the next list would be 0x66d0 + (13 entries x 4 bytes per entry) = 0x6704.  Since it seems that they are keeping everything on an even 16 (0x10) byte boundary, that would be 0x6710.  As you can see above, 0x6710 is the exact start of keyframe #0!  For timeline #1, 0x8240 + (20 entries * 4 bytes per entry) = 0x8290 which is the start of keyframe #1!  It makes sense, at least in Blizzard's convoluted data structure.  So while the lists are interleaved, it doesn't actually help us to know that.  However, it did assist me in decoding how each list was stored.<br/><br/>


I'm not yet sure exactly what the keyFrame data is.  I "believe" that it's the data necessary to do the animation but I'm still looking at it.<br/><br/>
- Wain


Ok, after further investigation I'm now almost certain that yes, the keyFrame data is variable depending on the animation. For example, in the bone structure animation block, the translation animation block is three (3) floats (i.e. 12 bytes per entry in the keyFrame list). For rotation animation, it's four (4) shorts (i.e. 8 bytes per entry in the keyFrame list -- remember, quaternions are stored as shorts in WotLK).  I have tested several examples of this and it seems to always work out.  I'll post another log entry of an entire bone object that my test application is reading in to try and make it more obvious.  I intend on releasing the code I'm writing as well once it's further along.<br/><br/>
* the second table was created by relax by tracing directx stuff in the MoP client, the first one was later created by schlumpf based on reverse engineering the WoD beta client. The first table assumes alpha test to be the same, the second one lists them separate. there might be errors in both, tbh. feel free to fix them if you find them, or come to quakenet#modcraft to discuss them. :) --[[User:Schlumpf|Schlumpf]] ([[User talk:Schlumpf|talk]]) 16:47, 14 October 2015 (UTC)


Ok, so I've now got some code that I think correctly reads in the bones from the M2 file and loads the animation blocks.  I cannot seem to create a new page.  If someone would kindly do this for me, perhaps call it AnimationBlockJavaDebug or something, I'll post the debug.  I just don't want to pollute this page with a plethora of debug output.  Sorry.
* Aha! Thanks very much :) I'll experiment with the modes more and see what I can find.
<br/><br/>
-- Wain
I did decide to add one more partial debugging section.  For some reason, the timeline data "jumps around" a bit.  That is, I figured it would start at some value like zero and than increase at intervals.  It could be that these are time lapses (i.e. the amount of time between each key frame that occurs before moving to the next frame). Here is an example of one set of timeline data that my code retrieved (btw, this is for an animation sequence that has 666 frames at a speed of 2.5f fwiw):


<pre>
[2009-01-29 11:47:22,437] DEBUG (M2AnimationBlock.java:71) - timelines Count/Offset[7,0x1340]
[2009-01-29 11:47:22,437] DEBUG (M2AnimationBlock.java:85) -  timeline #0[13,0x66d0]
[2009-01-29 11:47:22,437] DEBUG (M2AnimationBlock.java:88) -    time #0/0[0]
[2009-01-29 11:47:22,437] DEBUG (M2AnimationBlock.java:88) -    time #0/1[0]
[2009-01-29 11:47:22,437] DEBUG (M2AnimationBlock.java:88) -    time #0/2[13]
[2009-01-29 11:47:22,437] DEBUG (M2AnimationBlock.java:88) -    time #0/3[26384]
[2009-01-29 11:47:22,437] DEBUG (M2AnimationBlock.java:88) -    time #0/4[20]
[2009-01-29 11:47:22,437] DEBUG (M2AnimationBlock.java:88) -    time #0/5[33424]
[2009-01-29 11:47:22,437] DEBUG (M2AnimationBlock.java:88) -    time #0/6[0]
[2009-01-29 11:47:22,437] DEBUG (M2AnimationBlock.java:88) -    time #0/7[0]
[2009-01-29 11:47:22,437] DEBUG (M2AnimationBlock.java:88) -    time #0/8[18]
[2009-01-29 11:47:22,437] DEBUG (M2AnimationBlock.java:88) -    time #0/9[41760]
[2009-01-29 11:47:22,437] DEBUG (M2AnimationBlock.java:88) -    time #0/10[39]
[2009-01-29 11:47:22,437] DEBUG (M2AnimationBlock.java:88) -    time #0/11[48592]
[2009-01-29 11:47:22,437] DEBUG (M2AnimationBlock.java:88) -    time #0/12[0]
</pre>


If anyone has suggestions about this, please let me know.
== Particle Emitters ==


=== new and improved (Slartibartfast) ===
I'm not yet sure what to do with the new parameters: '''MultiTexParamX''' (2 values), and '''MultiTexParam0''' and '''MultiTexParam1''' (each two pairs of x and y values).
Maybe you've already figured it out yourself, chuanhsing, but it still might be helpful as I cannot see any anymations in your latest WMV version:


Animation in WotLK is actually easy once you've understood what Blizzard has changed. They have essentially gone from one single, long timeline per model (with every single animation covering a slice of this timeline, based on their start and end times) to a multi-timeline approach where each animation has its own timeline, starting at 0 with the length as defined in the animation sequence data structure.
Below I've included values for a sample of models that use multitextured particles, along with some other parameters from the same particle emitter structure. You'll note that some of the texture tile rotation values are also unusual, as they are expected to be -1, 0 or 1.


Because of this, the basic animation block structure had to be changed. They dumped the interpolation ranges (as those aren't necessary anymore if each animation has its own timeline), and they have added another layer of references for the timestamp and value information. This means instead of the number of timestamps followed by the offset to said timestamps there's now the number of timelines followed by an offset to the timeline data. This offset leads to a structure of 8 bytes per timeline, with the first 4 bytes making up the actual number of timestamps for one specific timeline and the next 4 being an offset to the actual timestamp data. The keyframe values are stored in a similar fashion.
Only particles that have multitexturing flagged are included, as the rest always seem to have zero for these parameters.


All of this of course does not account for the new .anim files which seem to be single animations that aren't stored in the .m2 file itself - I haven't looked at those yet, so I can't give you any hints about their structure. But the changes described above will get all animations stored in the file itself to work fine, which is the vast majority of animations.


{| style="background:#FCFCFC ; color:black; text-align:left;"
!! width="60" | Blend !! width="60" | Emitter !! width="80" |Texture !! width="200" colspan = "2" style="background: #CCCCCC" | MultitexParamX !! width="400"  colspan = "4" style="background: #DDDDDD" | MultitexParam0 !! width="400"  colspan = "4" style="background: #EEEEEE" | MultitexParam1
|-
| '''Type''' || '''Type''' || '''Tile Rotat''' || '''[0]''' || '''[1]''' || '''[0].x''' || '''[0].y'''  || '''[1].x''' || '''[1].y''' || '''[0].x''' || '''[0].y''' || '''[1].x''' || '''[1].y'''
|-
| colspan="13" | ''moosemount.m2''
|-
| 7 || 2 || 7 || 0.1875 || 0.09375 || 0.0234375 || 0.0234375 || -0.0136719 || -0.0136719 || 0.00585938 || 0.00585938 || 0.00585938 || 0.00585938
|-
| 2 || 2 || 4 || 0.5 || 0.6875 || 0 || 0.138438 || 0 || 0.138438 || 0.0488281 || 0.0488281 || 0.0234375 || 0.0234375
|-
| 2 || 1 || 4 || 0.5 || 0.6875 || 0 || 0.0996094 || 0 || 0.0488281 || 0.00976562 || 0.00976562 || 0.00390625 || 0.00390625
|-
| colspan="13" | ''saber2mount.m2''
|-
| 4 || 1 || 0 || 0.78125 || 1 || 0 || 0.0996094 || 0 || 0 || 0 || 0 || 0 || 0.0996094
|-
| 4 || 1 || 0 || 0.375 || 0.28125 || 0 || 0.0996094 || 0 || 0 || 0 || 0 || 0 || 0.0996094
|-
| colspan="13" | ''ironhordeclefthoof.m2''
|-
| 7 || 1 || 1 || 0.25 || 0.4375 || 0 || 0.298828 || 0 || 0.199219 || 0.0585938 || 0.0585938 || 0.0488281 || 0.0488281
|-
| colspan="13" | ''ironhordeelekk.m2''
|-
| 7 || 1 || 1 || 0.25 || 0.4375 || 0 || 0.298828 || 0 || 0.199219 || 0.0585938 || 0.0585938 || 0.0488281 || 0.0488281
|-
| colspan="13" | ''ironhordewolf.m2''
|-
| 2 || 1 || 0 || 0.09375 || 0.1875 || 0.0292969 || 0.0292969 || -0.0292969 || 0.0292969 || 0.00390625 || 0.00390625 || 0.00390625 || 0.00390625
|-
| 7 || 1 || 1 || 0.25 || 0.4375 || 0 || 0.298828 || 0 || 0.199219 || 0.0585938 || 0.0585938 || 0.0488281 || 0.0488281
|-
| colspan="13" | ''archimonde3.m2''
|-
| 7 || 1 || 5 || 0.5 || 0.25 || 0 || 0.199219 || 0 || 0.199219 || 0 || 0.138438 || 0 || 0.138438
|-
| colspan="13" | ''voidcaller_v2.m2''
|-
| 2 || 2 || -3 || 1 || 1.375 || 0 || -0.199219 || 0 || -0.298828 || 0.0996094 || 0.0234375 || 0.138438 || 0.0488281
|-
| 2 || 2 || -1 || 0.5 || 0.6875 || 0 || 0.0488281 || 0 || 0.0742188 || 0.0234375 || 0.0234375 || 0.00390625 || 0.00390625
|-
| 7 || 2 || 2 || 1 || 0.5 || 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0
|-
| 7 || 2 || 0 || 0.5 || 0.1875 || 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0
|-
| 7 || 1 || 0 || 0.5 || 0.1875 || 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0
|-
| colspan="13" | ''lavahorse.m2''
|-
| 7 || 1 || 5 || 0.5 || 0.25 || 0 || 0.199219 || 0 || 0.199219 || 0 || 0.138438 || 0 || 0.138438
|-
| 4 || 1 || 0 || 0.25 || 0.5 || 0 || 0.00976562 || 0 || 0.0195312 || 0.00195312 || 0.00195312 || 0.00195312 || 0.00195312
|-
| colspan="13" | ''corehound2.m2''
|-
| 7 || 1 || 0 || 0.25 || 0.4375 || 0 || 0.298828 || 0 || 0.199219 || 0 || 0.0488281 || 0 || 0.0488281
|-
| 4 || 1 || 0 || 0.25 || 0.5 || 0 || 0.00976562 || 0 || 0.0195312 || 0.00195312 || 0.00195312 || 0.00195312 || 0.00195312
|-
| colspan="13" | ''dreadravenwarbirdfel.m2''
|-
| 4 || 1 || 0 || 0.25 || 0.5 || 0 || 0.00976562 || 0 || 0.0195312 || 0.00195312 || 0.00195312 || 0.00195312 || 0.00195312
|-
| 7 || 1 || 0 || 0.25 || 0.4375 || 0 || 0.298828 || 0 || 0.199219 || 0 || 0.0488281 || 0 || 0.0488281
|-
| colspan="13" | ''dreadravenwarbirdsun.m2''
|-
| 4 || 1 || 0 || 0.25 || 0.5 || 0 || 0.00976562 || 0 || 0.0195312 || 0.00195312 || 0.00195312 || 0.00195312 || 0.00195312
|-
| 7 || 1 || 0 || 0.25 || 0.4375 || 0 || 0.298828 || 0 || 0.199219 || 0 || 0.0488281 || 0 || 0.0488281
|}


Okay, now I think I've understood how those .anim-files fit into the picture. Blizzard has created them to externalize seldomly-needed animations such as dance animations from the main model file, probably to speed up the loading process by loading them on-demand when they are to be played.
-- [[User:Wain|Wain]] ([[User talk:Wain|talk]]) 14:57, 4 November 2015 (UTC)


Their structure is simple: they don't have any structure on their own. They are just a pile of binary data, which is being referenced from the animation sub-blocks (I just decided to call those sub-structures in the animation blocks that contain the actual keyframe data for a single animation "animation sub-blocks", see my last post for details on this) in the exact same way as data in the main model file is being referenced: by offsets to timestamp/keyframe data. These references just point into the .anim-file corresponding to a specific animation id if an animation is stored externally.
Note that all the above MultitexParamX values are exact fractions of 32 (0.4375 = 14/32, etc.). This seems to be a complicated way of storing info that could be just represented as a simple uint8. -- [[User:Wain|Wain]] ([[User talk:Wain|talk]]) 03:15, 4 February 2016 (UTC)
 
The names of the animation data files are composed from the following template: [model file name][animation id]-[animation sub-id].anim
 
However, there's still one thing I don't understand: how someone can decide if an animation sequence is stored internally (in the model file) or externally. Of course it's possible to check if a corresponding .anim file exists, but I don't think that this is how WoW actually does it as that would result in quite some overhead. That information is probably stored somewhere in the main model file, but I haven't found it yet...
 
* Posted by Slartibartfast at the [http://www.wowmodelviewer.org/forum/index.php?topic=3672.40 wowmodelviewer-forums].
 
== Animations in some beta-code ==
 
animated.h
void init(AnimationBlock &b, MPQFile &f, int *gs)
 
original code
<pre>
    uint32 *ptimes = (uint32*)(f.getBuffer() + b.ofsTimes);
    for (size_t i=0; i<b.nTimes; i++)
      times.push_back(ptimes[i]);
   
    // keyframes
    assert((D*)(f.getBuffer() + b.ofsKeys));
    D *keys = (D*)(f.getBuffer() + b.ofsKeys);
    switch (type) {
      case INTERPOLATION_NONE:
      case INTERPOLATION_LINEAR:
        for (size_t i=0; i<b.nKeys; i++)
          data.push_back(Conv::conv(keys[i]));
        break;
      case INTERPOLATION_HERMITE:
        for (size_t i=0; i<b.nKeys; i++) {
          data.push_back(Conv::conv(keys[i*3]));
          in.push_back(Conv::conv(keys[i*3+1]));
          out.push_back(Conv::conv(keys[i*3+2]));
        }
        break;
    }
</pre>
 
modified code
 
<pre>
if( b.nTimes == 0 )
return;
 
for(size_t j=0; j < b.nTimes; j++) {
AnimationBlockHeader* pHeadTimes = (AnimationBlockHeader*)(f.getBuffer() + b.ofsTimes + j*sizeof(AnimationBlockHeader));
uint32 *ptimes = (uint32*)(f.getBuffer() + pHeadTimes->ofsEntrys);
for (size_t i=0; i < pHeadTimes->nEntrys; i++)
times[j].push_back(ptimes[i]);
}
 
// keyframes
for(size_t j=0; j < b.nKeys; j++) {
AnimationBlockHeader* pHeadKeys = (AnimationBlockHeader*)(f.getBuffer() + b.ofsKeys + j*sizeof(AnimationBlockHeader));
 
D *keys = (D*)(f.getBuffer() + pHeadKeys->ofsEntrys);
switch (type) {
case INTERPOLATION_NONE:
case INTERPOLATION_LINEAR:
for (size_t i = 0; i < pHeadKeys->nEntrys; i++)
data[j].push_back(Conv::conv(keys[i]));
break;
case INTERPOLATION_HERMITE:
for (size_t i = 0; i < pHeadKeys->nEntrys; i++) {
data[j].push_back(Conv::conv(keys[i*3]));
in[j].push_back(Conv::conv(keys[i*3+1]));
out[j].push_back(Conv::conv(keys[i*3+2]));
}
break;
}
}
</pre>
 
modelheaders.h
 
  struct AnimationBlockHeader
  {
    uint32 nEntrys;
    uint32 ofsEntrys;
  };
 
Igor:
Yeap, it does work but not for all animations. The pHeadTimes->ofsEntrys can be 0 or an offset that points to absurd timestamps, same for keyframes. For nKeys/nTimes even by 3 or nope as well. Be honest, we don't know how to solve this puzzle ;)
 
== Deleted Blocks ==
=== Block D ===
 
*'''nD records of (int16, int16) starting at ofsD'''
 
Maybe a lookup table for animations? Since the numbers happen to be in fixed positions. The first short seems to increase with the position for models with all animations (like characters), the second seems to be flags or a modifier? Or something.


== Blend values ==


Contain indices into the texture animations list, or -1 meaning a static texture.
Those arrays were retrieved from client
char* blendModes[14] = {
    "Blend_Opaque",           //0
    "Blend_AlphaKey",          //1
    "Blend_Alpha",            //2
    "Blend_Add",              //3
    "Blend_Mod",              //4
    "Blend_Mod2x",            //5
    "Blend_ModAdd",            //6
    "Blend_InvSrcAlphaAdd",    //7
    "Blend_InvSrcAlphaOpaque", //8
    "Blend_SrcAlphaOpaque",    //9
    "Blend_NoAlphaAdd",        //10
    "Blend_ConstantAlpha",    //11
    "Blend_Screen",            //12
    "Blend_BlendAdd"          //13
}
uint64 sourceRGBBlendFactorEnum[14]  = {
    1,            //0
    1,            //1
    4,            //2
    4,            //3
    6,            //4
    6,            //5
    6,            //6
    5,            //7
    5,            //8
    4,            //9
    1,            //10
    0x0B,        //11
    7,            //12
    1            //13
}
uint64 destRGBBlendFactorEnum[14] = {
    0,          //0
    0,          //1
    5,          //2
    1,          //3
    0,          //4
    2,          //5
    1,          //6
    1,          //7
    0,          //8
    0,          //9
    1,          //10
    0x0C,        //11
    1,          //12
    5            //13
}
uint64 sourceAlphaBlendFactorEnum[14] = {
    1,            //0
    1,            //1
    1,            //2
    0,            //3
    8,            //4
    8,            //5
    8,            //6
    5,            //7
    5,            //8
    4,            //9
    0,            //10
    0x0D,        //11
    1,            //12
    1            //13
}
uint64 destAlphaBlendFactorEnum[14] = {
    0,            //0
    0,            //1
    5,            //2
    1,            //3
    0,            //4
    4,            //5
    1,            //6
    1,            //7
    0,            //8
    0,            //9
    1,            //10
    0x0E,        //11
    0,            //12
    5            //13
  }


=== Texture animations ===
I think the lower 5 bits of GxBlendStateDesc(where this comes from) is index into this array. So based on info from notes in https://wowdev.wiki/M2/WotLK#Render_flags the blend modes are mapped this way:
  0 - Blend_Opaque(0),
  1 - Blend_AlphaKey(1),
  2 - Blend_Alpha(2),
  3 - Blend_NoAlphaAdd(10),
  4 - Blend_Add(3)
  5 - Blend_Mod(4)
  6 - Blend_Mod2x(5)
  7 - Blend_BlendAdd(13)


*'''This block contains definitions for texture animations,''' for example, flowing water or lava in some models. The keyframe values are used in the texture transform matrix.
I this assumption, it's a matter of correlation between numbers and blend modes
{| class="wikitable sortable"
|-
!
! blendMode
! RenderFlag blend
! Src Color
! Dest Color
! Src Alpha
! Dest Alpha
|-
| 0
| Blend_Opaque
| 0
| 1
| 0
| 1
| 0
|-
| 1
| Blend_AlphaKey
| 1
| 1
| 0
| 1
| 0
|-
| 2
| Blend_Alpha
| 2
| 4
| 5
| 1
| 5
|-
| 3
| Blend_Add
| 4
| 4
| 1
| 0
| 1
|-
| 4
| Blend_Mod
| 5
| 6
| 0
| 8
| 0
|-
| 5
| Blend_Mod2x
| 6
| 6
| 2
| 8
| 4
|-
| 6
| Blend_ModAdd
| -1
| 6
| 1
| 8
| 1
|-
| 7
| Blend_InvSrcAlphaAdd
| -1
| 5
| 1
| 5
| 1
|-
| 8
| Blend_InvSrcAlphaOpaque
| -1
| 5
| 0
| 5
| 0
|-
| 9
| Blend_SrcAlphaOpaque
| -1
| 4
| 0
| 4
| 0
|-
| 10
| Blend_NoAlphaAdd
| 3
| 1
| 1
| 0
| 1
|-
| 11
| Blend_ConstantAlpha
| -1
| 11
| 12
| 13
| 14
|-
| 12
| Blend_Screen
| -1
| 7
| 1
| 1
| 0
|-
| 13
| Blend_BlendAdd
| 7
| 1
| 5
| 1
| 5
|}


nTexAnims records of 0x54 bytes starting at ofsTexAnims, followed by data referenced in these records.
Based on info from article my current guess for values in this table is:
'''Offset Type Description'''
<code>
0x00 AnimationBlock (float, float, float) Translation
#define GL_ZERO                            0
0x1C AnimationBlock (float, float, float ???) Rotation?
#define GL_ONE                            1
0x38 AnimationBlock (float, float, float) Scaling?
#define GL_SRC_COLOR                      2
#define GL_ONE_MINUS_SRC_COLOR            3
#define GL_SRC_ALPHA                      4
#define GL_ONE_MINUS_SRC_ALPHA            5
#define GL_DST_ALPHA                      6
#define GL_ONE_MINUS_DST_ALPHA            7
#define GL_DST_COLOR                      8
#define GL_ONE_MINUS_DST_COLOR            9
#define GL_SRC_ALPHA_SATURATE              10
#define GL_CONSTANT_COLOR                  11
#define GL_ONE_MINUS_CONSTANT_COLOR        12
#define GL_CONSTANT_ALPHA                  13
#define GL_ONE_MINUS_CONSTANT_ALPHA        14
</code>
{| class="wikitable sortable"
|-
!
! blendMode
! RenderFlag blend
! Src Color
! Dest Color
! Src Alpha
! Dest Alpha
|-
| 0
| Blend_Opaque
| 0
| GL_ONE
| GL_ZERO
| GL_ONE
| GL_ZERO
|-
| 1
| Blend_AlphaKey
| 1
| GL_ONE
| GL_ZERO
| GL_ONE
| GL_ZERO
|-
| 2
| Blend_Alpha
| 2
| GL_SRC_ALPHA
| GL_ONE_MINUS_SRC_ALPHA
| GL_ONE
| GL_ONE_MINUS_SRC_ALPHA
|-
| 3
| Blend_Add
| 4
| GL_SRC_ALPHA
| GL_ONE
| GL_ZERO
| GL_ONE
|-
| 4
| Blend_Mod
| 5
| GL_DST_ALPHA
| GL_ZERO
| GL_DST_COLOR
| GL_ZERO
|-
| 5
| Blend_Mod2x
| 6
| GL_DST_ALPHA
| GL_SRC_COLOR
| GL_DST_COLOR
| GL_SRC_ALPHA
|-
| 6
| Blend_ModAdd
| -1
| GL_DST_ALPHA
| GL_ONE
| GL_DST_COLOR
| GL_ONE
|-
| 7
| Blend_InvSrcAlphaAdd
| -1
| GL_ONE_MINUS_SRC_ALPHA
| GL_ONE
| GL_ONE_MINUS_SRC_ALPHA
| GL_ONE
|-
| 8
| Blend_InvSrcAlphaOpaque
| -1
| GL_ONE_MINUS_SRC_ALPHA
| GL_ZERO
| GL_ONE_MINUS_SRC_ALPHA
| GL_ZERO
|-
| 9
| Blend_SrcAlphaOpaque
| -1
| GL_SRC_ALPHA
| GL_ZERO
| GL_SRC_ALPHA
| GL_ZERO
|-
| 10
| Blend_NoAlphaAdd
| 3
| GL_ONE
| GL_ONE
| GL_ZERO
| GL_ONE
|-
| 11
| Blend_ConstantAlpha
| -1
| GL_CONSTANT_COLOR
| GL_ONE_MINUS_CONSTANT_COLOR
| GL_CONSTANT_ALPHA
| GL_ONE_MINUS_CONSTANT_ALPHA
|-
| 12
| Blend_Screen
| -1
| GL_ONE_MINUS_DST_ALPHA
| GL_ONE
| GL_ONE
| GL_ZERO
|-
| 13
| Blend_BlendAdd
| 7
| GL_ONE
| GL_ONE_MINUS_SRC_ALPHA
| GL_ONE
| GL_ONE_MINUS_SRC_ALPHA
|}


The three subrecords specify texture transforms. Translation seems to work, producing nice flowing lava and waterfalls.
(updated using fallenoak's findings)

Latest revision as of 20:06, 21 November 2016

Finished for Build 8820 now. Post changes here first! And please pay attention to the style when you add something.. --schlumpf_ 00:53, 23 August 2008 (CEST)

Deleted Blocks

Block D

  • nD records of (int16, int16) starting at ofsD

Maybe a lookup table for animations? Since the numbers happen to be in fixed positions. The first short seems to increase with the position for models with all animations (like characters), the second seems to be flags or a modifier? Or something.


Contain indices into the texture animations list, or -1 meaning a static texture.


Render Flags

Please forgive any ignorance I show here as I'm not a professional programmer. I'm just trying to help improve Wow Model Viewer, which has very few staff left and these pages have been invaluable for understanding it :) I just wanted to mention something that confused me and I'm not sure if it needs correcting...

The Render Flags section of this page has two different tables for blend modes, with no mention of what the difference is. Is one out of date? The settings they recommend are different for a few modes, but they agree on others. I also tried them out in WMV and found that blend mode 1 is not working right with the settings from the tables (sections are appearing completely invisible when they shouldn't be) so maybe it's out of date in both, or there's something I'm not understanding. Perhaps it's meant to be used in conjunction with GL_ALPHA_TEST or glAlphaFunc() or something.

Thanks for all your work here! :)

- Wain

  • the second table was created by relax by tracing directx stuff in the MoP client, the first one was later created by schlumpf based on reverse engineering the WoD beta client. The first table assumes alpha test to be the same, the second one lists them separate. there might be errors in both, tbh. feel free to fix them if you find them, or come to quakenet#modcraft to discuss them. :) --Schlumpf (talk) 16:47, 14 October 2015 (UTC)
  • Aha! Thanks very much :) I'll experiment with the modes more and see what I can find.

-- Wain


Particle Emitters

I'm not yet sure what to do with the new parameters: MultiTexParamX (2 values), and MultiTexParam0 and MultiTexParam1 (each two pairs of x and y values).

Below I've included values for a sample of models that use multitextured particles, along with some other parameters from the same particle emitter structure. You'll note that some of the texture tile rotation values are also unusual, as they are expected to be -1, 0 or 1.

Only particles that have multitexturing flagged are included, as the rest always seem to have zero for these parameters.


Blend Emitter Texture MultitexParamX MultitexParam0 MultitexParam1
Type Type Tile Rotat [0] [1] [0].x [0].y [1].x [1].y [0].x [0].y [1].x [1].y
moosemount.m2
7 2 7 0.1875 0.09375 0.0234375 0.0234375 -0.0136719 -0.0136719 0.00585938 0.00585938 0.00585938 0.00585938
2 2 4 0.5 0.6875 0 0.138438 0 0.138438 0.0488281 0.0488281 0.0234375 0.0234375
2 1 4 0.5 0.6875 0 0.0996094 0 0.0488281 0.00976562 0.00976562 0.00390625 0.00390625
saber2mount.m2
4 1 0 0.78125 1 0 0.0996094 0 0 0 0 0 0.0996094
4 1 0 0.375 0.28125 0 0.0996094 0 0 0 0 0 0.0996094
ironhordeclefthoof.m2
7 1 1 0.25 0.4375 0 0.298828 0 0.199219 0.0585938 0.0585938 0.0488281 0.0488281
ironhordeelekk.m2
7 1 1 0.25 0.4375 0 0.298828 0 0.199219 0.0585938 0.0585938 0.0488281 0.0488281
ironhordewolf.m2
2 1 0 0.09375 0.1875 0.0292969 0.0292969 -0.0292969 0.0292969 0.00390625 0.00390625 0.00390625 0.00390625
7 1 1 0.25 0.4375 0 0.298828 0 0.199219 0.0585938 0.0585938 0.0488281 0.0488281
archimonde3.m2
7 1 5 0.5 0.25 0 0.199219 0 0.199219 0 0.138438 0 0.138438
voidcaller_v2.m2
2 2 -3 1 1.375 0 -0.199219 0 -0.298828 0.0996094 0.0234375 0.138438 0.0488281
2 2 -1 0.5 0.6875 0 0.0488281 0 0.0742188 0.0234375 0.0234375 0.00390625 0.00390625
7 2 2 1 0.5 0 0 0 0 0 0 0 0
7 2 0 0.5 0.1875 0 0 0 0 0 0 0 0
7 1 0 0.5 0.1875 0 0 0 0 0 0 0 0
lavahorse.m2
7 1 5 0.5 0.25 0 0.199219 0 0.199219 0 0.138438 0 0.138438
4 1 0 0.25 0.5 0 0.00976562 0 0.0195312 0.00195312 0.00195312 0.00195312 0.00195312
corehound2.m2
7 1 0 0.25 0.4375 0 0.298828 0 0.199219 0 0.0488281 0 0.0488281
4 1 0 0.25 0.5 0 0.00976562 0 0.0195312 0.00195312 0.00195312 0.00195312 0.00195312
dreadravenwarbirdfel.m2
4 1 0 0.25 0.5 0 0.00976562 0 0.0195312 0.00195312 0.00195312 0.00195312 0.00195312
7 1 0 0.25 0.4375 0 0.298828 0 0.199219 0 0.0488281 0 0.0488281
dreadravenwarbirdsun.m2
4 1 0 0.25 0.5 0 0.00976562 0 0.0195312 0.00195312 0.00195312 0.00195312 0.00195312
7 1 0 0.25 0.4375 0 0.298828 0 0.199219 0 0.0488281 0 0.0488281

-- Wain (talk) 14:57, 4 November 2015 (UTC)

Note that all the above MultitexParamX values are exact fractions of 32 (0.4375 = 14/32, etc.). This seems to be a complicated way of storing info that could be just represented as a simple uint8. -- Wain (talk) 03:15, 4 February 2016 (UTC)

Blend values

Those arrays were retrieved from client

char* blendModes[14] = {
    "Blend_Opaque",            //0
    "Blend_AlphaKey",          //1
    "Blend_Alpha",             //2
    "Blend_Add",               //3
    "Blend_Mod",               //4
    "Blend_Mod2x",             //5
    "Blend_ModAdd",            //6
    "Blend_InvSrcAlphaAdd",    //7
    "Blend_InvSrcAlphaOpaque", //8
    "Blend_SrcAlphaOpaque",    //9
    "Blend_NoAlphaAdd",        //10
    "Blend_ConstantAlpha",     //11
    "Blend_Screen",            //12
    "Blend_BlendAdd"           //13
}
uint64 sourceRGBBlendFactorEnum[14]   = {
    1,            //0
    1,            //1
    4,            //2
    4,            //3
    6,            //4
    6,            //5
    6,            //6
    5,            //7
    5,            //8
    4,            //9
    1,            //10
    0x0B,         //11
    7,            //12
    1             //13
}
uint64 destRGBBlendFactorEnum[14] = {
    0,           //0
    0,           //1
    5,           //2
    1,           //3
    0,           //4
    2,           //5
    1,           //6
    1,           //7
    0,           //8
    0,           //9
    1,           //10
    0x0C,        //11
    1,           //12
    5            //13
}
uint64 sourceAlphaBlendFactorEnum[14] = {
    1,            //0
    1,            //1
    1,            //2
    0,            //3
    8,            //4
    8,            //5
    8,            //6
    5,            //7
    5,            //8
    4,            //9
    0,            //10
    0x0D,         //11
    1,            //12
    1             //13
}
uint64 destAlphaBlendFactorEnum[14] = {
    0,            //0
    0,            //1
    5,            //2
    1,            //3
    0,            //4
    4,            //5
    1,            //6
    1,            //7
    0,            //8
    0,            //9
    1,            //10
    0x0E,         //11
    0,            //12
    5             //13
 }

I think the lower 5 bits of GxBlendStateDesc(where this comes from) is index into this array. So based on info from notes in https://wowdev.wiki/M2/WotLK#Render_flags the blend modes are mapped this way:

 0 - Blend_Opaque(0),
 1 - Blend_AlphaKey(1),
 2 - Blend_Alpha(2),
 3 - Blend_NoAlphaAdd(10),
 4 - Blend_Add(3)
 5 - Blend_Mod(4)
 6 - Blend_Mod2x(5)
 7 - Blend_BlendAdd(13)

I this assumption, it's a matter of correlation between numbers and blend modes

blendMode RenderFlag blend Src Color Dest Color Src Alpha Dest Alpha
0 Blend_Opaque 0 1 0 1 0
1 Blend_AlphaKey 1 1 0 1 0
2 Blend_Alpha 2 4 5 1 5
3 Blend_Add 4 4 1 0 1
4 Blend_Mod 5 6 0 8 0
5 Blend_Mod2x 6 6 2 8 4
6 Blend_ModAdd -1 6 1 8 1
7 Blend_InvSrcAlphaAdd -1 5 1 5 1
8 Blend_InvSrcAlphaOpaque -1 5 0 5 0
9 Blend_SrcAlphaOpaque -1 4 0 4 0
10 Blend_NoAlphaAdd 3 1 1 0 1
11 Blend_ConstantAlpha -1 11 12 13 14
12 Blend_Screen -1 7 1 1 0
13 Blend_BlendAdd 7 1 5 1 5

Based on info from article my current guess for values in this table is:

  1. define GL_ZERO 0
  2. define GL_ONE 1
  3. define GL_SRC_COLOR 2
  4. define GL_ONE_MINUS_SRC_COLOR 3
  5. define GL_SRC_ALPHA 4
  6. define GL_ONE_MINUS_SRC_ALPHA 5
  7. define GL_DST_ALPHA 6
  8. define GL_ONE_MINUS_DST_ALPHA 7
  9. define GL_DST_COLOR 8
  10. define GL_ONE_MINUS_DST_COLOR 9
  11. define GL_SRC_ALPHA_SATURATE 10
  12. define GL_CONSTANT_COLOR 11
  13. define GL_ONE_MINUS_CONSTANT_COLOR 12
  14. define GL_CONSTANT_ALPHA 13
  15. define GL_ONE_MINUS_CONSTANT_ALPHA 14

blendMode RenderFlag blend Src Color Dest Color Src Alpha Dest Alpha
0 Blend_Opaque 0 GL_ONE GL_ZERO GL_ONE GL_ZERO
1 Blend_AlphaKey 1 GL_ONE GL_ZERO GL_ONE GL_ZERO
2 Blend_Alpha 2 GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA GL_ONE GL_ONE_MINUS_SRC_ALPHA
3 Blend_Add 4 GL_SRC_ALPHA GL_ONE GL_ZERO GL_ONE
4 Blend_Mod 5 GL_DST_ALPHA GL_ZERO GL_DST_COLOR GL_ZERO
5 Blend_Mod2x 6 GL_DST_ALPHA GL_SRC_COLOR GL_DST_COLOR GL_SRC_ALPHA
6 Blend_ModAdd -1 GL_DST_ALPHA GL_ONE GL_DST_COLOR GL_ONE
7 Blend_InvSrcAlphaAdd -1 GL_ONE_MINUS_SRC_ALPHA GL_ONE GL_ONE_MINUS_SRC_ALPHA GL_ONE
8 Blend_InvSrcAlphaOpaque -1 GL_ONE_MINUS_SRC_ALPHA GL_ZERO GL_ONE_MINUS_SRC_ALPHA GL_ZERO
9 Blend_SrcAlphaOpaque -1 GL_SRC_ALPHA GL_ZERO GL_SRC_ALPHA GL_ZERO
10 Blend_NoAlphaAdd 3 GL_ONE GL_ONE GL_ZERO GL_ONE
11 Blend_ConstantAlpha -1 GL_CONSTANT_COLOR GL_ONE_MINUS_CONSTANT_COLOR GL_CONSTANT_ALPHA GL_ONE_MINUS_CONSTANT_ALPHA
12 Blend_Screen -1 GL_ONE_MINUS_DST_ALPHA GL_ONE GL_ONE GL_ZERO
13 Blend_BlendAdd 7 GL_ONE GL_ONE_MINUS_SRC_ALPHA GL_ONE GL_ONE_MINUS_SRC_ALPHA

(updated using fallenoak's findings)