Talk:M2: Difference between revisions

From wowdev
Jump to navigation Jump to search
No edit summary
No edit summary
Line 87: Line 87:


<pre>
<pre>
    AnimationBlockHeader* pHeadTimes = (AnimationBlockHeader*)(f.getBuffer() + b.ofsTimes);
if( b.nTimes == 0 )
   
return;
    uint32 *ptimes = (uint32*)(f.getBuffer() + pHeadTimes->ofsEntrys);
 
    for (size_t i=0; i < pHeadTimes->nEntrys; i++)
for(size_t j=0; j < b.nTimes; j++) {
      times.push_back(ptimes[i]);
AnimationBlockHeader* pHeadTimes = (AnimationBlockHeader*)(f.getBuffer() + b.ofsTimes + j*sizeof(AnimationBlockHeader));
   
    // keyframes
uint32 *ptimes = (uint32*)(f.getBuffer() + pHeadTimes->ofsEntrys);
    AnimationBlockHeader* pHeadKeys = (AnimationBlockHeader*)(f.getBuffer() + b.ofsKeys);
for (size_t i=0; i < pHeadTimes->nEntrys; i++)
    D *keys = (D*)(f.getBuffer() + pHeadKeys->ofsEntrys);
times[j].push_back(ptimes[i]);
    switch (type) {
}
      case INTERPOLATION_NONE:
 
      case INTERPOLATION_LINEAR:
// keyframes
        for (size_t i = 0; i < pHeadKeys->nEntrys; i++)  
for(size_t j=0; j < b.nKeys; j++) {
          data.push_back(Conv::conv(keys[i]));
AnimationBlockHeader* pHeadKeys = (AnimationBlockHeader*)(f.getBuffer() + b.ofsKeys + j*sizeof(AnimationBlockHeader));
        break;
 
      case INTERPOLATION_HERMITE:
D *keys = (D*)(f.getBuffer() + pHeadKeys->ofsEntrys);
        for (size_t i = 0; i < pHeadKeys->nEntrys; i++) {
switch (type) {
          data.push_back(Conv::conv(keys[i*3]));
case INTERPOLATION_NONE:
          in.push_back(Conv::conv(keys[i*3+1]));
case INTERPOLATION_LINEAR:
          out.push_back(Conv::conv(keys[i*3+2]));
for (size_t i = 0; i < pHeadKeys->nEntrys; i++)  
        }
data[j].push_back(Conv::conv(keys[i]));
        break;
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>
</pre>



Revision as of 05:14, 14 October 2008

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)

On animations

old (schlumpf)

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:

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.

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 .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.


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?

new and improved (Slartibartfast)

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.

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.

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.


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.

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.

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...

Animations in some beta-code

animated.h void init(AnimationBlock &b, MPQFile &f, int *gs)

original code

    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;
    }

modified code

		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;
			}
		}

modelheaders.h

 struct AnimationBlockHeader
 {
   uint32 nEntrys;
   uint32 ofsEntrys;
 };

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.

Texture animations

  • 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.

nTexAnims records of 0x54 bytes starting at ofsTexAnims, followed by data referenced in these records.

Offset 	Type 						Description
0x00 	AnimationBlock (float, float, float) 		Translation
0x1C 	AnimationBlock (float, float, float ???) 	Rotation?
0x38 	AnimationBlock (float, float, float) 		Scaling?

The three subrecords specify texture transforms. Translation seems to work, producing nice flowing lava and waterfalls.