最高指挥官SCM/SCA模型动画数据结构布局源件

dumpsca.py

#**************************************************************************************************
# Copyright (c) 2006 Gas Powered Games Corp. All Rights Reserved. 
# Gas Powered Games and Supreme Commander are the exclusive trademarks of Gas Powered Games Corp.
#**************************************************************************************************

def DumpSCA(filename):
   
   import struct,string

   data = file(filename, 'rb').read()

   fileheader_fmt = '4sllflllll'
   fileheader_size = struct.calcsize(fileheader_fmt)   

   start,stop = 0,fileheader_size
   
   (magic,             \
   version,           \
   numframes,         \
   durationseconds,   \
   numbones,          \
   namesoffset,       \
   linksoffset,       \
   firstframeoffset,  \
   framesize) = struct.unpack(fileheader_fmt,data[start:stop])
   
   print "----------------"
   print " Animation Header"
   print "----------------"
   print "File Type:",magic
   print "Version:",version
   print "NumFrames:",numframes
   print "NumBones:",numbones
   print "Duration: %-10.5f" % durationseconds
   print "NamesOffset:",namesoffset
   print "LinkOffset:",linksoffset
   print "DataOffset:",firstframeoffset
   print "FrameSize:",framesize
   
   print "\n----------------"
   print " Bone Info"
   print "----------------"

   start = namesoffset
   stop = linksoffset
   rawnames = struct.unpack(str(stop-start)+'s',data[start:stop])

   bonenames = string.split(rawnames[0],'\0')[:-1]

   links_fmt = str(numbones)+'l'
   links_size = struct.calcsize(links_fmt)
   
   start = linksoffset
   stop = start + links_size
   bonelinks = struct.unpack(links_fmt,data[start:stop])

   b = 0
   for b in range(0,len(bonenames)) :
   if (bonelinks[b] == -1) :
   print  "#%d %-12s (<local root>)" % (b, bonenames[b])
   else :
   print  "#%d %-12s (child of #%d %s)" % (b, bonenames[b], bonelinks[b], bonenames[bonelinks[b]])
   
   print "\n----------------"
   print " Animation Data"
   print "----------------"

   vec3_prettyprint = "(%11.5f,%11.5f,%11.5f)"
   quat_prettyprint = "(%11.5f,%11.5f,%11.5f,%11.5f)"

   frameheader_fmt = 'fl'
   frameheader_size = struct.calcsize(frameheader_fmt)

   posrot_fmt = '3f4f'
   posrot_size = struct.calcsize(posrot_fmt)

   start,stop = firstframeoffset,firstframeoffset+posrot_size
   root_posrot = struct.unpack(posrot_fmt,data[start:stop])

   print ("Root Delta:  Pos: " + vec3_prettyprint + " Rot:  " + quat_prettyprint) % root_posrot

   for f in range (0, numframes) :
   start,stop = stop,stop+frameheader_size           
   (keytime,keyflags) = struct.unpack(frameheader_fmt,data[start:stop])
   # Don't dump all the keys, only the beginning, middle, and end...
   dumpit = (f < 2) or ((f > (numframes/2)-2) and (f < (numframes/2+1))) or f > (numframes-3) 
   if dumpit :
   print "\nFrame: %-4d Time: %-11.5f Flags: 0x%08X\n" % (f, keytime, keyflags)
   for b in range (0, numbones) :
   start,stop = stop,stop+posrot_size
   keydata = struct.unpack(posrot_fmt,data[start:stop])
   if dumpit:
   print "   %2d: %-14s " % (b,'['+bonenames[b]+']'), (" Pos: " + vec3_prettyprint + " Rot:  " + quat_prettyprint) % keydata

if __name__ == '__main__' :
   import sys
   from os import path 

   if len(sys.argv) == 2 :
   DumpSCA(sys.argv[1])
   else:
   print "usage %s <SCA filename>" % path.basename(sys.argv[0])

dumpscm.py

#**************************************************************************************************
# Copyright (c) 2006 Gas Powered Games Corp. All Rights Reserved. 
# Gas Powered Games and Supreme Commander are the exclusive trademarks of Gas Powered Games Corp.
#**************************************************************************************************

#define some pretty print formats
vec4pp = "[%11.5f,%11.5f,%11.5f,%11.5f]"                             
vec3pp = "[%11.5f,%11.5f,%11.5f]"                             
vec2pp = "[%11.5f,%11.5f]"                             

