mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-04-30 05:47:57 +03:00
Imported Upstream version 0.26.0
This commit is contained in:
commit
9a2b6c69b6
1398 changed files with 212217 additions and 0 deletions
151
components/nif/controlled.hpp
Normal file
151
components/nif/controlled.hpp
Normal 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
|
330
components/nif/controller.hpp
Normal file
330
components/nif/controller.hpp
Normal 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
428
components/nif/data.hpp
Normal 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
105
components/nif/effect.hpp
Normal 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
108
components/nif/extra.hpp
Normal 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
440
components/nif/niffile.cpp
Normal 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
212
components/nif/niffile.hpp
Normal 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
|
181
components/nif/nifstream.hpp
Normal file
181
components/nif/nifstream.hpp
Normal 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
|
51
components/nif/niftypes.hpp
Normal file
51
components/nif/niftypes.hpp
Normal 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
272
components/nif/node.hpp
Normal 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
333
components/nif/property.hpp
Normal 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
120
components/nif/record.hpp
Normal 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
|
181
components/nif/recordptr.hpp
Normal file
181
components/nif/recordptr.hpp
Normal 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
|
12
components/nif/tests/Makefile
Normal file
12
components/nif/tests/Makefile
Normal 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
|
30
components/nif/tests/nif_bsa_test.cpp
Normal file
30
components/nif/tests/nif_bsa_test.cpp
Normal 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);
|
||||
}
|
||||
}
|
232
components/nif/tests/niftool.cpp
Normal file
232
components/nif/tests/niftool.cpp
Normal 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;
|
||||
}
|
||||
}
|
5799
components/nif/tests/output/nif_bsa_test.out
Normal file
5799
components/nif/tests/output/nif_bsa_test.out
Normal file
File diff suppressed because it is too large
Load diff
18
components/nif/tests/test.sh
Executable file
18
components/nif/tests/test.sh
Executable 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
|
Loading…
Add table
Add a link
Reference in a new issue