Imported Upstream version 0.26.0

This commit is contained in:
Bret Curtis 2013-10-17 16:37:22 +02:00
commit 9a2b6c69b6
1398 changed files with 212217 additions and 0 deletions

View file

@ -0,0 +1,151 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.sourceforge.net/
This file (controlled.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 OPENMW_COMPONENTS_NIF_CONTROLLED_HPP
#define OPENMW_COMPONENTS_NIF_CONTROLLED_HPP
#include "extra.hpp"
#include "controller.hpp"
namespace Nif
{
/// Anything that has a controller
class Controlled : public Extra
{
public:
ControllerPtr controller;
void read(NIFStream *nif)
{
Extra::read(nif);
controller.read(nif);
}
void post(NIFFile *nif)
{
Extra::post(nif);
controller.post(nif);
}
};
/// Has name, extra-data and controller
class Named : public Controlled
{
public:
std::string name;
void read(NIFStream *nif)
{
name = nif->getString();
Controlled::read(nif);
}
};
typedef Named NiSequenceStreamHelper;
class NiParticleGrowFade : public Controlled
{
public:
float growTime;
float fadeTime;
void read(NIFStream *nif)
{
Controlled::read(nif);
growTime = nif->getFloat();
fadeTime = nif->getFloat();
}
};
class NiParticleColorModifier : public Controlled
{
public:
NiColorDataPtr data;
void read(NIFStream *nif)
{
Controlled::read(nif);
data.read(nif);
}
void post(NIFFile *nif)
{
Controlled::post(nif);
data.post(nif);
}
};
class NiGravity : public Controlled
{
public:
float mForce;
/* 0 - Wind (fixed direction)
* 1 - Point (fixed origin)
*/
int mType;
Ogre::Vector3 mPosition;
Ogre::Vector3 mDirection;
void read(NIFStream *nif)
{
Controlled::read(nif);
/*unknown*/nif->getFloat();
mForce = nif->getFloat();
mType = nif->getUInt();
mPosition = nif->getVector3();
mDirection = nif->getVector3();
}
};
// NiPinaColada
class NiPlanarCollider : public Controlled
{
public:
void read(NIFStream *nif)
{
Controlled::read(nif);
// (I think) 4 floats + 4 vectors
nif->skip(4*16);
}
};
class NiParticleRotation : public Controlled
{
public:
void read(NIFStream *nif)
{
Controlled::read(nif);
/*
byte (0 or 1)
float (1)
float*3
*/
nif->skip(17);
}
};
} // Namespace
#endif

View file

@ -0,0 +1,330 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.sourceforge.net/
This file (controller.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 OPENMW_COMPONENTS_NIF_CONTROLLER_HPP
#define OPENMW_COMPONENTS_NIF_CONTROLLER_HPP
#include "record.hpp"
#include "niffile.hpp"
#include "recordptr.hpp"
namespace Nif
{
class Controller : public Record
{
public:
ControllerPtr next;
int flags;
float frequency, phase;
float timeStart, timeStop;
ControlledPtr target;
void read(NIFStream *nif)
{
next.read(nif);
flags = nif->getUShort();
frequency = nif->getFloat();
phase = nif->getFloat();
timeStart = nif->getFloat();
timeStop = nif->getFloat();
target.read(nif);
}
void post(NIFFile *nif)
{
Record::post(nif);
next.post(nif);
target.post(nif);
}
};
class NiParticleSystemController : public Controller
{
public:
struct Particle {
Ogre::Vector3 velocity;
float lifetime;
float lifespan;
float timestamp;
int vertex;
};
float velocity;
float velocityRandom;
float verticalDir; // 0=up, pi/2=horizontal, pi=down
float verticalAngle;
float horizontalDir;
float horizontalAngle;
float size;
float startTime;
float stopTime;
float emitRate;
float lifetime;
float lifetimeRandom;
int emitFlags; // Bit 0: Emit Rate toggle bit (0 = auto adjust, 1 = use Emit Rate value)
Ogre::Vector3 offsetRandom;
NodePtr emitter;
int numParticles;
int activeCount;
std::vector<Particle> particles;
ExtraPtr extra;
void read(NIFStream *nif)
{
Controller::read(nif);
velocity = nif->getFloat();
velocityRandom = nif->getFloat();
verticalDir = nif->getFloat();
verticalAngle = nif->getFloat();
horizontalDir = nif->getFloat();
horizontalAngle = nif->getFloat();
/*normal?*/ nif->getVector3();
/*color?*/ nif->getVector4();
size = nif->getFloat();
startTime = nif->getFloat();
stopTime = nif->getFloat();
nif->getChar();
emitRate = nif->getFloat();
lifetime = nif->getFloat();
lifetimeRandom = nif->getFloat();
emitFlags = nif->getUShort();
offsetRandom = nif->getVector3();
emitter.read(nif);
/* Unknown Short, 0?
* Unknown Float, 1.0?
* Unknown Int, 1?
* Unknown Int, 0?
* Unknown Short, 0?
*/
nif->skip(16);
numParticles = nif->getUShort();
activeCount = nif->getUShort();
particles.resize(numParticles);
for(size_t i = 0;i < particles.size();i++)
{
particles[i].velocity = nif->getVector3();
nif->getVector3(); /* unknown */
particles[i].lifetime = nif->getFloat();
particles[i].lifespan = nif->getFloat();
particles[i].timestamp = nif->getFloat();
nif->getUShort(); /* unknown */
particles[i].vertex = nif->getUShort();
}
nif->getUInt(); /* -1? */
extra.read(nif);
nif->getUInt(); /* -1? */
nif->getChar();
}
void post(NIFFile *nif)
{
Controller::post(nif);
emitter.post(nif);
extra.post(nif);
}
};
typedef NiParticleSystemController NiBSPArrayController;
class NiMaterialColorController : public Controller
{
public:
NiPosDataPtr data;
void read(NIFStream *nif)
{
Controller::read(nif);
data.read(nif);
}
void post(NIFFile *nif)
{
Controller::post(nif);
data.post(nif);
}
};
class NiPathController : public Controller
{
public:
NiPosDataPtr posData;
NiFloatDataPtr floatData;
void read(NIFStream *nif)
{
Controller::read(nif);
/*
int = 1
2xfloat
short = 0 or 1
*/
nif->skip(14);
posData.read(nif);
floatData.read(nif);
}
void post(NIFFile *nif)
{
Controller::post(nif);
posData.post(nif);
floatData.post(nif);
}
};
class NiUVController : public Controller
{
public:
NiUVDataPtr data;
void read(NIFStream *nif)
{
Controller::read(nif);
nif->getUShort(); // always 0
data.read(nif);
}
void post(NIFFile *nif)
{
Controller::post(nif);
data.post(nif);
}
};
class NiKeyframeController : public Controller
{
public:
NiKeyframeDataPtr data;
void read(NIFStream *nif)
{
Controller::read(nif);
data.read(nif);
}
void post(NIFFile *nif)
{
Controller::post(nif);
data.post(nif);
}
};
class NiAlphaController : public Controller
{
public:
NiFloatDataPtr data;
void read(NIFStream *nif)
{
Controller::read(nif);
data.read(nif);
}
void post(NIFFile *nif)
{
Controller::post(nif);
data.post(nif);
}
};
class NiGeomMorpherController : public Controller
{
public:
NiMorphDataPtr data;
void read(NIFStream *nif)
{
Controller::read(nif);
data.read(nif);
nif->getChar(); // always 0
}
void post(NIFFile *nif)
{
Controller::post(nif);
data.post(nif);
}
};
class NiVisController : public Controller
{
public:
NiVisDataPtr data;
void read(NIFStream *nif)
{
Controller::read(nif);
data.read(nif);
}
void post(NIFFile *nif)
{
Controller::post(nif);
data.post(nif);
}
};
class NiFlipController : public Controller
{
public:
int mTexSlot;
float mDelta; // Time between two flips. delta = (start_time - stop_time) / num_sources
NiSourceTextureList mSources;
void read(NIFStream *nif)
{
Controller::read(nif);
mTexSlot = nif->getUInt();
/*unknown=*/nif->getUInt();/*0?*/
mDelta = nif->getFloat();
mSources.read(nif);
}
void post(NIFFile *nif)
{
Controller::post(nif);
mSources.post(nif);
}
};
} // Namespace
#endif

428
components/nif/data.hpp Normal file
View file

@ -0,0 +1,428 @@
/*
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 OPENMW_COMPONENTS_NIF_DATA_HPP
#define OPENMW_COMPONENTS_NIF_DATA_HPP
#include "controlled.hpp"
#include <OgreQuaternion.h>
#include <OgreVector3.h>
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;
std::string 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(NIFStream *nif)
{
Named::read(nif);
external = !!nif->getChar();
if(external)
filename = nif->getString();
else
{
nif->getChar(); // always 1
data.read(nif);
}
pixel = nif->getInt();
mipmap = nif->getInt();
alpha = nif->getInt();
nif->getChar(); // always 1
}
void post(NIFFile *nif)
{
Named::post(nif);
data.post(nif);
}
};
// Common ancestor for several data classes
class ShapeData : public Record
{
public:
std::vector<Ogre::Vector3> vertices, normals;
std::vector<Ogre::Vector4> colors;
std::vector< std::vector<Ogre::Vector2> > uvlist;
Ogre::Vector3 center;
float radius;
void read(NIFStream *nif)
{
int verts = nif->getUShort();
if(nif->getInt())
nif->getVector3s(vertices, verts);
if(nif->getInt())
nif->getVector3s(normals, verts);
center = nif->getVector3();
radius = nif->getFloat();
if(nif->getInt())
nif->getVector4s(colors, verts);
// Only the first 6 bits are used as a count. I think the rest are
// flags of some sort.
int uvs = nif->getUShort();
uvs &= 0x3f;
if(nif->getInt())
{
uvlist.resize(uvs);
for(int i = 0;i < uvs;i++)
nif->getVector2s(uvlist[i], verts);
}
}
};
class NiTriShapeData : public ShapeData
{
public:
// Triangles, three vertex indices per triangle
std::vector<short> triangles;
void read(NIFStream *nif)
{
ShapeData::read(nif);
/*int tris =*/ nif->getUShort();
// We have three times as many vertices as triangles, so this
// is always equal to tris*3.
int cnt = nif->getInt();
nif->getShorts(triangles, 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->getUShort();
for(int i=0;i < verts;i++)
{
// Number of vertices matching vertex 'i'
int num = nif->getUShort();
nif->skip(num * sizeof(short));
}
}
};
class NiAutoNormalParticlesData : public ShapeData
{
public:
int numParticles;
float particleRadius;
int activeCount;
std::vector<float> sizes;
void read(NIFStream *nif)
{
ShapeData::read(nif);
// Should always match the number of vertices
numParticles = nif->getUShort();
particleRadius = nif->getFloat();
activeCount = nif->getUShort();
if(nif->getInt())
{
// Particle sizes
nif->getFloats(sizes, vertices.size());
}
}
};
class NiRotatingParticlesData : public NiAutoNormalParticlesData
{
public:
std::vector<Ogre::Quaternion> rotations;
void read(NIFStream *nif)
{
NiAutoNormalParticlesData::read(nif);
if(nif->getInt())
{
// Rotation quaternions.
nif->getQuaternions(rotations, vertices.size());
}
}
};
class NiPosData : public Record
{
public:
Vector3KeyList mKeyList;
void read(NIFStream *nif)
{
mKeyList.read(nif);
}
};
class NiUVData : public Record
{
public:
FloatKeyList mKeyList[4];
void read(NIFStream *nif)
{
for(int i = 0;i < 4;i++)
mKeyList[i].read(nif);
}
};
class NiFloatData : public Record
{
public:
FloatKeyList mKeyList;
void read(NIFStream *nif)
{
mKeyList.read(nif);
}
};
class NiPixelData : public Record
{
public:
unsigned int rmask, gmask, bmask, amask;
int bpp, mips;
void read(NIFStream *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:
Vector4KeyList mKeyList;
void read(NIFStream *nif)
{
mKeyList.read(nif);
}
};
class NiVisData : public Record
{
public:
struct VisData {
float time;
char isSet;
};
std::vector<VisData> mVis;
void read(NIFStream *nif)
{
int count = nif->getInt();
mVis.resize(count);
for(size_t i = 0;i < mVis.size();i++)
{
mVis[i].time = nif->getFloat();
mVis[i].isSet = nif->getChar();
}
}
};
class NiSkinInstance : public Record
{
public:
NiSkinDataPtr data;
NodePtr root;
NodeList bones;
void read(NIFStream *nif)
{
data.read(nif);
root.read(nif);
bones.read(nif);
}
void post(NIFFile *nif);
};
class NiSkinData : public Record
{
public:
struct BoneTrafo
{
Ogre::Matrix3 rotation; // Rotation offset from bone?
Ogre::Vector3 trans; // Translation
float scale; // Probably scale (always 1)
};
struct VertWeight
{
short vertex;
float weight;
};
struct BoneInfo
{
BoneTrafo trafo;
Ogre::Vector4 unknown;
std::vector<VertWeight> weights;
};
BoneTrafo trafo;
std::vector<BoneInfo> bones;
void read(NIFStream *nif)
{
trafo.rotation = nif->getMatrix3();
trafo.trans = nif->getVector3();
trafo.scale = nif->getFloat();
int boneNum = nif->getInt();
nif->getInt(); // -1
bones.resize(boneNum);
for(int i=0;i<boneNum;i++)
{
BoneInfo &bi = bones[i];
bi.trafo.rotation = nif->getMatrix3();
bi.trafo.trans = nif->getVector3();
bi.trafo.scale = nif->getFloat();
bi.unknown = nif->getVector4();
// Number of vertex weights
bi.weights.resize(nif->getUShort());
for(size_t j = 0;j < bi.weights.size();j++)
{
bi.weights[j].vertex = nif->getUShort();
bi.weights[j].weight = nif->getFloat();
}
}
}
};
struct NiMorphData : public Record
{
struct MorphData {
FloatKeyList mData;
std::vector<Ogre::Vector3> mVertices;
};
std::vector<MorphData> mMorphs;
void read(NIFStream *nif)
{
int morphCount = nif->getInt();
int vertCount = nif->getInt();
/*relative targets?*/nif->getChar();
mMorphs.resize(morphCount);
for(int i = 0;i < morphCount;i++)
{
mMorphs[i].mData.read(nif, true);
nif->getVector3s(mMorphs[i].mVertices, vertCount);
}
}
};
struct NiKeyframeData : public Record
{
QuaternionKeyList mRotations;
Vector3KeyList mTranslations;
FloatKeyList mScales;
void read(NIFStream *nif)
{
mRotations.read(nif);
mTranslations.read(nif);
mScales.read(nif);
}
};
} // Namespace
#endif

105
components/nif/effect.hpp Normal file
View file

@ -0,0 +1,105 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.sourceforge.net/
This file (effect.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 OPENMW_COMPONENTS_NIF_EFFECT_HPP
#define OPENMW_COMPONENTS_NIF_EFFECT_HPP
#include "node.hpp"
namespace Nif
{
typedef Node Effect;
// Used for NiAmbientLight and NiDirectionalLight. Might also work for
// NiPointLight and NiSpotLight?
struct NiLight : Effect
{
struct SLight
{
float dimmer;
Ogre::Vector3 ambient;
Ogre::Vector3 diffuse;
Ogre::Vector3 specular;
void read(NIFStream *nif)
{
dimmer = nif->getFloat();
ambient = nif->getVector3();
diffuse = nif->getVector3();
specular = nif->getVector3();
}
};
SLight light;
void read(NIFStream *nif)
{
Effect::read(nif);
nif->getInt(); // 1
nif->getInt(); // 1?
light.read(nif);
}
};
struct NiTextureEffect : Effect
{
NiSourceTexturePtr texture;
void read(NIFStream *nif)
{
Effect::read(nif);
int tmp = nif->getInt();
if(tmp) nif->getInt(); // always 1?
/*
3 x Vector4 = [1,0,0,0]
int = 2
int = 0 or 3
int = 2
int = 2
*/
nif->skip(16*4);
texture.read(nif);
/*
byte = 0
vector4 = [1,0,0,0]
short = 0
short = -75
short = 0
*/
nif->skip(23);
}
void post(NIFFile *nif)
{
Effect::post(nif);
texture.post(nif);
}
};
} // Namespace
#endif

108
components/nif/extra.hpp Normal file
View file

@ -0,0 +1,108 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.sourceforge.net/
This file (extra.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 OPENMW_COMPONENTS_NIF_EXTRA_HPP
#define OPENMW_COMPONENTS_NIF_EXTRA_HPP
#include "record.hpp"
#include "niffile.hpp"
#include "recordptr.hpp"
namespace Nif
{
/** A record that can have extra data. The extra data objects
themselves decend from the Extra class, and all the extra data
connected to an object form a linked list
*/
class Extra : public Record
{
public:
ExtraPtr extra;
void read(NIFStream *nif) { extra.read(nif); }
void post(NIFFile *nif) { extra.post(nif); }
};
class NiVertWeightsExtraData : public Extra
{
public:
void read(NIFStream *nif)
{
Extra::read(nif);
// We should have s*4+2 == i, for some reason. Might simply be the
// size of the rest of the record, unhelpful as that may be.
/*int i =*/ nif->getInt();
int s = nif->getUShort();
nif->skip(s * sizeof(float)); // vertex weights I guess
}
};
class NiTextKeyExtraData : public Extra
{
public:
struct TextKey
{
float time;
std::string text;
};
std::vector<TextKey> list;
void read(NIFStream *nif)
{
Extra::read(nif);
nif->getInt(); // 0
int keynum = nif->getInt();
list.resize(keynum);
for(int i=0; i<keynum; i++)
{
list[i].time = nif->getFloat();
list[i].text = nif->getString();
}
}
};
class NiStringExtraData : public Extra
{
public:
/* Two known meanings:
"MRK" - marker, only visible in the editor, not rendered in-game
"NCO" - no collision
*/
std::string string;
void read(NIFStream *nif)
{
Extra::read(nif);
nif->getInt(); // size of string + 4. Really useful...
string = nif->getString();
}
};
} // Namespace
#endif

440
components/nif/niffile.cpp Normal file
View file

@ -0,0 +1,440 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.sourceforge.net/
This file (nif_file.cpp) 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/ .
*/
#include "niffile.hpp"
#include "record.hpp"
#include "components/misc/stringops.hpp"
#include "extra.hpp"
#include "controlled.hpp"
#include "node.hpp"
#include "property.hpp"
#include "data.hpp"
#include "effect.hpp"
#include "controller.hpp"
#include <iostream>
//TODO: when threading is needed, enable these
//#include <boost/mutex.hpp>
#include <boost/thread/locks.hpp>
namespace Nif
{
class NIFFile::LoadedCache
{
//TODO: enable this to make cache thread safe...
//typedef boost::mutex mutex;
struct mutex
{
void lock () {};
void unlock () {}
};
typedef boost::lock_guard <mutex> lock_guard;
typedef std::map < std::string, boost::weak_ptr <NIFFile> > loaded_map;
typedef std::vector < boost::shared_ptr <NIFFile> > locked_files;
static int sLockLevel;
static mutex sProtector;
static loaded_map sLoadedMap;
static locked_files sLockedFiles;
public:
static ptr create (const std::string &name)
{
lock_guard _ (sProtector);
ptr result;
// lookup the resource
loaded_map::iterator i = sLoadedMap.find (name);
if (i == sLoadedMap.end ()) // it doesn't existing currently,
{ // or hasn't in the very near past
// create it now, for smoother threading if needed, the
// loading should be performed outside of the sLoaderMap
// lock and an alternate mechanism should be used to
// synchronize threads competing to load the same resource
result = boost::make_shared <NIFFile> (name, psudo_private_modifier());
// if we are locking the cache add an extra reference
// to keep the file in memory
if (sLockLevel > 0)
sLockedFiles.push_back (result);
// stash a reference to the resource so that future
// calls can benefit
sLoadedMap [name] = boost::weak_ptr <NIFFile> (result);
}
else // it may (probably) still exists
{
// attempt to get the reference
result = i->second.lock ();
if (!result) // resource is in the process of being destroyed
{
// create a new instance, to replace the one that has
// begun the irreversible process of being destroyed
result = boost::make_shared <NIFFile> (name, psudo_private_modifier());
// respect the cache lock...
if (sLockLevel > 0)
sLockedFiles.push_back (result);
// we potentially overwrite an expired pointer here
// but the other thread performing the delete on
// the previous copy of this resource will detect it
// and make sure not to erase the new reference
sLoadedMap [name] = boost::weak_ptr <NIFFile> (result);
}
}
// we made it!
return result;
}
static void release (NIFFile * file)
{
lock_guard _ (sProtector);
loaded_map::iterator i = sLoadedMap.find (file->filename);
// its got to be in here, it just might not be us...
assert (i != sLoadedMap.end ());
// if weak_ptr is still expired, this resource hasn't been recreated
// between the initiation of the final release due to destruction
// of the last shared pointer and this thread acquiring the lock on
// the loader map
if (i->second.expired ())
sLoadedMap.erase (i);
}
static void lockCache ()
{
lock_guard _ (sProtector);
sLockLevel++;
}
static void unlockCache ()
{
locked_files resetList;
{
lock_guard _ (sProtector);
if (--sLockLevel)
sLockedFiles.swap(resetList);
}
// this not necessary, but makes it clear that the
// deletion of the locked cache entries is being done
// outside the protection of sProtector
resetList.clear ();
}
};
int NIFFile::LoadedCache::sLockLevel = 0;
NIFFile::LoadedCache::mutex NIFFile::LoadedCache::sProtector;
NIFFile::LoadedCache::loaded_map NIFFile::LoadedCache::sLoadedMap;
NIFFile::LoadedCache::locked_files NIFFile::LoadedCache::sLockedFiles;
// these three calls are forwarded to the cache implementation...
void NIFFile::lockCache () { LoadedCache::lockCache (); }
void NIFFile::unlockCache () { LoadedCache::unlockCache (); }
NIFFile::ptr NIFFile::create (const std::string &name) { return LoadedCache::create (name); }
/// Open a NIF stream. The name is used for error messages.
NIFFile::NIFFile(const std::string &name, psudo_private_modifier)
: filename(name)
{
parse();
}
NIFFile::~NIFFile()
{
LoadedCache::release (this);
for(std::size_t i=0; i<records.size(); i++)
delete records[i];
}
template <typename NodeType> static Record* construct() { return new NodeType; }
struct RecordFactoryEntry {
typedef Record* (*create_t) ();
char const * mName;
create_t mCreate;
RecordType mType;
};
/* These are all the record types we know how to read.
This can be heavily optimized later if needed. For example, a
hash table or a FSM-based parser could be used to look up
node names.
*/
static const RecordFactoryEntry recordFactories [] = {
{ "NiNode", &construct <NiNode >, RC_NiNode },
{ "AvoidNode", &construct <NiNode >, RC_AvoidNode },
{ "NiBSParticleNode", &construct <NiNode >, RC_NiBSParticleNode },
{ "NiBSAnimationNode", &construct <NiNode >, RC_NiBSAnimationNode },
{ "NiBillboardNode", &construct <NiNode >, RC_NiNode },
{ "NiTriShape", &construct <NiTriShape >, RC_NiTriShape },
{ "NiRotatingParticles", &construct <NiRotatingParticles >, RC_NiRotatingParticles },
{ "NiAutoNormalParticles", &construct <NiAutoNormalParticles >, RC_NiAutoNormalParticles },
{ "NiCamera", &construct <NiCamera >, RC_NiCamera },
{ "RootCollisionNode", &construct <NiNode >, RC_RootCollisionNode },
{ "NiTexturingProperty", &construct <NiTexturingProperty >, RC_NiTexturingProperty },
{ "NiMaterialProperty", &construct <NiMaterialProperty >, RC_NiMaterialProperty },
{ "NiZBufferProperty", &construct <NiZBufferProperty >, RC_NiZBufferProperty },
{ "NiAlphaProperty", &construct <NiAlphaProperty >, RC_NiAlphaProperty },
{ "NiVertexColorProperty", &construct <NiVertexColorProperty >, RC_NiVertexColorProperty },
{ "NiShadeProperty", &construct <NiShadeProperty >, RC_NiShadeProperty },
{ "NiDitherProperty", &construct <NiDitherProperty >, RC_NiDitherProperty },
{ "NiWireframeProperty", &construct <NiWireframeProperty >, RC_NiWireframeProperty },
{ "NiSpecularProperty", &construct <NiSpecularProperty >, RC_NiSpecularProperty },
{ "NiStencilProperty", &construct <NiStencilProperty >, RC_NiStencilProperty },
{ "NiVisController", &construct <NiVisController >, RC_NiVisController },
{ "NiGeomMorpherController", &construct <NiGeomMorpherController >, RC_NiGeomMorpherController },
{ "NiKeyframeController", &construct <NiKeyframeController >, RC_NiKeyframeController },
{ "NiAlphaController", &construct <NiAlphaController >, RC_NiAlphaController },
{ "NiUVController", &construct <NiUVController >, RC_NiUVController },
{ "NiPathController", &construct <NiPathController >, RC_NiPathController },
{ "NiMaterialColorController", &construct <NiMaterialColorController >, RC_NiMaterialColorController },
{ "NiBSPArrayController", &construct <NiBSPArrayController >, RC_NiBSPArrayController },
{ "NiParticleSystemController", &construct <NiParticleSystemController >, RC_NiParticleSystemController },
{ "NiFlipController", &construct <NiFlipController >, RC_NiFlipController },
{ "NiAmbientLight", &construct <NiLight >, RC_NiLight },
{ "NiDirectionalLight", &construct <NiLight >, RC_NiLight },
{ "NiTextureEffect", &construct <NiTextureEffect >, RC_NiTextureEffect },
{ "NiVertWeightsExtraData", &construct <NiVertWeightsExtraData >, RC_NiVertWeightsExtraData },
{ "NiTextKeyExtraData", &construct <NiTextKeyExtraData >, RC_NiTextKeyExtraData },
{ "NiStringExtraData", &construct <NiStringExtraData >, RC_NiStringExtraData },
{ "NiGravity", &construct <NiGravity >, RC_NiGravity },
{ "NiPlanarCollider", &construct <NiPlanarCollider >, RC_NiPlanarCollider },
{ "NiParticleGrowFade", &construct <NiParticleGrowFade >, RC_NiParticleGrowFade },
{ "NiParticleColorModifier", &construct <NiParticleColorModifier >, RC_NiParticleColorModifier },
{ "NiParticleRotation", &construct <NiParticleRotation >, RC_NiParticleRotation },
{ "NiFloatData", &construct <NiFloatData >, RC_NiFloatData },
{ "NiTriShapeData", &construct <NiTriShapeData >, RC_NiTriShapeData },
{ "NiVisData", &construct <NiVisData >, RC_NiVisData },
{ "NiColorData", &construct <NiColorData >, RC_NiColorData },
{ "NiPixelData", &construct <NiPixelData >, RC_NiPixelData },
{ "NiMorphData", &construct <NiMorphData >, RC_NiMorphData },
{ "NiKeyframeData", &construct <NiKeyframeData >, RC_NiKeyframeData },
{ "NiSkinData", &construct <NiSkinData >, RC_NiSkinData },
{ "NiUVData", &construct <NiUVData >, RC_NiUVData },
{ "NiPosData", &construct <NiPosData >, RC_NiPosData },
{ "NiRotatingParticlesData", &construct <NiRotatingParticlesData >, RC_NiRotatingParticlesData },
{ "NiAutoNormalParticlesData", &construct <NiAutoNormalParticlesData >, RC_NiAutoNormalParticlesData },
{ "NiSequenceStreamHelper", &construct <NiSequenceStreamHelper >, RC_NiSequenceStreamHelper },
{ "NiSourceTexture", &construct <NiSourceTexture >, RC_NiSourceTexture },
{ "NiSkinInstance", &construct <NiSkinInstance >, RC_NiSkinInstance },
};
static RecordFactoryEntry const * recordFactories_begin = &recordFactories [0];
static RecordFactoryEntry const * recordFactories_end = &recordFactories [sizeof (recordFactories) / sizeof (recordFactories[0])];
RecordFactoryEntry const * lookupRecordFactory (char const * name)
{
RecordFactoryEntry const * i;
for (i = recordFactories_begin; i != recordFactories_end; ++i)
if (strcmp (name, i->mName) == 0)
break;
if (i == recordFactories_end)
return NULL;
return i;
}
/* This file implements functions from the NIFFile class. It is also
where we stash all the functions we couldn't add as inline
definitions in the record types.
*/
void NIFFile::parse()
{
NIFStream nif (this, Ogre::ResourceGroupManager::getSingleton().openResource(filename));
// Check the header string
std::string head = nif.getString(40);
if(head.compare(0, 22, "NetImmerse File Format") != 0)
fail("Invalid NIF header");
// Get BCD version
ver = nif.getInt();
if(ver != VER_MW)
fail("Unsupported NIF version");
// Number of records
size_t recNum = nif.getInt();
records.resize(recNum);
/* The format for 10.0.1.0 seems to be a bit different. After the
header, it contains the number of records, r (int), just like
4.0.0.2, but following that it contains a short x, followed by x
strings. Then again by r shorts, one for each record, giving
which of the above strings to use to identify the record. After
this follows two ints (zero?) and then the record data. However
we do not support or plan to support other versions yet.
*/
for(size_t i = 0;i < recNum;i++)
{
Record *r = NULL;
std::string rec = nif.getString();
RecordFactoryEntry const * entry = lookupRecordFactory (rec.c_str ());
if (entry != NULL)
{
r = entry->mCreate ();
r->recType = entry->mType;
}
else
fail("Unknown record type " + rec);
assert(r != NULL);
assert(r->recType != RC_MISSING);
r->recName = rec;
r->recIndex = i;
records[i] = r;
r->read(&nif);
// Discard tranformations for the root node, otherwise some meshes
// occasionally get wrong orientation. Only for NiNode-s for now, but
// can be expanded if needed.
// This should be rewritten when the method is cleaned up.
if (0 == i && rec == "NiNode")
{
static_cast<Nif::Node*>(r)->trafo = Nif::Transformation::getIdentity();
}
}
size_t rootNum = nif.getUInt();
roots.resize(rootNum);
for(size_t i = 0;i < rootNum;i++)
{
intptr_t idx = nif.getInt();
roots[i] = ((idx >= 0) ? records.at(idx) : NULL);
}
// Once parsing is done, do post-processing.
for(size_t i=0; i<recNum; i++)
records[i]->post(this);
}
/// \todo move to the write cpp file
void NiSkinInstance::post(NIFFile *nif)
{
data.post(nif);
root.post(nif);
bones.post(nif);
if(data.empty() || root.empty())
nif->fail("NiSkinInstance missing root or data");
size_t bnum = bones.length();
if(bnum != data->bones.size())
nif->fail("Mismatch in NiSkinData bone count");
root->makeRootBone(&data->trafo);
for(size_t i=0; i<bnum; i++)
{
if(bones[i].empty())
nif->fail("Oops: Missing bone! Don't know how to handle this.");
bones[i]->makeBone(i, data->bones[i]);
}
}
void Node::getProperties(const Nif::NiTexturingProperty *&texprop,
const Nif::NiMaterialProperty *&matprop,
const Nif::NiAlphaProperty *&alphaprop,
const Nif::NiVertexColorProperty *&vertprop,
const Nif::NiZBufferProperty *&zprop,
const Nif::NiSpecularProperty *&specprop,
const Nif::NiWireframeProperty *&wireprop) const
{
if(parent)
parent->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop);
for(size_t i = 0;i < props.length();i++)
{
// Entries may be empty
if(props[i].empty())
continue;
const Nif::Property *pr = props[i].getPtr();
if(pr->recType == Nif::RC_NiTexturingProperty)
texprop = static_cast<const Nif::NiTexturingProperty*>(pr);
else if(pr->recType == Nif::RC_NiMaterialProperty)
matprop = static_cast<const Nif::NiMaterialProperty*>(pr);
else if(pr->recType == Nif::RC_NiAlphaProperty)
alphaprop = static_cast<const Nif::NiAlphaProperty*>(pr);
else if(pr->recType == Nif::RC_NiVertexColorProperty)
vertprop = static_cast<const Nif::NiVertexColorProperty*>(pr);
else if(pr->recType == Nif::RC_NiZBufferProperty)
zprop = static_cast<const Nif::NiZBufferProperty*>(pr);
else if(pr->recType == Nif::RC_NiSpecularProperty)
specprop = static_cast<const Nif::NiSpecularProperty*>(pr);
else if(pr->recType == Nif::RC_NiWireframeProperty)
wireprop = static_cast<const Nif::NiWireframeProperty*>(pr);
else
std::cerr<< "Unhandled property type: "<<pr->recName <<std::endl;
}
}
Ogre::Matrix4 Node::getLocalTransform() const
{
Ogre::Matrix4 mat4(Ogre::Matrix4::IDENTITY);
mat4.makeTransform(trafo.pos, Ogre::Vector3(trafo.scale), Ogre::Quaternion(trafo.rotation));
return mat4;
}
Ogre::Matrix4 Node::getWorldTransform() const
{
if(parent != NULL)
return parent->getWorldTransform() * getLocalTransform();
return getLocalTransform();
}
}

212
components/nif/niffile.hpp Normal file
View file

@ -0,0 +1,212 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.sourceforge.net/
This file (nif_file.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 OPENMW_COMPONENTS_NIF_NIFFILE_HPP
#define OPENMW_COMPONENTS_NIF_NIFFILE_HPP
#include <OgreResourceGroupManager.h>
#include <OgreDataStream.h>
#include <OgreVector2.h>
#include <OgreVector3.h>
#include <OgreVector4.h>
#include <OgreMatrix3.h>
#include <OgreQuaternion.h>
#include <OgreStringConverter.h>
#include <stdexcept>
#include <vector>
#include <cassert>
#include <boost/weak_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/detail/endian.hpp>
#include <libs/platform/stdint.h>
#include "record.hpp"
#include "niftypes.hpp"
#include "nifstream.hpp"
namespace Nif
{
class NIFFile
{
enum NIFVersion {
VER_MW = 0x04000002 // Morrowind NIFs
};
/// Nif file version
int ver;
/// File name, used for error messages
std::string filename;
/// Record list
std::vector<Record*> records;
/// Root list
std::vector<Record*> roots;
/// Parse the file
void parse();
class LoadedCache;
friend class LoadedCache;
// attempt to protect NIFFile from misuse...
struct psudo_private_modifier {}; // this dirty little trick should optimize out
NIFFile (NIFFile const &);
void operator = (NIFFile const &);
public:
/// Used for error handling
void fail(const std::string &msg)
{
std::string err = "NIFFile Error: " + msg;
err += "\nFile: " + filename;
throw std::runtime_error(err);
}
void warn(const std::string &msg)
{
std::cerr << "NIFFile Warning: " << msg <<std::endl
<< "File: " << filename <<std::endl;
}
typedef boost::shared_ptr <NIFFile> ptr;
/// Open a NIF stream. The name is used for error messages.
NIFFile(const std::string &name, psudo_private_modifier);
~NIFFile();
static ptr create (const std::string &name);
static void lockCache ();
static void unlockCache ();
struct CacheLock
{
CacheLock () { lockCache (); }
~CacheLock () { unlockCache (); }
};
/// Get a given record
Record *getRecord(size_t index)
{
Record *res = records.at(index);
assert(res != NULL);
return res;
}
/// Number of records
size_t numRecords() { return records.size(); }
/// Get a given root
Record *getRoot(size_t index=0)
{
Record *res = roots.at(index);
assert(res != NULL);
return res;
}
/// Number of roots
size_t numRoots() { return roots.size(); }
};
template<typename T>
struct KeyT {
float mTime;
T mValue;
T mForwardValue; // Only for Quadratic interpolation
T mBackwardValue; // Only for Quadratic interpolation
float mTension; // Only for TBC interpolation
float mBias; // Only for TBC interpolation
float mContinuity; // Only for TBC interpolation
};
typedef KeyT<float> FloatKey;
typedef KeyT<Ogre::Vector3> Vector3Key;
typedef KeyT<Ogre::Vector4> Vector4Key;
typedef KeyT<Ogre::Quaternion> QuaternionKey;
template<typename T, T (NIFStream::*getValue)()>
struct KeyListT {
typedef std::vector< KeyT<T> > VecType;
static const int sLinearInterpolation = 1;
static const int sQuadraticInterpolation = 2;
static const int sTBCInterpolation = 3;
int mInterpolationType;
VecType mKeys;
void read(NIFStream *nif, bool force=false)
{
size_t count = nif->getInt();
if(count == 0 && !force)
return;
mInterpolationType = nif->getInt();
mKeys.resize(count);
if(mInterpolationType == sLinearInterpolation)
{
for(size_t i = 0;i < count;i++)
{
KeyT<T> &key = mKeys[i];
key.mTime = nif->getFloat();
key.mValue = (nif->*getValue)();
}
}
else if(mInterpolationType == sQuadraticInterpolation)
{
for(size_t i = 0;i < count;i++)
{
KeyT<T> &key = mKeys[i];
key.mTime = nif->getFloat();
key.mValue = (nif->*getValue)();
key.mForwardValue = (nif->*getValue)();
key.mBackwardValue = (nif->*getValue)();
}
}
else if(mInterpolationType == sTBCInterpolation)
{
for(size_t i = 0;i < count;i++)
{
KeyT<T> &key = mKeys[i];
key.mTime = nif->getFloat();
key.mValue = (nif->*getValue)();
key.mTension = nif->getFloat();
key.mBias = nif->getFloat();
key.mContinuity = nif->getFloat();
}
}
else
nif->file->warn("Unhandled interpolation type: "+Ogre::StringConverter::toString(mInterpolationType));
}
};
typedef KeyListT<float,&NIFStream::getFloat> FloatKeyList;
typedef KeyListT<Ogre::Vector3,&NIFStream::getVector3> Vector3KeyList;
typedef KeyListT<Ogre::Vector4,&NIFStream::getVector4> Vector4KeyList;
typedef KeyListT<Ogre::Quaternion,&NIFStream::getQuaternion> QuaternionKeyList;
} // Namespace
#endif

View file

@ -0,0 +1,181 @@
#ifndef OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP
#define OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP
namespace Nif
{
class NIFFile;
class NIFStream {
/// Input stream
Ogre::DataStreamPtr inp;
uint8_t read_byte()
{
uint8_t byte;
if(inp->read(&byte, 1) != 1) return 0;
return byte;
}
uint16_t read_le16()
{
uint8_t buffer[2];
if(inp->read(buffer, 2) != 2) return 0;
return buffer[0] | (buffer[1]<<8);
}
uint32_t read_le32()
{
uint8_t buffer[4];
if(inp->read(buffer, 4) != 4) return 0;
return buffer[0] | (buffer[1]<<8) | (buffer[2]<<16) | (buffer[3]<<24);
}
float read_le32f()
{
union {
uint32_t i;
float f;
} u = { read_le32() };
return u.f;
}
public:
NIFFile * const file;
NIFStream (NIFFile * file, Ogre::DataStreamPtr inp): file (file), inp (inp) {}
/*************************************************
Parser functions
****************************************************/
template <typename T>
struct GetHandler
{
typedef T (NIFStream::*fn_t)();
static const fn_t sValue; // this is specialized per supported type in the .cpp file
static T read (NIFStream* nif)
{
return (nif->*sValue) ();
}
};
template <typename T>
void read (NIFStream* nif, T & Value)
{
Value = GetHandler <T>::read (nif);
}
void skip(size_t size) { inp->skip(size); }
void read (void * data, size_t size) { inp->read (data, size); }
char getChar() { return read_byte(); }
short getShort() { return read_le16(); }
unsigned short getUShort() { return read_le16(); }
int getInt() { return read_le32(); }
int getUInt() { return read_le32(); }
float getFloat() { return read_le32f(); }
Ogre::Vector2 getVector2()
{
float a[2];
for(size_t i = 0;i < 2;i++)
a[i] = getFloat();
return Ogre::Vector2(a);
}
Ogre::Vector3 getVector3()
{
float a[3];
for(size_t i = 0;i < 3;i++)
a[i] = getFloat();
return Ogre::Vector3(a);
}
Ogre::Vector4 getVector4()
{
float a[4];
for(size_t i = 0;i < 4;i++)
a[i] = getFloat();
return Ogre::Vector4(a);
}
Ogre::Matrix3 getMatrix3()
{
Ogre::Real a[3][3];
for(size_t i = 0;i < 3;i++)
{
for(size_t j = 0;j < 3;j++)
a[i][j] = Ogre::Real(getFloat());
}
return Ogre::Matrix3(a);
}
Ogre::Quaternion getQuaternion()
{
float a[4];
for(size_t i = 0;i < 4;i++)
a[i] = getFloat();
return Ogre::Quaternion(a);
}
Transformation getTrafo()
{
Transformation t;
t.pos = getVector3();
t.rotation = getMatrix3();
t.scale = getFloat();
return t;
}
std::string getString(size_t length)
{
std::vector<char> str (length+1, 0);
if(inp->read(&str[0], length) != length)
throw std::runtime_error ("string length in NIF file does not match");
return &str[0];
}
std::string getString()
{
size_t size = read_le32();
return getString(size);
}
void getShorts(std::vector<short> &vec, size_t size)
{
vec.resize(size);
for(size_t i = 0;i < vec.size();i++)
vec[i] = getShort();
}
void getFloats(std::vector<float> &vec, size_t size)
{
vec.resize(size);
for(size_t i = 0;i < vec.size();i++)
vec[i] = getFloat();
}
void getVector2s(std::vector<Ogre::Vector2> &vec, size_t size)
{
vec.resize(size);
for(size_t i = 0;i < vec.size();i++)
vec[i] = getVector2();
}
void getVector3s(std::vector<Ogre::Vector3> &vec, size_t size)
{
vec.resize(size);
for(size_t i = 0;i < vec.size();i++)
vec[i] = getVector3();
}
void getVector4s(std::vector<Ogre::Vector4> &vec, size_t size)
{
vec.resize(size);
for(size_t i = 0;i < vec.size();i++)
vec[i] = getVector4();
}
void getQuaternions(std::vector<Ogre::Quaternion> &quat, size_t size)
{
quat.resize(size);
for(size_t i = 0;i < quat.size();i++)
quat[i] = getQuaternion();
}
};
}
#endif

View file

@ -0,0 +1,51 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.sourceforge.net/
This file (nif_types.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 OPENMW_COMPONENTS_NIF_NIFTYPES_HPP
#define OPENMW_COMPONENTS_NIF_NIFTYPES_HPP
#include <OgreVector3.h>
#include <OgreMatrix3.h>
// Common types used in NIF files
namespace Nif
{
struct Transformation
{
Ogre::Vector3 pos;
Ogre::Matrix3 rotation;
float scale;
static const Transformation& getIdentity()
{
static const Transformation identity = {
Ogre::Vector3::ZERO, Ogre::Matrix3::IDENTITY, 1.0f
};
return identity;
}
};
} // Namespace
#endif

272
components/nif/node.hpp Normal file
View file

@ -0,0 +1,272 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.sourceforge.net/
This file (node.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 OPENMW_COMPONENTS_NIF_NODE_HPP
#define OPENMW_COMPONENTS_NIF_NODE_HPP
#include <OgreMatrix4.h>
#include "controlled.hpp"
#include "data.hpp"
#include "property.hpp"
namespace Nif
{
class NiNode;
/** A Node is an object that's part of the main NIF tree. It has
parent node (unless it's the root), and transformation (location
and rotation) relative to it's parent.
*/
class Node : public Named
{
public:
// Node flags. Interpretation depends somewhat on the type of node.
int flags;
Transformation trafo;
Ogre::Vector3 velocity; // Unused? Might be a run-time game state
PropertyList props;
// Bounding box info
bool hasBounds;
Ogre::Vector3 boundPos;
Ogre::Matrix3 boundRot;
Ogre::Vector3 boundXYZ; // Box size
void read(NIFStream *nif)
{
Named::read(nif);
flags = nif->getUShort();
trafo = nif->getTrafo();
velocity = nif->getVector3();
props.read(nif);
hasBounds = !!nif->getInt();
if(hasBounds)
{
nif->getInt(); // always 1
boundPos = nif->getVector3();
boundRot = nif->getMatrix3();
boundXYZ = nif->getVector3();
}
parent = NULL;
boneTrafo = NULL;
boneIndex = -1;
}
void post(NIFFile *nif)
{
Named::post(nif);
props.post(nif);
}
// Parent node, or NULL for the root node. As far as I'm aware, only
// NiNodes (or types derived from NiNodes) can be parents.
NiNode *parent;
// Bone transformation. If set, node is a part of a skeleton.
const NiSkinData::BoneTrafo *boneTrafo;
// Bone weight info, from NiSkinData
const NiSkinData::BoneInfo *boneInfo;
// Bone index. If -1, this node is either not a bone, or if
// boneTrafo is set it is the root bone in the skeleton.
short boneIndex;
void makeRootBone(const NiSkinData::BoneTrafo *tr)
{
boneTrafo = tr;
boneIndex = -1;
}
void makeBone(short ind, const NiSkinData::BoneInfo &bi)
{
boneInfo = &bi;
boneTrafo = &bi.trafo;
boneIndex = ind;
}
void getProperties(const Nif::NiTexturingProperty *&texprop,
const Nif::NiMaterialProperty *&matprop,
const Nif::NiAlphaProperty *&alphaprop,
const Nif::NiVertexColorProperty *&vertprop,
const Nif::NiZBufferProperty *&zprop,
const Nif::NiSpecularProperty *&specprop,
const Nif::NiWireframeProperty *&wireprop) const;
Ogre::Matrix4 getLocalTransform() const;
Ogre::Matrix4 getWorldTransform() const;
};
struct NiNode : Node
{
NodeList children;
NodeList effects;
enum Flags {
Flag_Hidden = 0x0001,
Flag_MeshCollision = 0x0002,
Flag_BBoxCollision = 0x0004
};
enum BSAnimFlags {
AnimFlag_AutoPlay = 0x0020
};
enum BSParticleFlags {
ParticleFlag_AutoPlay = 0x0020
};
void read(NIFStream *nif)
{
Node::read(nif);
children.read(nif);
effects.read(nif);
}
void post(NIFFile *nif)
{
Node::post(nif);
children.post(nif);
effects.post(nif);
for(size_t i = 0;i < children.length();i++)
{
// Why would a unique list of children contain empty refs?
if(!children[i].empty())
children[i]->parent = this;
}
}
};
struct NiTriShape : Node
{
/* Possible flags:
0x40 - mesh has no vertex normals ?
Only flags included in 0x47 (ie. 0x01, 0x02, 0x04 and 0x40) have
been observed so far.
*/
NiTriShapeDataPtr data;
NiSkinInstancePtr skin;
void read(NIFStream *nif)
{
Node::read(nif);
data.read(nif);
skin.read(nif);
}
void post(NIFFile *nif)
{
Node::post(nif);
data.post(nif);
skin.post(nif);
}
};
struct NiCamera : Node
{
struct Camera
{
// Camera frustrum
float left, right, top, bottom, nearDist, farDist;
// Viewport
float vleft, vright, vtop, vbottom;
// Level of detail modifier
float LOD;
void read(NIFStream *nif)
{
left = nif->getFloat();
right = nif->getFloat();
top = nif->getFloat();
bottom = nif->getFloat();
nearDist = nif->getFloat();
farDist = nif->getFloat();
vleft = nif->getFloat();
vright = nif->getFloat();
vtop = nif->getFloat();
vbottom = nif->getFloat();
LOD = nif->getFloat();
}
};
Camera cam;
void read(NIFStream *nif)
{
Node::read(nif);
cam.read(nif);
nif->getInt(); // -1
nif->getInt(); // 0
}
};
struct NiAutoNormalParticles : Node
{
NiAutoNormalParticlesDataPtr data;
void read(NIFStream *nif)
{
Node::read(nif);
data.read(nif);
nif->getInt(); // -1
}
void post(NIFFile *nif)
{
Node::post(nif);
data.post(nif);
}
};
struct NiRotatingParticles : Node
{
NiRotatingParticlesDataPtr data;
void read(NIFStream *nif)
{
Node::read(nif);
data.read(nif);
nif->getInt(); // -1
}
void post(NIFFile *nif)
{
Node::post(nif);
data.post(nif);
}
};
} // Namespace
#endif

333
components/nif/property.hpp Normal file
View file

@ -0,0 +1,333 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.sourceforge.net/
This file (property.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 OPENMW_COMPONENTS_NIF_PROPERTY_HPP
#define OPENMW_COMPONENTS_NIF_PROPERTY_HPP
#include "controlled.hpp"
namespace Nif
{
class Property : public Named
{
public:
// The meaning of these depends on the actual property type.
int flags;
void read(NIFStream *nif)
{
Named::read(nif);
flags = nif->getUShort();
}
};
class NiTexturingProperty : public Property
{
public:
// A sub-texture
struct Texture
{
/* Clamp mode
0 - clampS clampT
1 - clampS wrapT
2 - wrapS clampT
3 - wrapS wrapT
*/
/* Filter:
0 - nearest
1 - bilinear
2 - trilinear
3, 4, 5 - who knows
*/
bool inUse;
NiSourceTexturePtr texture;
int clamp, uvSet, filter;
short unknown2;
void read(NIFStream *nif)
{
inUse = !!nif->getInt();
if(!inUse) return;
texture.read(nif);
clamp = nif->getInt();
filter = nif->getInt();
uvSet = nif->getInt();
// I have no idea, but I think these are actually two
// PS2-specific shorts (ps2L and ps2K), followed by an unknown
// short.
nif->skip(6);
}
void post(NIFFile *nif)
{
texture.post(nif);
}
};
/* Apply mode:
0 - replace
1 - decal
2 - modulate
3 - hilight // These two are for PS2 only?
4 - hilight2
*/
int apply;
/*
* The textures in this list are as follows:
*
* 0 - Base texture
* 1 - Dark texture
* 2 - Detail texture
* 3 - Gloss texture (never used?)
* 4 - Glow texture
* 5 - Bump map texture
* 6 - Decal texture
*/
enum TextureType
{
BaseTexture = 0,
DarkTexture = 1,
DetailTexture = 2,
GlossTexture = 3,
GlowTexture = 4,
BumpTexture = 5,
DecalTexture = 6
};
Texture textures[7];
void read(NIFStream *nif)
{
Property::read(nif);
apply = nif->getInt();
// Unknown, always 7. Probably the number of textures to read
// below
nif->getInt();
textures[0].read(nif); // Base
textures[1].read(nif); // Dark
textures[2].read(nif); // Detail
textures[3].read(nif); // Gloss (never present)
textures[4].read(nif); // Glow
textures[5].read(nif); // Bump map
if(textures[5].inUse)
{
// Ignore these at the moment
/*float lumaScale =*/ nif->getFloat();
/*float lumaOffset =*/ nif->getFloat();
/*const Vector4 *lumaMatrix =*/ nif->getVector4();
}
textures[6].read(nif); // Decal
}
void post(NIFFile *nif)
{
Property::post(nif);
for(int i = 0;i < 7;i++)
textures[i].post(nif);
}
};
// These contain no other data than the 'flags' field in Property
class NiShadeProperty : public Property { };
class NiDitherProperty : public Property { };
class NiZBufferProperty : public Property { };
class NiSpecularProperty : public Property { };
class NiWireframeProperty : public Property { };
// The rest are all struct-based
template <typename T>
struct StructPropT : Property
{
T data;
void read(NIFStream *nif)
{
Property::read(nif);
data.read(nif);
}
};
struct S_MaterialProperty
{
// The vector components are R,G,B
Ogre::Vector3 ambient, diffuse, specular, emissive;
float glossiness, alpha;
void read(NIFStream *nif)
{
ambient = nif->getVector3();
diffuse = nif->getVector3();
specular = nif->getVector3();
emissive = nif->getVector3();
glossiness = nif->getFloat();
alpha = nif->getFloat();
}
};
struct S_VertexColorProperty
{
/* Vertex mode:
0 - source ignore
1 - source emmisive
2 - source amb diff
Lighting mode
0 - lighting emmisive
1 - lighting emmisive ambient/diffuse
*/
int vertmode, lightmode;
void read(NIFStream *nif)
{
vertmode = nif->getInt();
lightmode = nif->getInt();
}
};
struct S_AlphaProperty
{
/*
In NiAlphaProperty, the flags have the following meaning:
Bit 0 : alpha blending enable
Bits 1-4 : source blend mode
Bits 5-8 : destination blend mode
Bit 9 : alpha test enable
Bit 10-12 : alpha test mode
Bit 13 : no sorter flag ( disables triangle sorting )
blend modes (glBlendFunc):
0000 GL_ONE
0001 GL_ZERO
0010 GL_SRC_COLOR
0011 GL_ONE_MINUS_SRC_COLOR
0100 GL_DST_COLOR
0101 GL_ONE_MINUS_DST_COLOR
0110 GL_SRC_ALPHA
0111 GL_ONE_MINUS_SRC_ALPHA
1000 GL_DST_ALPHA
1001 GL_ONE_MINUS_DST_ALPHA
1010 GL_SRC_ALPHA_SATURATE
test modes (glAlphaFunc):
000 GL_ALWAYS
001 GL_LESS
010 GL_EQUAL
011 GL_LEQUAL
100 GL_GREATER
101 GL_NOTEQUAL
110 GL_GEQUAL
111 GL_NEVER
Taken from:
http://niftools.sourceforge.net/doc/nif/NiAlphaProperty.html
Right now we only use standard alpha blending (see the Ogre code
that sets it up) and it appears that this is the only blending
used in the original game. Bloodmoon (along with several mods) do
however use other settings, such as discarding pixel values with
alpha < 1.0. This is faster because we don't have to mess with the
depth stuff like we did for blending. And OGRE has settings for
this too.
*/
// Tested against when certain flags are set (see above.)
unsigned char threshold;
void read(NIFStream *nif)
{
threshold = nif->getChar();
}
};
/*
Docs taken from:
http://niftools.sourceforge.net/doc/nif/NiStencilProperty.html
*/
struct S_StencilProperty
{
// Is stencil test enabled?
unsigned char enabled;
/*
0 TEST_NEVER
1 TEST_LESS
2 TEST_EQUAL
3 TEST_LESS_EQUAL
4 TEST_GREATER
5 TEST_NOT_EQUAL
6 TEST_GREATER_EQUAL
7 TEST_ALWAYS
*/
int compareFunc;
unsigned stencilRef;
unsigned stencilMask;
/*
Stencil test fail action, depth test fail action and depth test pass action:
0 ACTION_KEEP
1 ACTION_ZERO
2 ACTION_REPLACE
3 ACTION_INCREMENT
4 ACTION_DECREMENT
5 ACTION_INVERT
*/
int failAction;
int zFailAction;
int zPassAction;
/*
Face draw mode:
0 DRAW_CCW_OR_BOTH
1 DRAW_CCW [default]
2 DRAW_CW
3 DRAW_BOTH
*/
int drawMode;
void read(NIFStream *nif)
{
enabled = nif->getChar();
compareFunc = nif->getInt();
stencilRef = nif->getUInt();
stencilMask = nif->getUInt();
failAction = nif->getInt();
zFailAction = nif->getInt();
zPassAction = nif->getInt();
drawMode = nif->getInt();
}
};
class NiAlphaProperty : public StructPropT<S_AlphaProperty> { };
class NiMaterialProperty : public StructPropT<S_MaterialProperty> { };
class NiVertexColorProperty : public StructPropT<S_VertexColorProperty> { };
class NiStencilProperty : public StructPropT<S_StencilProperty> { };
} // Namespace
#endif

120
components/nif/record.hpp Normal file
View file

@ -0,0 +1,120 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.sourceforge.net/
This file (record.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 OPENMW_COMPONENTS_NIF_RECORD_HPP
#define OPENMW_COMPONENTS_NIF_RECORD_HPP
#include <string>
namespace Nif
{
class NIFFile;
class NIFStream;
enum RecordType
{
RC_MISSING = 0,
RC_NiNode,
RC_AvoidNode,
RC_NiTriShape,
RC_NiRotatingParticles,
RC_NiAutoNormalParticles,
RC_NiBSParticleNode,
RC_NiCamera,
RC_NiTexturingProperty,
RC_NiMaterialProperty,
RC_NiZBufferProperty,
RC_NiAlphaProperty,
RC_NiVertexColorProperty,
RC_NiShadeProperty,
RC_NiDitherProperty,
RC_NiWireframeProperty,
RC_NiSpecularProperty,
RC_NiStencilProperty,
RC_NiVisController,
RC_NiGeomMorpherController,
RC_NiKeyframeController,
RC_NiAlphaController,
RC_NiUVController,
RC_NiPathController,
RC_NiMaterialColorController,
RC_NiBSPArrayController,
RC_NiParticleSystemController,
RC_NiFlipController,
RC_NiBSAnimationNode,
RC_NiLight,
RC_NiTextureEffect,
RC_NiVertWeightsExtraData,
RC_NiTextKeyExtraData,
RC_NiStringExtraData,
RC_NiGravity,
RC_NiPlanarCollider,
RC_NiParticleGrowFade,
RC_NiParticleColorModifier,
RC_NiParticleRotation,
RC_NiFloatData,
RC_NiTriShapeData,
RC_NiVisData,
RC_NiColorData,
RC_NiPixelData,
RC_NiMorphData,
RC_NiKeyframeData,
RC_NiSkinData,
RC_NiUVData,
RC_NiPosData,
RC_NiRotatingParticlesData,
RC_NiAutoNormalParticlesData,
RC_NiSequenceStreamHelper,
RC_NiSourceTexture,
RC_NiSkinInstance,
RC_RootCollisionNode
};
/// Base class for all records
struct Record
{
// Record type and type name
int recType;
std::string recName;
size_t recIndex;
Record() : recType(RC_MISSING), recIndex(~(size_t)0) {}
/// Parses the record from file
virtual void read(NIFStream *nif) = 0;
/// Does post-processing, after the entire tree is loaded
virtual void post(NIFFile *nif) {}
virtual ~Record() {}
/*
Use these later if you want custom allocation of all NIF objects
static void* operator new(size_t size);
static void operator delete(void *p);
*/
};
} // Namespace
#endif

View file

@ -0,0 +1,181 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.sourceforge.net/
This file (record_ptr.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 OPENMW_COMPONENTS_NIF_RECORDPTR_HPP
#define OPENMW_COMPONENTS_NIF_RECORDPTR_HPP
#include "niffile.hpp"
#include <vector>
namespace Nif
{
/** A reference to another record. It is read as an index from the
NIF, and later looked up in the index table to get an actual
pointer.
*/
template <class X>
class RecordPtrT
{
union {
intptr_t index;
X* ptr;
};
public:
RecordPtrT() : index(-2) {}
/// Read the index from the nif
void read(NIFStream *nif)
{
// Can only read the index once
assert(index == -2);
// Store the index for later
index = nif->getInt();
assert(index >= -1);
}
/// Resolve index to pointer
void post(NIFFile *nif)
{
if(index < 0)
ptr = NULL;
else
{
Record *r = nif->getRecord(index);
// And cast it
ptr = dynamic_cast<X*>(r);
assert(ptr != NULL);
}
}
/// Look up the actual object from the index
const X* getPtr() const
{
assert(ptr != NULL);
return ptr;
}
X* getPtr()
{
assert(ptr != NULL);
return ptr;
}
const X& get() const
{ return *getPtr(); }
X& get()
{ return *getPtr(); }
/// Syntactic sugar
const X* operator->() const
{ return getPtr(); }
X* operator->()
{ return getPtr(); }
/// Pointers are allowed to be empty
bool empty() const
{ return ptr == NULL; }
};
/** A list of references to other records. These are read as a list,
and later converted to pointers as needed. Not an optimized
implementation.
*/
template <class X>
class RecordListT
{
typedef RecordPtrT<X> Ptr;
std::vector<Ptr> list;
public:
void read(NIFStream *nif)
{
int len = nif->getInt();
list.resize(len);
for(size_t i=0;i < list.size();i++)
list[i].read(nif);
}
void post(NIFFile *nif)
{
for(size_t i=0;i < list.size();i++)
list[i].post(nif);
}
const Ptr& operator[](size_t index) const
{ return list.at(index); }
Ptr& operator[](size_t index)
{ return list.at(index); }
size_t length() const
{ return list.size(); }
};
class Node;
class Extra;
class Property;
class NiUVData;
class NiPosData;
class NiVisData;
class Controller;
class Controlled;
class NiSkinData;
class NiFloatData;
class NiMorphData;
class NiPixelData;
class NiColorData;
class NiKeyframeData;
class NiTriShapeData;
class NiSkinInstance;
class NiSourceTexture;
class NiRotatingParticlesData;
class NiAutoNormalParticlesData;
typedef RecordPtrT<Node> NodePtr;
typedef RecordPtrT<Extra> ExtraPtr;
typedef RecordPtrT<NiUVData> NiUVDataPtr;
typedef RecordPtrT<NiPosData> NiPosDataPtr;
typedef RecordPtrT<NiVisData> NiVisDataPtr;
typedef RecordPtrT<Controller> ControllerPtr;
typedef RecordPtrT<Controlled> ControlledPtr;
typedef RecordPtrT<NiSkinData> NiSkinDataPtr;
typedef RecordPtrT<NiMorphData> NiMorphDataPtr;
typedef RecordPtrT<NiPixelData> NiPixelDataPtr;
typedef RecordPtrT<NiFloatData> NiFloatDataPtr;
typedef RecordPtrT<NiColorData> NiColorDataPtr;
typedef RecordPtrT<NiKeyframeData> NiKeyframeDataPtr;
typedef RecordPtrT<NiTriShapeData> NiTriShapeDataPtr;
typedef RecordPtrT<NiSkinInstance> NiSkinInstancePtr;
typedef RecordPtrT<NiSourceTexture> NiSourceTexturePtr;
typedef RecordPtrT<NiRotatingParticlesData> NiRotatingParticlesDataPtr;
typedef RecordPtrT<NiAutoNormalParticlesData> NiAutoNormalParticlesDataPtr;
typedef RecordListT<Node> NodeList;
typedef RecordListT<Property> PropertyList;
typedef RecordListT<NiSourceTexture> NiSourceTextureList;
} // Namespace
#endif

View file

@ -0,0 +1,12 @@
GCC=g++
all: niftool nif_bsa_test
niftool: niftool.cpp ../nif_file.hpp ../nif_file.cpp ../record.hpp
$(GCC) $< ../nif_file.cpp ../../tools/stringops.cpp -o $@
nif_bsa_test: nif_bsa_test.cpp ../nif_file.cpp ../../bsa/bsa_file.cpp ../../tools/stringops.cpp
$(GCC) $^ -o $@
clean:
rm niftool *_test

View file

@ -0,0 +1,30 @@
/*
Runs NIFFile through all the NIFs in Morrowind.bsa.
*/
#include "../nif_file.hpp"
#include "../../bsa/bsa_file.hpp"
#include "../../tools/stringops.hpp"
#include <iostream>
using namespace Mangle::Stream;
using namespace std;
using namespace Nif;
int main(int argc, char **args)
{
BSAFile bsa;
cout << "Reading Morrowind.bsa\n";
bsa.open("../../data/Morrowind.bsa");
const BSAFile::FileList &files = bsa.getList();
for(int i=0; i<files.size(); i++)
{
const char *n = files[i].name;
if(!ends(n, ".nif")) continue;
cout << "Decoding " << n << endl;
NIFFile nif(bsa.getFile(n), n);
}
}

View file

@ -0,0 +1,232 @@
/*
Test of the NIFFile class
*/
#include "../nif_file.hpp"
#include <iostream>
#include <iomanip>
#include "../../mangle/stream/servers/file_stream.hpp"
#include "../node.hpp"
#include "../controller.hpp"
#include "../data.hpp"
using namespace Mangle::Stream;
using namespace std;
using namespace Nif;
// Display very verbose information
bool verbose = false;
void doVector(const Vector *vec)
{
cout << "["
<< vec->array[0] << ","
<< vec->array[1] << ","
<< vec->array[2] << "]\n";
}
void doVector4(const Vector4 *vec)
{
cout << "["
<< vec->array[0] << ","
<< vec->array[1] << ","
<< vec->array[2] << ","
<< vec->array[3] << "]\n";
}
void doMatrix(const Matrix *mat)
{
cout << "Matrix:\n";
for(int i=0; i<3; i++)
{
cout << " ";
doVector(&mat->v[i]);
}
}
void doTrafo(const Transformation* trafo)
{
cout << "--- transformation:\n";
cout << "Pos: "; doVector(&trafo->pos);
cout << "Rot: "; doMatrix(&trafo->rotation);
cout << "Scale: " << trafo->scale << endl;
cout << "Vel: "; doVector(&trafo->velocity);
cout << "--- end transformation\n";
}
void doExtra(Extra *e)
{
cout << "Extra: " << e->extra.getIndex() << endl;
}
void doControlled(Controlled *c)
{
doExtra(c);
cout << "Controller: " << c->controller.getIndex() << endl;
}
void doNamed(Named *n)
{
doControlled(n);
cout << "Name: " << n->name.toString() << endl;
}
void doNode(Node *n)
{
doNamed(n);
cout << "Flags: 0x" << hex << n->flags << dec << endl;
doTrafo(n->trafo);
cout << "Properties:";
for(int i=0; i<n->props.length(); i++)
cout << " " << n->props.getIndex(i);
cout << endl;
if(n->hasBounds)
{
cout << "Bounding box:\n";
doVector(n->boundPos);
doMatrix(n->boundRot);
doVector(n->boundXYZ);
}
if(n->boneTrafo)
{
cout << "This is a bone: ";
if(n->boneIndex == -1)
cout << "root bone\n";
else
cout << "index " << n->boneIndex << endl;
}
}
void doNiTriShape(NiTriShape *n)
{
doNode(n);
cout << "Shape data: " << n->data.getIndex() << endl;
cout << "Skin instance: " << n->skin.getIndex() << endl;
}
void doNiSkinData(NiSkinData *n)
{
int c = n->bones.size();
cout << "Global transformation:\n";
doMatrix(&n->trafo->rotation);
doVector(&n->trafo->trans);
cout << "Scale: " << n->trafo->scale << endl;
cout << "Bone number: " << c << endl;
for(int i=0; i<c; i++)
{
NiSkinData::BoneInfo &bi = n->bones[i];
cout << "-- Bone " << i << ":\n";
doMatrix(&bi.trafo->rotation);
doVector(&bi.trafo->trans);
cout << "Scale: " << bi.trafo->scale << endl;
cout << "Unknown: "; doVector4(bi.unknown);
cout << "Weight number: " << bi.weights.length << endl;
if(verbose)
for(int j=0; j<bi.weights.length; j++)
{
const NiSkinData::VertWeight &w = bi.weights.ptr[j];
cout << " vert:" << w.vertex << " weight:" << w.weight << endl;
}
}
}
void doNiSkinInstance(NiSkinInstance *n)
{
cout << "Data: " << n->data.getIndex() << endl;
cout << "Root: " << n->root.getIndex() << endl;
cout << "Bones:";
for(int i=0; i<n->bones.length(); i++)
cout << " " << n->bones.getIndex(i);
cout << endl;
}
void doNiNode(NiNode *n)
{
doNode(n);
cout << "Children:";
for(int i=0; i<n->children.length(); i++)
cout << " " << n->children.getIndex(i);
cout << endl;
cout << "Effects:";
for(int i=0; i<n->effects.length(); i++)
cout << " " << n->effects.getIndex(i);
cout << endl;
}
void doNiStringExtraData(NiStringExtraData *s)
{
doExtra(s);
cout << "String: " << s->string.toString() << endl;
}
void doNiTextKeyExtraData(NiTextKeyExtraData *t)
{
doExtra(t);
for(int i=0; i<t->list.size(); i++)
{
cout << "@time " << t->list[i].time << ":\n\""
<< t->list[i].text.toString() << "\"" << endl;
}
}
void doController(Controller *r)
{
cout << "Next controller: " << r->next.getIndex() << endl;
cout << "Flags: " << hex << r->flags << dec << endl;
cout << "Frequency: " << r->frequency << endl;
cout << "Phase: " << r->phase << endl;
cout << "Time start: " << r->timeStart << endl;
cout << "Time stop: " << r->timeStop << endl;
cout << "Target: " << r->target.getIndex() << endl;
}
void doNiKeyframeController(NiKeyframeController *k)
{
doController(k);
cout << "Data: " << k->data.getIndex() << endl;
}
int main(int argc, char **args)
{
if(argc != 2)
{
cout << "Specify a NIF file on the command line\n";
return 1;
}
StreamPtr file(new FileStream(args[1]));
NIFFile nif(file, args[1]);
int num = nif.numRecords();
for(int i=0; i<num; i++)
{
Record *r = nif.getRecord(i);
cout << i << ": " << r->recName.toString() << endl;
switch(r->recType)
{
case RC_NiNode: doNiNode((NiNode*)r); break;
case RC_NiSkinData: doNiSkinData((NiSkinData*)r); break;
case RC_NiSkinInstance: doNiSkinInstance((NiSkinInstance*)r); break;
case RC_NiTriShape: doNiTriShape((NiTriShape*)r); break;
case RC_NiStringExtraData: doNiStringExtraData((NiStringExtraData*)r); break;
case RC_NiSequenceStreamHelper: doNamed((Named*)r); break;
case RC_NiTextKeyExtraData: doNiTextKeyExtraData((NiTextKeyExtraData*)r); break;
case RC_NiKeyframeController: doNiKeyframeController((NiKeyframeController*)r); break;
}
cout << endl;
}
}

File diff suppressed because it is too large Load diff

18
components/nif/tests/test.sh Executable file
View file

@ -0,0 +1,18 @@
#!/bin/bash
make || exit
mkdir -p output
PROGS=*_test
for a in $PROGS; do
if [ -f "output/$a.out" ]; then
echo "Running $a:"
./$a | diff output/$a.out -
else
echo "Creating $a.out"
./$a > "output/$a.out"
git add "output/$a.out"
fi
done