mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 21:26:41 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			500 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			500 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|   OpenMW - The completely unofficial reimplementation of Morrowind
 | |
|   Copyright (C) 2008-2010  Nicolay Korslund
 | |
|   Email: < korslund@gmail.com >
 | |
|   WWW: http://openmw.sourceforge.net/
 | |
| 
 | |
|   This file (data.h) is part of the OpenMW package.
 | |
| 
 | |
|   OpenMW is distributed as free software: you can redistribute it
 | |
|   and/or modify it under the terms of the GNU General Public License
 | |
|   version 3, as published by the Free Software Foundation.
 | |
| 
 | |
|   This program is distributed in the hope that it will be useful, but
 | |
|   WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|   General Public License for more details.
 | |
| 
 | |
|   You should have received a copy of the GNU General Public License
 | |
|   version 3 along with this program. If not, see
 | |
|   http://www.gnu.org/licenses/ .
 | |
| 
 | |
|  */
 | |
| 
 | |
| #ifndef _NIF_DATA_H_
 | |
| #define _NIF_DATA_H_
 | |
| 
 | |
| #include "controlled.hpp"
 | |
| 
 | |
| namespace Nif
 | |
| {
 | |
| 
 | |
| class NiSourceTexture : public Named
 | |
| {
 | |
| public:
 | |
| 
 | |
|   // Is this an external (references a separate texture file) or
 | |
|   // internal (data is inside the nif itself) texture?
 | |
|   bool external;
 | |
| 
 | |
|   SString filename;    // In case of external textures
 | |
|   NiPixelDataPtr data; // In case of internal textures
 | |
| 
 | |
|   /* Pixel layout
 | |
|      0 - Palettised
 | |
|      1 - High color 16
 | |
|      2 - True color 32
 | |
|      3 - Compressed
 | |
|      4 - Bumpmap
 | |
|      5 - Default */
 | |
|   int pixel;
 | |
| 
 | |
|   /* Mipmap format
 | |
|      0 - no
 | |
|      1 - yes
 | |
|      2 - default */
 | |
|   int mipmap;
 | |
| 
 | |
|   /* Alpha
 | |
|      0 - none
 | |
|      1 - binary
 | |
|      2 - smooth
 | |
|      3 - default (use material alpha, or multiply material with texture if present)
 | |
|   */
 | |
|   int alpha;
 | |
| 
 | |
|   void read(NIFFile *nif)
 | |
|   {
 | |
|     Named::read(nif);
 | |
| 
 | |
|     external = !!nif->getByte();
 | |
| 
 | |
|     if(external) filename = nif->getString();
 | |
|     else
 | |
|       {
 | |
|         nif->getByte(); // always 1
 | |
|         data.read(nif);
 | |
|       }
 | |
| 
 | |
|     pixel = nif->getInt();
 | |
|     mipmap = nif->getInt();
 | |
|     alpha = nif->getInt();
 | |
| 
 | |
|     nif->getByte(); // always 1
 | |
|   }
 | |
| };
 | |
| 
 | |
| // Common ancestor for several data classes
 | |
| class ShapeData : public Record
 | |
| {
 | |
| public:
 | |
|   FloatArray vertices, normals, colors, uvlist;
 | |
|   const Vector *center;
 | |
|   float radius;
 | |
| 
 | |
|   void read(NIFFile *nif)
 | |
|   {
 | |
|     int verts = nif->getShort();
 | |
| 
 | |
|     if(nif->getInt())
 | |
|       vertices = nif->getFloatLen(verts*3);
 | |
| 
 | |
|     if(nif->getInt())
 | |
|       normals = nif->getFloatLen(verts*3);
 | |
| 
 | |
|     center = nif->getVector();
 | |
|     radius = nif->getFloat();
 | |
| 
 | |
|     if(nif->getInt())
 | |
|       colors = nif->getFloatLen(verts*4);
 | |
| 
 | |
|     int uvs = nif->getShort();
 | |
| 
 | |
|     // Only the first 6 bits are used as a count. I think the rest are
 | |
|     // flags of some sort.
 | |
|     uvs &= 0x3f;
 | |
| 
 | |
|     if(nif->getInt())
 | |
|       uvlist = nif->getFloatLen(uvs*verts*2);
 | |
|   }
 | |
| };
 | |
| 
 | |
| class NiTriShapeData : public ShapeData
 | |
| {
 | |
| public:
 | |
|   // Triangles, three vertex indices per triangle
 | |
|   SliceArray<short> triangles;
 | |
| 
 | |
|   void read(NIFFile *nif)
 | |
|   {
 | |
|     ShapeData::read(nif);
 | |
| 
 | |
|     int tris = nif->getShort();
 | |
|     if(tris)
 | |
|       {
 | |
|         // We have three times as many vertices as triangles, so this
 | |
|         // is always equal to tris*3.
 | |
|         int cnt = nif->getInt();
 | |
|         triangles = nif->getArrayLen<short>(cnt);
 | |
|       }
 | |
| 
 | |
|     // Read the match list, which lists the vertices that are equal to
 | |
|     // vertices. We don't actually need need this for anything, so
 | |
|     // just skip it.
 | |
|     int verts = nif->getShort();
 | |
|     if(verts)
 | |
|       {
 | |
|         for(int i=0;i<verts;i++)
 | |
|           {
 | |
|             // Number of vertices matching vertex 'i'
 | |
|             short num = nif->getShort();
 | |
|             nif->skip(num*sizeof(short));
 | |
|           }
 | |
|       }
 | |
|   }
 | |
| };
 | |
| 
 | |
| class NiAutoNormalParticlesData : public ShapeData
 | |
| {
 | |
| public:
 | |
|   int activeCount;
 | |
| 
 | |
|   void read(NIFFile *nif)
 | |
|   {
 | |
|     ShapeData::read(nif);
 | |
| 
 | |
|     // Should always match the number of vertices
 | |
|     activeCount = nif->getShort();
 | |
| 
 | |
|     // Skip all the info, we don't support particles yet
 | |
|     nif->getFloat();  // Active radius ?
 | |
|     nif->getShort(); // Number of valid entries in the following arrays ?
 | |
| 
 | |
|     if(nif->getInt())
 | |
|       // Particle sizes
 | |
|       nif->getFloatLen(activeCount);
 | |
|   }
 | |
| };
 | |
| 
 | |
| class NiRotatingParticlesData : public NiAutoNormalParticlesData
 | |
| {
 | |
| public:
 | |
|   void read(NIFFile *nif)
 | |
|   {
 | |
|     NiAutoNormalParticlesData::read(nif);
 | |
| 
 | |
|     if(nif->getInt())
 | |
|       // Rotation quaternions. I THINK activeCount is correct here,
 | |
|       // but verts (vertex number) might also be correct, if there is
 | |
|       // any case where the two don't match.
 | |
|       nif->getArrayLen<Vector4>(activeCount);
 | |
|   }
 | |
| };
 | |
| 
 | |
| class NiPosData : public Record
 | |
| {
 | |
| public:
 | |
|   void read(NIFFile *nif)
 | |
|   {
 | |
|     int count = nif->getInt();
 | |
|     int type = nif->getInt();
 | |
|     if(type != 1 && type != 2)
 | |
|       nif->fail("Cannot handle NiPosData type");
 | |
| 
 | |
|     // TODO: Could make structs of these. Seems to be identical to
 | |
|     // translation in NiKeyframeData.
 | |
|     for(int i=0; i<count; i++)
 | |
|       {
 | |
|         /*float time =*/ nif->getFloat();
 | |
|         nif->getVector(); // This isn't really shared between type 1
 | |
|                           // and type 2, most likely
 | |
|         if(type == 2)
 | |
|           {
 | |
|             nif->getVector();
 | |
|             nif->getVector();
 | |
|           }
 | |
|       }
 | |
|   }
 | |
| };
 | |
| 
 | |
| class NiUVData : public Record
 | |
| {
 | |
| public:
 | |
|   void read(NIFFile *nif)
 | |
|   {
 | |
|     // TODO: This is claimed to be a "float animation key", which is
 | |
|     // also used in FloatData and KeyframeData. We could probably
 | |
|     // reuse and refactor a lot of this if we actually use it at some
 | |
|     // point.
 | |
| 
 | |
|     for(int i=0; i<2; i++)
 | |
|       {
 | |
|         int count = nif->getInt();
 | |
| 
 | |
|         if(count)
 | |
|           {
 | |
|             nif->getInt(); // always 2
 | |
|             nif->getArrayLen<Vector4>(count); // Really one time float + one vector
 | |
|           }
 | |
|       }
 | |
|     // Always 0
 | |
|     nif->getInt();
 | |
|     nif->getInt();
 | |
|   }
 | |
| };
 | |
| 
 | |
| class NiFloatData : public Record
 | |
| {
 | |
| public:
 | |
|   void read(NIFFile *nif)
 | |
|   {
 | |
|     int count = nif->getInt();
 | |
|     nif->getInt(); // always 2
 | |
|     nif->getArrayLen<Vector4>(count); // Really one time float + one vector
 | |
|   }
 | |
| };
 | |
| 
 | |
| class NiPixelData : public Record
 | |
| {
 | |
| public:
 | |
|   unsigned int rmask, gmask, bmask, amask;
 | |
|   int bpp, mips;
 | |
| 
 | |
|   void read(NIFFile *nif)
 | |
|   {
 | |
|     nif->getInt(); // always 0 or 1
 | |
| 
 | |
|     rmask = nif->getInt(); // usually 0xff
 | |
|     gmask = nif->getInt(); // usually 0xff00
 | |
|     bmask = nif->getInt(); // usually 0xff0000
 | |
|     amask = nif->getInt(); // usually 0xff000000 or zero
 | |
| 
 | |
|     bpp = nif->getInt();
 | |
| 
 | |
|     // Unknown
 | |
|     nif->skip(12);
 | |
| 
 | |
|     mips = nif->getInt();
 | |
| 
 | |
|     // Bytes per pixel, should be bpp * 8
 | |
|     /*int bytes =*/ nif->getInt();
 | |
| 
 | |
|     for(int i=0; i<mips; i++)
 | |
|       {
 | |
|         // Image size and offset in the following data field
 | |
|         /*int x =*/ nif->getInt();
 | |
|         /*int y =*/ nif->getInt();
 | |
|         /*int offset =*/ nif->getInt();
 | |
|       }
 | |
| 
 | |
|     // Skip the data
 | |
|     unsigned int dataSize = nif->getInt();
 | |
|     nif->skip(dataSize);
 | |
|   }
 | |
| };
 | |
| 
 | |
| class NiColorData : public Record
 | |
| {
 | |
| public:
 | |
|   struct ColorData
 | |
|   {
 | |
|     float time;
 | |
|     Vector4 rgba;
 | |
|   };
 | |
| 
 | |
|   void read(NIFFile *nif)
 | |
|   {
 | |
|     int count = nif->getInt();
 | |
|     nif->getInt(); // always 1
 | |
| 
 | |
|     // Skip the data
 | |
|     assert(sizeof(ColorData) == 4*5);
 | |
|     nif->skip(sizeof(ColorData) * count);
 | |
|   }
 | |
| };
 | |
| 
 | |
| class NiVisData : public Record
 | |
| {
 | |
| public:
 | |
|   void read(NIFFile *nif)
 | |
|   {
 | |
|     int count = nif->getInt();
 | |
|     /*
 | |
|       Each VisData consists of:
 | |
|         float time;
 | |
|         byte isSet;
 | |
| 
 | |
|       If you implement this, make sure you use a packed struct
 | |
|       (sizeof==5), or read each element individually.
 | |
|      */
 | |
|     nif->skip(count*5);
 | |
|   }
 | |
| };
 | |
| 
 | |
| class NiSkinInstance : public Record
 | |
| {
 | |
| public:
 | |
|   NiSkinDataPtr data;
 | |
|   NodePtr root;
 | |
|   NodeList bones;
 | |
| 
 | |
|   void read(NIFFile *nif)
 | |
|   {
 | |
|     data.read(nif);
 | |
|     root.read(nif);
 | |
|     bones.read(nif);
 | |
| 
 | |
|     if(data.empty() || root.empty())
 | |
|       nif->fail("NiSkinInstance missing root or data");
 | |
|   }
 | |
| 
 | |
|   void post(NIFFile *nif);
 | |
| };
 | |
| 
 | |
| class NiSkinData : public Record
 | |
| {
 | |
| public:
 | |
|   // This is to make sure the structs are packed, ie. that the
 | |
|   // compiler doesn't mess them up with extra alignment bytes.
 | |
| #pragma pack(push)
 | |
| #pragma pack(1)
 | |
| 
 | |
|   struct BoneTrafo
 | |
|   {
 | |
|     Matrix rotation; // Rotation offset from bone?
 | |
|     Vector trans;    // Translation
 | |
|     float scale;     // Probably scale (always 1)
 | |
|   };
 | |
| 
 | |
|   struct VertWeight
 | |
|   {
 | |
|     short vertex;
 | |
|     float weight;
 | |
|   };
 | |
| #pragma pack(pop)
 | |
| 
 | |
|   struct BoneInfo
 | |
|   {
 | |
|     const BoneTrafo *trafo;
 | |
|     const Vector4 *unknown;
 | |
|     SliceArray<VertWeight> weights;
 | |
|   };
 | |
| 
 | |
|   const BoneTrafo *trafo;
 | |
|   std::vector<BoneInfo> bones;
 | |
| 
 | |
|   void read(NIFFile *nif)
 | |
|   {
 | |
|     assert(sizeof(BoneTrafo) == 4*(9+3+1));
 | |
|     assert(sizeof(VertWeight) == 6);
 | |
| 
 | |
|     trafo = nif->getPtr<BoneTrafo>();
 | |
| 
 | |
|     int boneNum = nif->getInt();
 | |
|     nif->getInt(); // -1
 | |
| 
 | |
|     bones.resize(boneNum);
 | |
| 
 | |
|     for(int i=0;i<boneNum;i++)
 | |
|       {
 | |
|         BoneInfo &bi = bones[i];
 | |
| 
 | |
|         bi.trafo = nif->getPtr<BoneTrafo>();
 | |
|         bi.unknown = nif->getVector4();
 | |
| 
 | |
|         // Number of vertex weights
 | |
|         int count = nif->getShort();
 | |
|         bi.weights = nif->getArrayLen<VertWeight>(count);
 | |
|       }
 | |
|   }
 | |
| };
 | |
| 
 | |
| class NiMorphData : public Record
 | |
| {
 | |
| public:
 | |
|   void read(NIFFile *nif)
 | |
|   {
 | |
|     int morphCount = nif->getInt();
 | |
|     int vertCount  = nif->getInt();
 | |
|     nif->getByte();
 | |
| 
 | |
|     for(int i=0; i<morphCount; i++)
 | |
|       {
 | |
|         int magic = nif->getInt();
 | |
|         nif->getInt();
 | |
|         if(magic)
 | |
|           // Time, data, forward, backward tangents
 | |
|           nif->getFloatLen(4*magic);
 | |
| 
 | |
|         nif->getFloatLen(vertCount*3);
 | |
|       }
 | |
|   }
 | |
| };
 | |
| 
 | |
| class NiKeyframeData : public Record
 | |
| {
 | |
| public:
 | |
|   void read(NIFFile *nif)
 | |
|   {
 | |
|     // Rotations first
 | |
|     int count = nif->getInt();
 | |
|     if(count)
 | |
|       {
 | |
|         int type = nif->getInt();
 | |
| 
 | |
|         if(type == 1)
 | |
|           nif->skip(count*4*5); // time + quaternion
 | |
|         else if(type == 3)
 | |
|           nif->skip(count*4*8); // rot1 + tension+bias+continuity
 | |
|         else if(type == 4)
 | |
|           {
 | |
|             for(int j=0;j<count;j++)
 | |
|               {
 | |
|                 nif->getFloat(); // time
 | |
|                 for(int i=0; i<3; i++)
 | |
|                   {
 | |
|                     int cnt = nif->getInt();
 | |
|                     int type = nif->getInt();
 | |
|                     if(type == 1)
 | |
|                       nif->skip(cnt*4*2); // time + unknown
 | |
|                     else if(type == 2)
 | |
|                       nif->skip(cnt*4*4); // time + unknown vector
 | |
|                     else nif->fail("Unknown sub-rotation type");
 | |
|                   }
 | |
|               }
 | |
|           }
 | |
|         else nif->fail("Unknown rotation type in NiKeyframeData");
 | |
|       }
 | |
| 
 | |
|     // Then translation
 | |
|     count = nif->getInt();
 | |
|     if(count)
 | |
|       {
 | |
|         int type = nif->getInt();
 | |
| 
 | |
|         if(type == 1) nif->getFloatLen(count*4); // time + translation
 | |
|         else if(type == 2)
 | |
|           nif->getFloatLen(count*10); // trans1 + forward + backward
 | |
|         else if(type == 3)
 | |
|           nif->getFloatLen(count*7); // trans1 + tension,bias,continuity
 | |
|         else nif->fail("Unknown translation type");
 | |
|       }
 | |
| 
 | |
|     // Finally, scalings
 | |
|     count = nif->getInt();
 | |
|     if(count)
 | |
|       {
 | |
|         int type = nif->getInt();
 | |
| 
 | |
|         int size;
 | |
|         if(type == 1) size = 2; // time+scale
 | |
|         else if(type == 2) size = 4; // 1 + forward + backward (floats)
 | |
|         else if(type == 3) size = 5; // 1 + tbc
 | |
|         else nif->fail("Unknown scaling type");
 | |
|         nif->getFloatLen(count*size);
 | |
|       }
 | |
|   }
 | |
| };
 | |
| 
 | |
| } // Namespace
 | |
| #endif
 |