mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 04:26:42 +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 = 0;
 | 
						|
        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
 |