def DumpVert(v,d):
 
   print "VERT[%4d]" % v
   print ("  Pos:      " + vec3pp) % d[0:3]
   print ("  Tangent:  " + vec3pp) % d[3:6]
   print ("  Normal:   " + vec3pp) % d[6:9]
   print ("  Binormal: " + vec3pp) % d[9:12]
   print ("  UV0:      " + vec2pp) % d[12:14]
   print ("  UV1:      " + vec2pp) % d[14:16]
   print  "  Bone %3d" % d[16:17]
   
def DumpSCM(filename) :
   
   import struct,string

   data = file(filename, 'rb').read()

   start,stop = 0,struct.calcsize('4sL')
   marker,version = struct.unpack('4sL',data[start:stop])

   start,stop = stop,stop+struct.calcsize('2L')
   boneoffset,bonecount = struct.unpack ('2L',data[start:stop])

   start,stop = stop,stop+struct.calcsize('3L')
   vertoffset,extravertoffset,vertcount = struct.unpack ('3L',data[start:stop])

   start,stop = stop,stop+struct.calcsize('2L')
   indexoffset,indexcount = struct.unpack ('2L',data[start:stop])
   tricount = indexcount/3

   start,stop = stop,stop+struct.calcsize('2L')
   infooffset,infocount = struct.unpack ('2L',data[start:stop])

   print "*** HEADER ***\n"

   print 'File Type: %s' %marker
   print 'Version: %d' % version
   print 'Bone count: %d, Bone offset: %d' % (bonecount,boneoffset)
   print 'Vertex count: %d, Vertex offset: %d, ExtraVert offset: %d' % (vertcount,vertoffset,extravertoffset)
   print 'Triangle coun:t %d, Triangle offset: %d' % (tricount,indexoffset)
   print 'Info count: %d, Info offset: %d' % (infocount,infooffset)

   padding = str(32-(stop+4)%32)+'s4s'
   start = stop+struct.calcsize(padding)
   stop = boneoffset
   rawnames = struct.unpack(str(stop-start)+'s',data[start:stop])

   print "\n*** BONE NAMES ***\n"
   bonenames = string.split(rawnames[0],'\0')[:-1]
   for b in range(0,len(bonenames)):
   print "[%2d] %s" % (b,bonenames[b])

   print "\n*** BONE DATA ***\n"

   bonestruct = '16f3f4f4i'
   bonesize = struct.calcsize(bonestruct)
   start,stop = boneoffset,boneoffset+bonesize
   for b in range(0,bonecount):
   bone = struct.unpack(bonestruct,data[start:stop])
   start,stop = stop,stop+bonesize
   print "\nBONE %d : [%s]" % (b,bonenames[b])
   print ' Parent Bone'
   if (bone[24] == -1) :
   print '  -1 <root>'
   else :
   print '  %d <%s>' % (bone[24],bonenames[bone[24]])
   print ' Parent Relative Position'
   print ('  '+vec3pp) % bone[16:19]
   print ' Parent Relative Rotation'
   print ('  ' +vec4pp) % bone[19:23]
   print ' Rest Pose Inverse'
   for row in range(4):
   print ('  '+ vec4pp) % bone[row*4:row*4+4]

   print "\n*** VERTEX DATA ***\n"

   vertstruct = '3f3f3f3f2f2f4B'
   vertsize = struct.calcsize(vertstruct)
   stop = vertoffset
   if (vertcount < 7):
   for v in range(0,vertcount):
   start,stop = stop,stop+vertsize
   vert = struct.unpack(vertstruct,data[start:stop])
   DumpVert(v,vert)
   else:
   for v in range(0,3):
   start,stop = stop,stop+vertsize
   vert = struct.unpack(vertstruct,data[start:stop])
   DumpVert(v,vert)
   print "...skipping %d verts..." % (vertcount-5)
   stop = vertoffset+(vertsize*(vertcount-3))
   for v in range(vertcount-3,vertcount):
   start,stop = stop,stop+vertsize
   vert = struct.unpack(vertstruct,data[start:stop])
   DumpVert(v,vert)

   if (extravertoffset != 0) :
   print "EXTRAVERTS (first 5 only...)\n"
   extravertstruct = '2f'
   extravertsize = struct.calcsize(extravertstruct)
   start,stop = extravertoffset,extravertoffset+extravertsize
   for v in range(0,min(5,vertcount)):
   xvert = struct.unpack(extravertstruct,data[start:stop])
   start,stop = stop,stop+extravertsize
   print xvert[0:2]
   else :
   print "No EXTRAVERT data for this model\n"

   print "\n*** TRIANGLE INDICES ***\n"

   tristruct = '3h'
   trisize = struct.calcsize(tristruct)
   stop = indexoffset

   if (tricount < 7) :
   for t in range(0,tricount):
   start,stop = stop,stop+trisize
   tri = struct.unpack(tristruct,data[start:stop])
   print tri[0],
   print tri[1],
   print tri[2]
   else :
   for t in range(0,3):
   start,stop = stop,stop+trisize
   tri = struct.unpack(tristruct,data[start:stop])
   print "%4d: %s" % (t, tri)
   print "...skipping %d triangles..." % (tricount-5)
   stop = indexoffset+(trisize*(tricount-3))
   for t in range(tricount-3,tricount):
   start,stop = stop,stop+trisize
   tri = struct.unpack(tristruct,data[start:stop])
   print "%4d: %s" % (t, tri)
   
   print "\n*** INFO ***\n"

   infostruct = str(infocount)+'s'
   infosize = struct.calcsize(infostruct)
   start,stop = infooffset,infooffset+infosize
   info = struct.unpack(infostruct,data[start:stop])
   infostrings = string.split(info[0],'\0')[:-1]
   for i in infostrings:
   print " %s" % i

