|
|
|
#ifndef OPENMW_COMPONENTS_NIF_NODE_HPP
|
|
|
|
#define OPENMW_COMPONENTS_NIF_NODE_HPP
|
|
|
|
|
|
|
|
#include "controlled.hpp"
|
|
|
|
#include "extra.hpp"
|
|
|
|
#include "data.hpp"
|
|
|
|
#include "property.hpp"
|
|
|
|
#include "niftypes.hpp"
|
|
|
|
#include "controller.hpp"
|
|
|
|
#include "base.hpp"
|
|
|
|
#include "physics.hpp"
|
|
|
|
|
|
|
|
#include <components/misc/stringops.hpp>
|
|
|
|
|
|
|
|
namespace Nif
|
|
|
|
{
|
|
|
|
|
|
|
|
struct NiNode;
|
|
|
|
|
|
|
|
struct NiBoundingVolume
|
|
|
|
{
|
|
|
|
enum Type
|
|
|
|
{
|
|
|
|
SPHERE_BV = 0,
|
|
|
|
BOX_BV = 1,
|
|
|
|
CAPSULE_BV = 2,
|
|
|
|
LOZENGE_BV = 3,
|
|
|
|
UNION_BV = 4,
|
|
|
|
HALFSPACE_BV = 5
|
|
|
|
};
|
|
|
|
|
|
|
|
struct NiSphereBV
|
|
|
|
{
|
|
|
|
osg::Vec3f center;
|
|
|
|
float radius{0.f};
|
|
|
|
};
|
|
|
|
|
|
|
|
struct NiBoxBV
|
|
|
|
{
|
|
|
|
osg::Vec3f center;
|
|
|
|
Matrix3 axis;
|
|
|
|
osg::Vec3f extents;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct NiCapsuleBV
|
|
|
|
{
|
|
|
|
osg::Vec3f center, axis;
|
|
|
|
float extent{0.f}, radius{0.f};
|
|
|
|
};
|
|
|
|
|
|
|
|
struct NiLozengeBV
|
|
|
|
{
|
|
|
|
float radius{0.f}, extent0{0.f}, extent1{0.f};
|
|
|
|
osg::Vec3f center, axis0, axis1;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct NiHalfSpaceBV
|
|
|
|
{
|
|
|
|
osg::Vec3f center, normal;
|
|
|
|
};
|
|
|
|
|
|
|
|
unsigned int type;
|
|
|
|
NiSphereBV sphere;
|
|
|
|
NiBoxBV box;
|
|
|
|
NiCapsuleBV capsule;
|
|
|
|
NiLozengeBV lozenge;
|
|
|
|
std::vector<NiBoundingVolume> children;
|
|
|
|
NiHalfSpaceBV plane;
|
|
|
|
void read(NIFStream* nif)
|
|
|
|
{
|
|
|
|
type = nif->getUInt();
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case SPHERE_BV:
|
|
|
|
{
|
|
|
|
sphere.center = nif->getVector3();
|
|
|
|
sphere.radius = nif->getFloat();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case BOX_BV:
|
|
|
|
{
|
|
|
|
box.center = nif->getVector3();
|
|
|
|
box.axis = nif->getMatrix3();
|
|
|
|
box.extents = nif->getVector3();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CAPSULE_BV:
|
|
|
|
{
|
|
|
|
capsule.center = nif->getVector3();
|
|
|
|
capsule.axis = nif->getVector3();
|
|
|
|
capsule.extent = nif->getFloat();
|
|
|
|
capsule.radius = nif->getFloat();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LOZENGE_BV:
|
|
|
|
{
|
|
|
|
lozenge.radius = nif->getFloat();
|
|
|
|
lozenge.extent0 = nif->getFloat();
|
|
|
|
lozenge.extent1 = nif->getFloat();
|
|
|
|
lozenge.center = nif->getVector3();
|
|
|
|
lozenge.axis0 = nif->getVector3();
|
|
|
|
lozenge.axis1 = nif->getVector3();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case UNION_BV:
|
|
|
|
{
|
|
|
|
unsigned int numChildren = nif->getUInt();
|
|
|
|
if (numChildren == 0)
|
|
|
|
break;
|
|
|
|
children.resize(numChildren);
|
|
|
|
for (NiBoundingVolume& child : children)
|
|
|
|
child.read(nif);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case HALFSPACE_BV:
|
|
|
|
{
|
|
|
|
plane.center = nif->getVector3();
|
|
|
|
plane.normal = nif->getVector3();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
std::stringstream error;
|
|
|
|
error << "Unhandled NiBoundingVolume type: " << type;
|
|
|
|
nif->file->fail(error.str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** 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.
|
|
|
|
*/
|
|
|
|
struct Node : public Named
|
|
|
|
{
|
|
|
|
// Node flags. Interpretation depends somewhat on the type of node.
|
|
|
|
unsigned int flags;
|
|
|
|
Transformation trafo;
|
|
|
|
osg::Vec3f velocity; // Unused? Might be a run-time game state
|
|
|
|
PropertyList props;
|
|
|
|
|
|
|
|
// Bounding box info
|
|
|
|
bool hasBounds{false};
|
|
|
|
NiBoundingVolume bounds;
|
|
|
|
|
|
|
|
// Collision object info
|
|
|
|
NiCollisionObjectPtr collision;
|
|
|
|
|
|
|
|
void read(NIFStream *nif) override
|
|
|
|
{
|
|
|
|
Named::read(nif);
|
|
|
|
|
|
|
|
flags = nif->getBethVersion() <= 26 ? nif->getUShort() : nif->getUInt();
|
|
|
|
trafo = nif->getTrafo();
|
|
|
|
if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))
|
|
|
|
velocity = nif->getVector3();
|
|
|
|
if (nif->getBethVersion() <= NIFFile::BethVersion::BETHVER_FO3)
|
|
|
|
props.read(nif);
|
|
|
|
|
|
|
|
if (nif->getVersion() <= NIFStream::generateVersion(4,2,2,0))
|
|
|
|
hasBounds = nif->getBoolean();
|
|
|
|
if (hasBounds)
|
|
|
|
bounds.read(nif);
|
|
|
|
// Reference to the collision object in Gamebryo files.
|
|
|
|
if (nif->getVersion() >= NIFStream::generateVersion(10,0,1,0))
|
|
|
|
collision.read(nif);
|
|
|
|
|
|
|
|
parents.clear();
|
|
|
|
|
|
|
|
isBone = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void post(NIFFile *nif) override
|
|
|
|
{
|
|
|
|
Named::post(nif);
|
|
|
|
props.post(nif);
|
|
|
|
collision.post(nif);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parent node, or nullptr for the root node. As far as I'm aware, only
|
|
|
|
// NiNodes (or types derived from NiNodes) can be parents.
|
|
|
|
std::vector<NiNode*> parents;
|
|
|
|
|
|
|
|
bool isBone{false};
|
|
|
|
|
|
|
|
void setBone()
|
|
|
|
{
|
|
|
|
isBone = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct NiNode : Node
|
|
|
|
{
|
|
|
|
NodeList children;
|
|
|
|
NodeList effects;
|
|
|
|
|
|
|
|
enum Flags {
|
|
|
|
Flag_Hidden = 0x0001,
|
|
|
|
Flag_MeshCollision = 0x0002,
|
|
|
|
Flag_BBoxCollision = 0x0004,
|
|
|
|
Flag_ActiveCollision = 0x0020
|
|
|
|
};
|
|
|
|
enum BSAnimFlags {
|
|
|
|
AnimFlag_AutoPlay = 0x0020
|
|
|
|
};
|
|
|
|
enum BSParticleFlags {
|
|
|
|
ParticleFlag_AutoPlay = 0x0020,
|
|
|
|
ParticleFlag_LocalSpace = 0x0080
|
|
|
|
};
|
|
|
|
enum ControllerFlags {
|
|
|
|
ControllerFlag_Active = 0x8
|
|
|
|
};
|
|
|
|
enum BSPArrayController {
|
|
|
|
BSPArrayController_AtNode = 0x8,
|
|
|
|
BSPArrayController_AtVertex = 0x10
|
|
|
|
};
|
|
|
|
|
|
|
|
void read(NIFStream *nif) override
|
|
|
|
{
|
|
|
|
Node::read(nif);
|
|
|
|
children.read(nif);
|
|
|
|
if (nif->getBethVersion() < NIFFile::BethVersion::BETHVER_FO4)
|
|
|
|
effects.read(nif);
|
|
|
|
|
|
|
|
// Discard transformations for the root node, otherwise some meshes
|
|
|
|
// occasionally get wrong orientation. Only for NiNode-s for now, but
|
|
|
|
// can be expanded if needed.
|
|
|
|
// FIXME: if node 0 is *not* the only root node, this must not happen.
|
|
|
|
if (0 == recIndex && !Misc::StringUtils::ciEqual(name, "bip01"))
|
|
|
|
{
|
|
|
|
trafo = Nif::Transformation::getIdentity();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void post(NIFFile *nif) override
|
|
|
|
{
|
|
|
|
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]->parents.push_back(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct NiGeometry : 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct MaterialData
|
|
|
|
{
|
|
|
|
std::vector<std::string> names;
|
|
|
|
std::vector<int> extra;
|
|
|
|
unsigned int active{0};
|
|
|
|
bool needsUpdate{false};
|
|
|
|
void read(NIFStream *nif)
|
|
|
|
{
|
|
|
|
if (nif->getVersion() <= NIFStream::generateVersion(10,0,1,0))
|
|
|
|
return;
|
|
|
|
unsigned int num = 0;
|
|
|
|
if (nif->getVersion() <= NIFStream::generateVersion(20,1,0,3))
|
|
|
|
num = nif->getBoolean(); // Has Shader
|
|
|
|
else if (nif->getVersion() >= NIFStream::generateVersion(20,2,0,5))
|
|
|
|
num = nif->getUInt();
|
|
|
|
if (num)
|
|
|
|
{
|
|
|
|
nif->getStrings(names, num);
|
|
|
|
nif->getInts(extra, num);
|
|
|
|
}
|
|
|
|
if (nif->getVersion() >= NIFStream::generateVersion(20,2,0,5))
|
|
|
|
active = nif->getUInt();
|
|
|
|
if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS)
|
|
|
|
needsUpdate = nif->getBoolean();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
NiGeometryDataPtr data;
|
|
|
|
NiSkinInstancePtr skin;
|
|
|
|
MaterialData material;
|
|
|
|
BSShaderPropertyPtr shaderprop;
|
|
|
|
NiAlphaPropertyPtr alphaprop;
|
|
|
|
|
|
|
|
void read(NIFStream *nif) override
|
|
|
|
{
|
|
|
|
Node::read(nif);
|
|
|
|
data.read(nif);
|
|
|
|
skin.read(nif);
|
|
|
|
material.read(nif);
|
|
|
|
if (nif->getVersion() == NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)
|
|
|
|
{
|
|
|
|
shaderprop.read(nif);
|
|
|
|
alphaprop.read(nif);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void post(NIFFile *nif) override
|
|
|
|
{
|
|
|
|
Node::post(nif);
|
|
|
|
data.post(nif);
|
|
|
|
skin.post(nif);
|
|
|
|
shaderprop.post(nif);
|
|
|
|
alphaprop.post(nif);
|
|
|
|
if (recType != RC_NiParticles && !skin.empty())
|
|
|
|
nif->setUseSkinning(true);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct NiTriShape : NiGeometry {};
|
|
|
|
struct BSLODTriShape : NiTriShape
|
|
|
|
{
|
|
|
|
unsigned int lod0, lod1, lod2;
|
|
|
|
void read(NIFStream *nif) override
|
|
|
|
{
|
|
|
|
NiTriShape::read(nif);
|
|
|
|
lod0 = nif->getUInt();
|
|
|
|
lod1 = nif->getUInt();
|
|
|
|
lod2 = nif->getUInt();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
struct NiTriStrips : NiGeometry {};
|
|
|
|
struct NiLines : NiGeometry {};
|
|
|
|
struct NiParticles : NiGeometry { };
|
|
|
|
|
|
|
|
struct NiCamera : Node
|
|
|
|
{
|
|
|
|
struct Camera
|
|
|
|
{
|
|
|
|
unsigned short cameraFlags{0};
|
|
|
|
|
|
|
|
// Camera frustrum
|
|
|
|
float left, right, top, bottom, nearDist, farDist;
|
|
|
|
|
|
|
|
// Viewport
|
|
|
|
float vleft, vright, vtop, vbottom;
|
|
|
|
|
|
|
|
// Level of detail modifier
|
|
|
|
float LOD;
|
|
|
|
|
|
|
|
// Orthographic projection usage flag
|
|
|
|
bool orthographic{false};
|
|
|
|
|
|
|
|
void read(NIFStream *nif)
|
|
|
|
{
|
|
|
|
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
|
|
|
cameraFlags = nif->getUShort();
|
|
|
|
left = nif->getFloat();
|
|
|
|
right = nif->getFloat();
|
|
|
|
top = nif->getFloat();
|
|
|
|
bottom = nif->getFloat();
|
|
|
|
nearDist = nif->getFloat();
|
|
|
|
farDist = nif->getFloat();
|
|
|
|
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
|
|
|
orthographic = nif->getBoolean();
|
|
|
|
vleft = nif->getFloat();
|
|
|
|
vright = nif->getFloat();
|
|
|
|
vtop = nif->getFloat();
|
|
|
|
vbottom = nif->getFloat();
|
|
|
|
|
|
|
|
LOD = nif->getFloat();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Camera cam;
|
|
|
|
|
|
|
|
void read(NIFStream *nif) override
|
|
|
|
{
|
|
|
|
Node::read(nif);
|
|
|
|
|
|
|
|
cam.read(nif);
|
|
|
|
|
|
|
|
nif->getInt(); // -1
|
|
|
|
nif->getInt(); // 0
|
|
|
|
if (nif->getVersion() >= NIFStream::generateVersion(4,2,1,0))
|
|
|
|
nif->getInt(); // 0
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// A node used as the base to switch between child nodes, such as for LOD levels.
|
|
|
|
struct NiSwitchNode : public NiNode
|
|
|
|
{
|
|
|
|
unsigned int switchFlags{0};
|
|
|
|
unsigned int initialIndex{0};
|
|
|
|
|
|
|
|
void read(NIFStream *nif) override
|
|
|
|
{
|
|
|
|
NiNode::read(nif);
|
|
|
|
if (nif->getVersion() >= NIFStream::generateVersion(10,1,0,0))
|
|
|
|
switchFlags = nif->getUShort();
|
|
|
|
initialIndex = nif->getUInt();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct NiLODNode : public NiSwitchNode
|
|
|
|
{
|
|
|
|
osg::Vec3f lodCenter;
|
|
|
|
|
|
|
|
struct LODRange
|
|
|
|
{
|
|
|
|
float minRange;
|
|
|
|
float maxRange;
|
|
|
|
};
|
|
|
|
std::vector<LODRange> lodLevels;
|
|
|
|
|
|
|
|
void read(NIFStream *nif) override
|
|
|
|
{
|
|
|
|
NiSwitchNode::read(nif);
|
|
|
|
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFStream::generateVersion(10,0,1,0))
|
|
|
|
lodCenter = nif->getVector3();
|
|
|
|
else if (nif->getVersion() > NIFStream::generateVersion(10,0,1,0))
|
|
|
|
{
|
|
|
|
nif->skip(4); // NiLODData, unsupported at the moment
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int numLodLevels = nif->getUInt();
|
|
|
|
for (unsigned int i=0; i<numLodLevels; ++i)
|
|
|
|
{
|
|
|
|
LODRange r;
|
|
|
|
r.minRange = nif->getFloat();
|
|
|
|
r.maxRange = nif->getFloat();
|
|
|
|
lodLevels.push_back(r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct NiFltAnimationNode : public NiSwitchNode
|
|
|
|
{
|
|
|
|
float mDuration;
|
|
|
|
enum Flags
|
|
|
|
{
|
|
|
|
Flag_Swing = 0x40
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
void read(NIFStream *nif) override
|
|
|
|
{
|
|
|
|
NiSwitchNode::read(nif);
|
|
|
|
mDuration = nif->getFloat();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Abstract
|
|
|
|
struct NiAccumulator : Record
|
|
|
|
{
|
|
|
|
void read(NIFStream *nif) override {}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Node children sorters
|
|
|
|
struct NiClusterAccumulator : NiAccumulator {};
|
|
|
|
struct NiAlphaAccumulator : NiClusterAccumulator {};
|
|
|
|
|
|
|
|
struct NiSortAdjustNode : NiNode
|
|
|
|
{
|
|
|
|
enum SortingMode
|
|
|
|
{
|
|
|
|
SortingMode_Inherit,
|
|
|
|
SortingMode_Off,
|
|
|
|
SortingMode_Subsort
|
|
|
|
};
|
|
|
|
|
|
|
|
int mMode;
|
|
|
|
NiAccumulatorPtr mSubSorter;
|
|
|
|
void read(NIFStream *nif) override
|
|
|
|
{
|
|
|
|
NiNode::read(nif);
|
|
|
|
mMode = nif->getInt();
|
|
|
|
if (nif->getVersion() <= NIFStream::generateVersion(20,0,0,3))
|
|
|
|
mSubSorter.read(nif);
|
|
|
|
}
|
|
|
|
void post(NIFFile *nif) override
|
|
|
|
{
|
|
|
|
NiNode::post(nif);
|
|
|
|
mSubSorter.post(nif);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} // Namespace
|
|
|
|
#endif
|