diff --git a/nif/data.h b/nif/data.h index 867473203..4947104e2 100644 --- a/nif/data.h +++ b/nif/data.h @@ -82,5 +82,73 @@ struct NiSourceTexture : Named } }; +// Common ancestor for several data classes +struct ShapeData : Record +{ + FloatArray vertices, normals, colors, uvlist; + const Vector *center; + float radius; + + void read(NIFFile *nif) + { + int verts = nif->getUshort(); + + if(nif->getInt()) + vertices = nif->getFloatLen(verts*3); + + if(nif->getInt()) + normals = nif->getFloatLen(verts*3); + + center = nif->getVector(); + radius = nif->getFloat(); + + if(nif->getInt()) + colors = nif->getFloatLen(verts*4); + + int uvs = nif->getUshort(); + + // Only the first 6 bits are used as a count. I think the rest are + // flags of some sort. + uvs &= 0x3f; + + if(nif->getInt()) + uvlist = nif->getFloatLen(uvs*verts*2); + } +}; + +struct NiTriShapeData : ShapeData +{ + // Triangles, three vertex indices per triangle + SliceArray triangles; + + void read(NIFFile *nif) + { + ShapeData::read(nif); + + int tris = nif->getUshort(); + if(tris) + { + // We have three times as many vertices as triangles, so this + // is always equal to tris*3. + int cnt = nif->getInt(); + triangles = nif->getArrayLen(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(); + if(verts) + { + for(int i=0;igetUshort(); + nif->skip(num*sizeof(short)); + } + } + } +}; + } // Namespace #endif diff --git a/nif/nif_file.cpp b/nif/nif_file.cpp index 477c2a183..433a5572b 100644 --- a/nif/nif_file.cpp +++ b/nif/nif_file.cpp @@ -55,18 +55,90 @@ void NIFFile::parse() { SString rec = getString(); - cout << i << ": " << rec.toString() << endl; + //cout << i << ": " << rec.toString() << endl; Record *r; - // 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. - if(rec == "NiNode") r = new NiNode; + /* 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. + */ + + // NiNodes + if(rec == "NiNode" || rec == "AvoidNode" || + rec == "RootCollisionNode" || + rec == "NiBSParticleNode" || + rec == "NiBSAnimationNode" || + rec == "NiBillboardNode") r = new NiNode; + + // Other nodes else if(rec == "NiTriShape") r = new NiTriShape; + else if(rec == "NiRotatingParticles") r = new NiRotatingParticles; + else if(rec == "NiAutoNormalParticles") r = new NiAutoNormalParticles; + else if(rec == "NiCamera") r = new NiCamera; + + // Properties else if(rec == "NiTexturingProperty") r = new NiTexturingProperty; + else if(rec == "NiMaterialProperty") r = new NiMaterialProperty; + else if(rec == "NiZBufferProperty") r = new NiZBufferProperty; + else if(rec == "NiAlphaProperty") r = new NiAlphaProperty; + else if(rec == "NiVertexColorProperty") r = new NiVertexColorProperty; + else if(rec == "NiShadeProperty") r = new NiShadeProperty; + else if(rec == "NiDitherProperty") r = new NiDitherProperty; + else if(rec == "NiWireframeProperty") r = new NiWireframeProperty; + else if(rec == "NiSpecularProperty") r = new NiSpecularProperty; + + // Controllers + else if(rec == "NiVisController") r = new NiVisController; + else if(rec == "NiGeomMorpherController") r = new NiGeomMorpherController; + else if(rec == "NiKeyframeController") r = new NiKeyframeController; + else if(rec == "NiAlphaController") r = new NiAlphaController; + else if(rec == "NiUVController") r = new NiUVController; + else if(rec == "NiPathController") r = new NiPathController; + else if(rec == "NiMaterialColorController") r = new NiMaterialColorController; + else if(rec == "NiBSPArrayController") r = new NiBSPArrayController; + else if(rec == "NiParticleSystemController") r = new NiParticleSystemController; + + // Effects + else if(rec == "NiAmbientLight" || + rec == "NiDirectionalLight") r = new NiLight; + else if(rec == "NiTextureEffect") r = new NiTextureEffect; + + // Extra Data + else if(rec == "NiVertWeightsExtraData") r = new NiVertWeightsExtraData; + else if(rec == "NiTextKeyExtraData") r = new NiTextKeyExtraData; + else if(rec == "NiStringExtraData") r = new NiStringExtraData; + + else if(rec == "NiGravity") r = new NiGravity; + else if(rec == "NiPlanarCollider") r = new NiPlanarCollider; + else if(rec == "NiParticleGrowFade") r = new NiParticleGrowFade; + else if(rec == "NiParticleColorModifier") r = new NiParticleColorModifier; + else if(rec == "NiParticleRotation") r = new NiParticleRotation; + + // Data + else if(rec == "NiFloatData") r = new NiFloatData; + else if(rec == "NiTriShapeData") r = new NiTriShapeData; + else if(rec == "NiVisData") r = new NiVisData; + else if(rec == "NiColorData") r = new NiColorData; + else if(rec == "NiPixelData") r = new NiPixelData; + else if(rec == "NiMorphData") r = new NiMorphData; + else if(rec == "NiKeyframeData") r = new NiKeyframeData; + else if(rec == "NiSkinData") r = new NiSkinData; + else if(rec == "NiUVData") r = new NiUVData; + else if(rec == "NiPosData") r = new NiPosData; + else if(rec == "NiRotatingParticlesData") r = new NiRotatingParticlesData; + else if(rec == "NiAutoNormalParticlesData") r = new NiAutoNormalParticlesData; + + // Other + else if(rec == "NiSequenceStreamHelper") r = new NiSequenceStreamHelper; else if(rec == "NiSourceTexture") r = new NiSourceTexture; - else break; + else if(rec == "NiSkinInstance") r = new NiSkinInstance; + + // Failure + else + fail("Unknown record type " + rec.toString()); r->read(this); } diff --git a/nif/nif_file.h b/nif/nif_file.h index 229fae138..94b93658e 100644 --- a/nif/nif_file.h +++ b/nif/nif_file.h @@ -117,11 +117,15 @@ class NIFFile float getFloat() { return getType(); } char getByte() { return getType(); } + template + SliceArray getArrayLen(int num) + { return SliceArray((const X*)inp->getPtr(num*sizeof(X)),num); } + template SliceArray getArray() { int len = getInt(); - return SliceArray((const X*)inp->getPtr(len), len); + return getArrayLen(len); } SString getString() { return getArray(); } @@ -131,6 +135,9 @@ class NIFFile const Transformation *getTrafo() { return getPtr(); } const Vector4 *getVector4() { return getPtr(); } + FloatArray getFloatLen(int num) + { return getArrayLen(num); } + // For fixed-size strings where you already know the size const char *getString(int size) { return (const char*)inp->getPtr(size); } diff --git a/nif/property.h b/nif/property.h index d717dc600..fb3dac65d 100644 --- a/nif/property.h +++ b/nif/property.h @@ -130,5 +130,101 @@ struct NiTexturingProperty : Property } }; +// These contain no other data than the 'flags' field in Property +typedef Property NiShadeProperty; +typedef Property NiDitherProperty; +typedef Property NiZBufferProperty; +typedef Property NiSpecularProperty; +typedef Property NiWireframeProperty; + +// The rest are all struct-based +template +struct StructPropT : Property +{ + const Struct* data; + + void read(NIFFile *nif) + { + Property::read(nif); + data = nif->getPtr(); + } +}; + +struct S_MaterialProperty +{ + // The vector components are R,G,B + Vector ambient, diffuse, specular, emissive; + float glossiness, alpha; +}; + +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; +}; + +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; +}; + +typedef StructPropT NiAlphaProperty; +typedef StructPropT NiMaterialProperty; +typedef StructPropT NiVertexColorProperty; + } // Namespace #endif