if __name__ == '__main__' :
   import sys
   from os import path 

   if len(sys.argv) == 2 :
   DumpSCM(sys.argv[1])
   else:
   print "usage %s <SCM filename>" % path.basename(sys.argv[0])

ScaFile_format.h

/**************************************************************************************************
Copyright (c) 2006 Gas Powered Games Corp. All Rights Reserved. 
Gas Powered Games and Supreme Commander are the exclusive trademarks of Gas Powered Games Corp.
***************************************************************************************************/

#ifndef SCAFILE_H
#define SCAFILE_H


// SScaFileHeader -- The header for .SCA files.
//
struct SScaFileHeader
{
   // The FOURCC 'ANIM'
   unsigned long mMagic;

   // The .SCA version number.
   unsigned long mVersion;

   // The number of frames in this animation.
   unsigned long mNumFrames;

   // The duration (in seconds) of this animation.  
   // The animation plays at (mNumFrames-1)/mDuration frames per second.
   float mDuration;

   // The number of bones in this animation.
   unsigned long mNumBones;

   // Offset of the bone names (SScaFileBoneNames[0])
   unsigned long mBoneNamesOffset;

   // Offset of the bone link info (SScaFileBoneLinks[0])
   unsigned long mBoneLinksOffset;

   // Offset of the actual animation data (SScaFileAnimData[0])
   unsigned long mFirstFrameOffset;

   // The number of bytes in one animation frame.
   unsigned long mFrameSize;
};

// SScaFileBoneNames -- The bone names used in this animation.
//
struct SScaFileBoneNames
{
   // Array of bone names.  There are header.mNumBones NUL terminated
   // strings concatenated together starting here.
   char mBoneNameData[1]; //(array size is mNumBones)
};

// SScaFileBoneLinks -- The parent links for the bones used in this animation.
//
struct SScaFileBoneLinks
{
   // Array of bone indices. 
   unsigned long mParentBoneIndex[]; //(array size is mNumBones)
};

// SScaFileBoneKeyframe -- The data for single bone at a single key
// frame.
//
struct SScaFileBoneKeyframe
{
   // Position relative to the parent bone.
   // Vector (x,y,z)
   float mPosition[3];

   // Rotation relative to the parent bone.
   // Quaternion (w,x,y,z)
   float mRotation[4];
};

// SScaFileKeyframe -- The data about a single key frame.
//
struct SScaFileKeyframe
{
   // The time (in seconds) of this keyframe.
   float mTime;

   // Various flags.  None defined yet.
   unsigned long mFlags;

   // Array of keyframe data for each bone.
   SScaFileBoneKeyframe mBones[1];  //(array size is mNumBones)
};

// SScaFileAnimData -- The actual animation data.
//
struct SScaFileAnimData
{
   // The total position delta between the first frame and the last frame.
   // Vector (x,y,z)
   float mPositionDelta[3];

   // The total orientation delta between the first frame and the last frame.
   // Quaternion (w,x,y,z)
   float mOrientDelta[4];

   // The per-frame data.
   SScaFileKeyframe mFrames[1];  //(array size is mNumFrames)
};


#endif SCAFILE_H

ScmFile_format.h

/**************************************************************************************************
Copyright (c) 2006 Gas Powered Games Corp. All Rights Reserved. 
Gas Powered Games and Supreme Commander are the exclusive trademarks of Gas Powered Games Corp.
***************************************************************************************************/

#ifndef SCMFILE_H
#define SCMFILE_H

// SCM VERSION 5 DATA LAYOUT

// Multi-byte data is writting in little-endian ("Intel") format 

// There are 5 required and 2 optional sections in an SCM file, each indicated by a leading FOURCC code:

// FOURCC  |  Contents
// --------+------------------------
// 'MODL'  | Header info
// 'NAME'  | List of bone name strings
// 'SKEL'  | Array of bone data
// 'VTXL'  | Array of basic vertex data
// 'TRIS'  | Array of triangle indices
// 'VEXT'  | Array of extra vertex data (OPTIONAL SECTION)
// 'INFO'  | List of null terminated information strings (OPTIONAL SECTION)

// Section offsets in the file header point to the start of the data for that section (ie, the first byte AFTER
// the section's identifying FOURCC) Padding characters are added to the end of each section to ensure that 
// the next section is 16-byte aligned. Ommitted sections are indicated by an offset of 0. 
 
// *** All offsets are relative to the start of the file ***

//
//
struct ScmHeader
{

   // The FOURCC 'MODL'
   unsigned long mMagic;

	// The .SCM version number
   unsigned long mVersion;

   // Offset to SCM_BoneData[0]
   unsigned long mBoneOffset;
   
   // Number of elements in SCM_BoneData that actually influence verts (no reference points)
   unsigned long mWeightedBoneCount;

   // Offset of basic vertex data section (SCM_VertData[0])
   unsigned long mVertexOffset;

   // Offset of extra vertex data section (SCM_VertExtraData[0]) 
   // Contains additional per-vertex information. *** Currently unused (and omitted) in SupCom 1.0 ***
   unsigned long mVertexExtraOffset;   

   // Number of elements in the SCM_VertData array
   // (and the SCM_VertExtraData array, if mVertexExtraOffset != 0)
   unsigned long mVertexCount;        

   // Offset of the triangle index section (SCM_TriangleData[0])
   unsigned long mIndexOffset;         

   // Number of elements in the SCM_TriangleData array
   unsigned long mIndexCount;          

   // Offset of information section (SCM_InfoData[0])
   unsigned long mInfoOffset;
   
   // Number of elements in the SCM_InfoData list
   unsigned long mInfoCount;           

   // Number of elements in the SCM_BoneData array (including 'reference point' bones)
   unsigned long mTotalBoneCount;      
};

//
//
struct ScmBoneData
{
	// Inverse transform of the bone relative to the local origin of the mesh
   // 4x4 Matrix with row major (i.e. D3D default ordering)
   float mRestPoseInverse[4][4];

   // Position relative to the parent bone.
   // Vector (x,y,z)
   float mPosition[3];

   // Rotation relative to the parent bone.
   // Quaternion (w,x,y,z)
   float mRotation[4];

	// Offset of the bone's name string
   unsigned long mNameOffset;

	// Index of the bone's parent in the SCM_BoneData array
   unsigned long mParentBoneIndex;

   unsigned long RESERVED_0;      
   unsigned long RESERVED_1;      
};

//
//
struct ScmVertData
{
	// Position of the vertex relative to the local origin of the mesh
   float mPosition[3];

	// 'Tangent Space' normal, tangent & binormal unit vectors
   float mNormal[3];
   float mTangent[3];
   float mBinormal[3];

	// Two sets of UV coordinates 
   float mUV0[2];
   float mUV1[2];

   // Up to 4-bone skinning can be supported using additional 
   // indices (in conjunction with bone weights in the optional VertexExtra array)
   // Skinned meshes are not used in SupCom 1.0 so only mBoneIndex[0] is expected
   // to contain a valid index.
   unsigned char mBoneIndex[4]; 
};

struct ScmTriangleData
{
   unsigned short triIndices[3];
};


#endif SCMFILE_H

以上文件来源于GPG论坛,本人估计是开发最高指挥官SCM/SCA模型动画格式导入导出插件所需要用到的数据结构布局源件.
最高指挥官模型解析器原始下载地址:
http://www.pixelheaven.net/supcom/SupCom3DFormats.rar
http://viking.gurut.org/tools/SupCom3DFormats.rar
最高指挥官模型解析器天下会备份下载地址:
SupCom3DFormats.rar (188 KB)
115网盘下载地址:
http://115.com/file/c24amasb#
最高指挥官模型解析器图片嵌入包:
使用说明:点击以下图片并保存为PNG格式,改后缀名为RAR即可得嵌入文件包,此包可解压.

最高指挥官模型与动画格式导入导出插件汇总:

说明;以上附件包包含Autodesk 3ds Max,Blender与LightWave 3D三个建模工具的SCM&SCA格式导入导出插件.