forked from mirror/openmw-tes3mp
Cleaned away old nif code
parent
752237e245
commit
27ec79e33f
@ -1,211 +0,0 @@
|
||||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.snaptoad.com/
|
||||
|
||||
This file (base.d) 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/ .
|
||||
|
||||
*/
|
||||
|
||||
// Basic file structure
|
||||
module nif.base;
|
||||
|
||||
import nif.nif;
|
||||
import nif.niffile;
|
||||
import nif.misc;
|
||||
import monster.util.string;
|
||||
|
||||
//import nif.controller;
|
||||
//import nif.node;
|
||||
import nif.record;
|
||||
|
||||
debug(verbose) public import std.stdio;
|
||||
|
||||
void parseFile()
|
||||
{
|
||||
with(nifFile)
|
||||
{ // Read header
|
||||
int ver; // Hex-encoded version
|
||||
char[40] head; // Header string
|
||||
|
||||
// Read and check header string
|
||||
getString(head);
|
||||
|
||||
if(!begins(head,"NetImmerse File Format"))
|
||||
fail("Not a NetImmerse file");
|
||||
|
||||
debug(verbose) writefln("Header: \"%s\"", head[0..39]);
|
||||
debug(strict)
|
||||
if(head[0..39] != "NetImmerse File Format, Version 4.0.0.2" || head[39] != 0x0a)
|
||||
fail("Unrecognized header string, most likely an unsupported NIF version");
|
||||
|
||||
// Get binary coded version
|
||||
ver = getInt;
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
writef("BCD version tag: ");
|
||||
for(ubyte *b = (cast(ubyte*)&ver)+3; b > (cast(ubyte*)&ver); b--)
|
||||
writef(*b, ".");
|
||||
writefln(cast(ubyte)ver);
|
||||
}
|
||||
|
||||
if(ver != 0x04_00_00_02)
|
||||
fail("Don't know how to process this version");
|
||||
|
||||
// The number of records in the file
|
||||
nifMesh.records = nifRegion.allocateT!(Record)(getInt);
|
||||
debug(verbose) writefln("Number of records: ", nifMesh.records.length);
|
||||
|
||||
// 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.
|
||||
|
||||
// Besides giving me more work to do, this structure suggests some
|
||||
// obvious code optimizations we can now apply to the reading
|
||||
// structure. Since we now know in advance how many records there
|
||||
// are of each type, we could allocate the structures in bulk. D
|
||||
// doesn't allow this for classes directly, but we can make a
|
||||
// custom allocator and whatnot. I doubt we save a lot of time
|
||||
// with it, though. I'll save this for later.
|
||||
|
||||
// As for the record data itself, I do not know what has
|
||||
// changed. There are some new records. I might as well have
|
||||
// separate reading functions for the entire main structure.
|
||||
|
||||
// EDIT 14. feb 06: Since we are concentrating on Morrowind, let
|
||||
// us drop other versions for now. It would be nice to support
|
||||
// other versions (or even other mesh formats), but the priority
|
||||
// of this is very low at the moment.
|
||||
}
|
||||
|
||||
endFor:
|
||||
foreach(int i, ref Record o; nifMesh.records)
|
||||
{
|
||||
// Get type string, and update the NIFFiles error message to
|
||||
// match it.
|
||||
char[] recName = nifFile.getString;
|
||||
nifFile.setRec(i,recName);
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
writefln("\n%d: %s (%x)", i, recName, nifFile.position);
|
||||
}
|
||||
|
||||
switch(recName)
|
||||
{
|
||||
// These are the record types we know how to read
|
||||
|
||||
// Time controllers
|
||||
case "NiVisController": o = new NiVisController; break;
|
||||
case "NiGeomMorpherController": o = new NiGeomMorpherController; break;
|
||||
case "NiKeyframeController": o = new NiKeyframeController; break;
|
||||
case "NiAlphaController": o = new NiAlphaController; break;
|
||||
case "NiUVController": o = new NiUVController; break;
|
||||
case "NiPathController": o = new NiPathController; break;
|
||||
case "NiMaterialColorController": o = new NiMaterialColorController; break;
|
||||
case "NiBSPArrayController": o = new NiBSPArrayController; break;
|
||||
case "NiParticleSystemController": o = new NiParticleSystemController; break;
|
||||
|
||||
// NiNodes
|
||||
case "RootCollisionNode":
|
||||
case "NiBSParticleNode":
|
||||
case "NiBSAnimationNode":
|
||||
case "NiBillboardNode":
|
||||
case "AvoidNode":
|
||||
case "NiNode": o = new NiNode; break;
|
||||
|
||||
// Other nodes
|
||||
case "NiTriShape": o = new NiTriShape; break;
|
||||
case "NiRotatingParticles": o = new NiRotatingParticles; break;
|
||||
case "NiAutoNormalParticles": o = new NiAutoNormalParticles; break;
|
||||
case "NiCamera": o = new NiCamera; break;
|
||||
|
||||
// Effects
|
||||
case "NiAmbientLight":
|
||||
case "NiDirectionalLight": o = new Light; break;
|
||||
case "NiTextureEffect": o = new NiTextureEffect; break;
|
||||
|
||||
// Properties
|
||||
case "NiTexturingProperty": o = new NiTexturingProperty; break;
|
||||
case "NiMaterialProperty": o = new NiMaterialProperty; break;
|
||||
case "NiZBufferProperty": o = new NiZBufferProperty; break;
|
||||
case "NiAlphaProperty": o = new NiAlphaProperty; break;
|
||||
case "NiVertexColorProperty": o = new NiVertexColorProperty; break;
|
||||
case "NiShadeProperty": o = new NiShadeProperty; break;
|
||||
case "NiDitherProperty": o = new NiDitherProperty; break;
|
||||
case "NiWireframeProperty": o = new NiWireframeProperty; break;
|
||||
case "NiSpecularProperty": o = new NiSpecularProperty; break;
|
||||
|
||||
// Extra Data
|
||||
case "NiVertWeightsExtraData": o = new NiVertWeightsExtraData; break;
|
||||
case "NiTextKeyExtraData": o = new NiTextKeyExtraData; break;
|
||||
case "NiStringExtraData": o = new NiStringExtraData; break;
|
||||
|
||||
case "NiGravity": o = new NiGravity; break;
|
||||
case "NiPlanarCollider": o = new NiPlanarCollider; break;
|
||||
case "NiParticleGrowFade": o = new NiParticleGrowFade; break;
|
||||
case "NiParticleColorModifier": o = new NiParticleColorModifier; break;
|
||||
case "NiParticleRotation": o = new NiParticleRotation; break;
|
||||
|
||||
// Data
|
||||
case "NiFloatData": o = new NiFloatData; break;
|
||||
case "NiTriShapeData": o = new NiTriShapeData; break;
|
||||
case "NiVisData": o = new NiVisData; break;
|
||||
case "NiColorData": o = new NiColorData; break;
|
||||
case "NiPixelData": o = new NiPixelData; break;
|
||||
case "NiMorphData": o = new NiMorphData; break;
|
||||
case "NiKeyframeData": o = new NiKeyframeData; break;
|
||||
case "NiSkinData": o = new NiSkinData; break;
|
||||
case "NiUVData": o = new NiUVData; break;
|
||||
case "NiPosData": o = new NiPosData; break;
|
||||
case "NiRotatingParticlesData": o = new NiRotatingParticlesData; break;
|
||||
case "NiAutoNormalParticlesData": o = new NiAutoNormalParticlesData; break;
|
||||
|
||||
// Other
|
||||
case "NiSequenceStreamHelper": o = new NiSequenceStreamHelper; break;
|
||||
case "NiSourceTexture": o = new NiSourceTexture; break;
|
||||
case "NiSkinInstance": o = new NiSkinInstance; break;
|
||||
|
||||
default:
|
||||
nifFile.fail("Unknown record type " ~ recName);
|
||||
}
|
||||
|
||||
if(o !is null)
|
||||
{
|
||||
//nifFile.setRec(i,recName ~ " (" ~ o.toString ~ ")");
|
||||
o.read();
|
||||
}
|
||||
}
|
||||
// Clear error message.
|
||||
nifFile.setRec(-1,"");
|
||||
|
||||
// The 'footer', which it seems isn't always constant after all. I
|
||||
// have no clue what it is for though.
|
||||
int roots = nifFile.getInt;
|
||||
for(int i; i<roots;i++)
|
||||
nifFile.getInt();
|
||||
|
||||
debug(verbose) if(nifFile.eof()) writefln("End of file");
|
||||
|
||||
// A lot of files don't end where they should, so we just have to
|
||||
// accept it.
|
||||
//debug(check) if(!nifFile.eof()) nifFile.warn("End of file not reached");
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.snaptoad.com/
|
||||
|
||||
This file (controlled.d) 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/ .
|
||||
|
||||
*/
|
||||
|
||||
module nif.controlled;
|
||||
|
||||
import nif.record;
|
||||
import nif.extra;
|
||||
|
||||
// 'Controlled' is something that has a controller
|
||||
abstract class Controlled : Extra
|
||||
{
|
||||
Controller controller;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
debug(verbose) writef("Controller ");
|
||||
getIndex();
|
||||
}
|
||||
|
||||
void sortOut(Record[] list)
|
||||
{
|
||||
super.sortOut(list);
|
||||
controller = lookup!(Controller)(list);
|
||||
}
|
||||
}
|
||||
|
||||
// Record with name and controller/extra data link
|
||||
abstract class Named : Controlled
|
||||
{
|
||||
// Name of this object. This is used to refer to the object from .kf
|
||||
// files.
|
||||
char[] name;
|
||||
|
||||
override:
|
||||
char[] toString()
|
||||
{
|
||||
if(name.length)
|
||||
return super.toString() ~ " '" ~ name ~ "'";
|
||||
else
|
||||
return super.toString();
|
||||
}
|
||||
|
||||
void read()
|
||||
{
|
||||
//super.read(f);
|
||||
name = nifFile.getString();
|
||||
debug(verbose) writefln("Name: %s", name);
|
||||
super.read();
|
||||
}
|
||||
}
|
||||
|
||||
class NiSequenceStreamHelper : Named {}
|
@ -1,316 +0,0 @@
|
||||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.snaptoad.com/
|
||||
|
||||
This file (controller.d) 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/ .
|
||||
|
||||
*/
|
||||
|
||||
module nif.controller;
|
||||
|
||||
import nif.record;
|
||||
|
||||
// Time controller
|
||||
abstract class Controller : Record
|
||||
{
|
||||
Controller next;
|
||||
ushort flags;
|
||||
float frequency, phase; // Don't know what these do.
|
||||
float timeStart, timeStop;
|
||||
Controlled target;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{with(nifFile){
|
||||
super.read();
|
||||
|
||||
debug(verbose) writef("Next controller ");
|
||||
getIndex();
|
||||
|
||||
flags = getUshort;
|
||||
|
||||
frequency = getFloatIs(1);
|
||||
phase = getFloat;
|
||||
timeStart = getFloat;
|
||||
timeStop = getFloat;
|
||||
|
||||
debug(verbose) writef("Target ");
|
||||
getIndex();
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
writefln("Flags: %x", flags);
|
||||
writefln("Frequency: ", frequency);
|
||||
writefln("Phase: ", phase);
|
||||
writefln("Start time: ", timeStart);
|
||||
writefln("Stop time: ", timeStop);
|
||||
}
|
||||
}}
|
||||
|
||||
void sortOut(Record[] list)
|
||||
{
|
||||
super.sortOut(list);
|
||||
next = lookup!(Controller)(list);
|
||||
target = lookup!(Controlled)(list);
|
||||
}
|
||||
}
|
||||
|
||||
alias NiBSPArrayController NiParticleSystemController;
|
||||
|
||||
class NiBSPArrayController : Controller
|
||||
{
|
||||
override:
|
||||
void read()
|
||||
{with(nifFile){
|
||||
super.read();
|
||||
|
||||
// At the moment, just skip it all
|
||||
|
||||
//*
|
||||
// 23 floats = 92 bytes
|
||||
// 3 ints = 12 bytes
|
||||
// 3 shorts = 6 bytes
|
||||
// 1 byte = 1 byte
|
||||
// Total: 111 bytes
|
||||
seekCur(111);
|
||||
|
||||
short s = wgetShort;
|
||||
|
||||
seekCur(15 + s*40);
|
||||
/*/
|
||||
float speed, speedRandom, angle, angleRandom;
|
||||
|
||||
speed = wgetFloat;
|
||||
speedRandom = wgetFloat;
|
||||
angle = wgetFloat;
|
||||
angleRandom = wgetFloat;
|
||||
|
||||
wgetFloat(); // Unknown
|
||||
wgetFloat(); // Sometimes pi
|
||||
|
||||
for(int i; i<10; i++)
|
||||
wgetFloat();
|
||||
|
||||
wgetByte();
|
||||
|
||||
wgetFloat();
|
||||
wgetFloat();
|
||||
wgetFloat();
|
||||
|
||||
wgetShort();
|
||||
|
||||
wgetVector();
|
||||
|
||||
debug(verbose) writef("Emitter: ");
|
||||
wgetInt();
|
||||
|
||||
wgetShort();
|
||||
|
||||
wgetFloat();
|
||||
|
||||
wgetInt();
|
||||
wgetInt();
|
||||
|
||||
wgetShort();
|
||||
|
||||
debug(verbose) writefln("begin particle group");
|
||||
short s = wgetShort;
|
||||
wgetShort();
|
||||
for(short i; i<s; i++)
|
||||
{
|
||||
Matrix m;
|
||||
getMatrix(m);
|
||||
debug(verbose) writefln("Matrix: ", m.toString);
|
||||
wgetShort();
|
||||
wgetShort();
|
||||
}
|
||||
debug(verbose) writefln("end particle group");
|
||||
|
||||
debug(verbose) writefln("Links:");
|
||||
wgetInt();
|
||||
wgetInt();
|
||||
wgetInt();
|
||||
wgetByte();
|
||||
//*/
|
||||
}}
|
||||
}
|
||||
|
||||
class NiMaterialColorController : Controller
|
||||
{
|
||||
NiPosData data;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
debug(verbose) writef("PosData ");
|
||||
getIndex();
|
||||
}
|
||||
|
||||
void sortOut(Record[] list)
|
||||
{
|
||||
super.sortOut(list);
|
||||
data = lookup!(NiPosData)(list);
|
||||
}
|
||||
}
|
||||
|
||||
class NiPathController : Controller
|
||||
{
|
||||
NiPosData posData;
|
||||
NiFloatData floatData;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{with(nifFile){
|
||||
super.read();
|
||||
|
||||
wgetIntIs(1);
|
||||
wgetFloat();
|
||||
wgetFloat();
|
||||
wgetShortIs(0,1);
|
||||
debug(verbose) writef("Pos Data ");
|
||||
getIndex();
|
||||
debug(verbose) writef("Float Data ");
|
||||
getIndex();
|
||||
}}
|
||||
|
||||
void sortOut(Record[] list)
|
||||
{
|
||||
super.sortOut(list);
|
||||
posData = lookup!(NiPosData)(list);
|
||||
floatData = lookup!(NiFloatData)(list);
|
||||
}
|
||||
}
|
||||
|
||||
class NiUVController : Controller
|
||||
{
|
||||
NiUVData data;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
short s = nifFile.getShortIs(0);
|
||||
|
||||
debug(verbose) writef("UV Data ");
|
||||
getIndex();
|
||||
}
|
||||
|
||||
void sortOut(Record[] list)
|
||||
{
|
||||
super.sortOut(list);
|
||||
data = lookup!(NiUVData)(list);
|
||||
}
|
||||
}
|
||||
|
||||
// If there was more than four of these, I would template it.
|
||||
class NiKeyframeController : Controller
|
||||
{
|
||||
NiKeyframeData data;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
debug(check)
|
||||
if(flags & 0xf0) nifFile.warn("Unknown flags");
|
||||
|
||||
debug(verbose) writef("KeyframeData ");
|
||||
getIndex();
|
||||
// If this index is empty, timeStart/timeStop are set to +/-
|
||||
// float.max.
|
||||
}
|
||||
|
||||
void sortOut(Record[] list)
|
||||
{
|
||||
super.sortOut(list);
|
||||
data = lookup!(NiKeyframeData)(list);
|
||||
}
|
||||
}
|
||||
|
||||
class NiAlphaController : Controller
|
||||
{
|
||||
NiFloatData data;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
debug(controllerCheck)
|
||||
if(flags != 12) nifFile.warn("Unknown flags");
|
||||
|
||||
debug(verbose) writef("Float Data ");
|
||||
getIndex();
|
||||
}
|
||||
|
||||
void sortOut(Record[] list)
|
||||
{
|
||||
super.sortOut(list);
|
||||
data = lookup!(NiFloatData)(list);
|
||||
}
|
||||
}
|
||||
|
||||
class NiGeomMorpherController : Controller
|
||||
{
|
||||
NiMorphData data;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
debug(controllerCheck)
|
||||
if(flags != 12) nifFile.warn("Unknown flags");
|
||||
|
||||
debug(verbose) writef("Morph Data ");
|
||||
getIndex();
|
||||
|
||||
byte b = nifFile.wgetByteIs(0);
|
||||
}
|
||||
|
||||
void sortOut(Record[] list)
|
||||
{
|
||||
super.sortOut(list);
|
||||
data = lookup!(NiMorphData)(list);
|
||||
}
|
||||
}
|
||||
|
||||
class NiVisController : Controller
|
||||
{
|
||||
NiVisData data;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
debug(controllerCheck)
|
||||
if(flags != 12) nifFile.warn("Unknown flags");
|
||||
|
||||
debug(verbose) writef("Vis Data ");
|
||||
getIndex();
|
||||
}
|
||||
|
||||
void sortOut(Record[] list)
|
||||
{
|
||||
super.sortOut(list);
|
||||
data = lookup!(NiVisData)(list);
|
||||
}
|
||||
}
|
||||
|
@ -1,816 +0,0 @@
|
||||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.snaptoad.com/
|
||||
|
||||
This file (data.d) 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/ .
|
||||
|
||||
*/
|
||||
|
||||
module nif.data;
|
||||
import nif.record;
|
||||
|
||||
class NiSourceTexture : Named
|
||||
{
|
||||
byte external;
|
||||
union
|
||||
{
|
||||
char[] filename;
|
||||
NiPixelData data;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
override:
|
||||
void sortOut(Record[] list)
|
||||
{
|
||||
super.sortOut(list);
|
||||
if(!external)
|
||||
data = lookup!(NiPixelData)(list);
|
||||
}
|
||||
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
external = nifFile.getByteIs(0,1);
|
||||
debug(verbose) writefln("Use external file: ", external);
|
||||
|
||||
if(external)
|
||||
{
|
||||
filename = nifFile.getString;
|
||||
debug(verbose) writefln("Filename: ", filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
nifFile.wgetByteIs(1);
|
||||
debug(verbose) writef("Pixel Data ");
|
||||
getIndex();
|
||||
}
|
||||
|
||||
pixel = nifFile.getInt;
|
||||
mipmap = nifFile.getInt;
|
||||
alpha = nifFile.getIntIs(3);
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
writefln("Pixel layout: ", pixel);
|
||||
writefln("Mipmap: ", mipmap);
|
||||
writefln("Alpha: ", alpha);
|
||||
}
|
||||
|
||||
nifFile.wgetByteIs(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Common ancestor for several classes
|
||||
class ShapeData : Record
|
||||
{
|
||||
float[] vertices, normals, colors, uvlist;
|
||||
Vector center;
|
||||
float radius;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
short verts = nifFile.getShort;
|
||||
debug(verbose) writefln("Vertices: ", verts);
|
||||
|
||||
if(nifFile.getInt != 0)
|
||||
{
|
||||
debug(verbose) writefln("Reading vertices");
|
||||
if(verts == 0) nifFile.warn("Empty vertex array");
|
||||
vertices = nifFile.getArraySize!(float)(verts*3);
|
||||
}
|
||||
else nifFile.warn("No vertices found");
|
||||
|
||||
if(nifFile.getInt != 0)
|
||||
{
|
||||
debug(verbose) writefln("Reading normals");
|
||||
normals = nifFile.getArraySize!(float)(verts*3);
|
||||
}
|
||||
|
||||
center = nifFile.getVector();
|
||||
radius = nifFile.getFloat();
|
||||
debug(verbose)
|
||||
{
|
||||
writefln("Center: ", center.toString);
|
||||
writefln("Radius: ", radius);
|
||||
}
|
||||
|
||||
if(nifFile.getInt != 0)
|
||||
{
|
||||
debug(verbose) writefln("Reading vertex colors");
|
||||
colors = nifFile.getArraySize!(float)(verts*4);
|
||||
}
|
||||
|
||||
short uvs = nifFile.getShort;
|
||||
debug(verbose) writefln("Number of UV sets: ", uvs);
|
||||
|
||||
if(nifFile.getInt != 0)
|
||||
{
|
||||
if(uvs == 0) nifFile.warn("Empty UV list");
|
||||
|
||||
// Only the 6 first bits are used as a count, the rest are
|
||||
// (unknown) flags.
|
||||
if(uvs > 0b111111)
|
||||
{
|
||||
nifFile.warn("UV count contains (unknown) flags");
|
||||
uvs = cast(short)(uvs & 0b111111);
|
||||
}
|
||||
|
||||
uvlist = nifFile.getArraySize!(float)(uvs*verts*2);
|
||||
}
|
||||
else if(uvs != 0) nifFile.warn("UV list was unexpectedly missing");
|
||||
}
|
||||
}
|
||||
|
||||
class NiAutoNormalParticlesData : ShapeData
|
||||
{
|
||||
ushort activeCount;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
nifFile.assertWarn(uvlist.length == 0, "UV coordinates are not expected in this record");
|
||||
|
||||
debug(verbose) writef("Active vertices: ");
|
||||
|
||||
// Number of active vertices (always matches count?)
|
||||
activeCount = nifFile.wgetUshortIs(cast(ushort)(vertices.length/3));
|
||||
|
||||
nifFile.wgetFloat(); // Active radius (?)
|
||||
nifFile.wgetShort(); // Number of valid entries in the following arrays
|
||||
|
||||
//writefln("%x", nifFile.position);
|
||||
if(nifFile.wgetInt == 0) nifFile.warn("Particle size list is missing");
|
||||
else
|
||||
for(int i; i<activeCount; i++)
|
||||
{
|
||||
float fl = nifFile.getFloat; // Particle sizes
|
||||
debug(veryverbose)
|
||||
writefln(" %d: ", i, fl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NiRotatingParticlesData : NiAutoNormalParticlesData
|
||||
{
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
nifFile.assertWarn(normals.length == 0, "Normals are not expected in this record");
|
||||
|
||||
Vector4 v;
|
||||
|
||||
if(nifFile.wgetInt == 0) nifFile.warn("Rotation data is not present");
|
||||
else
|
||||
// This is either activeCount or vertices.length/3, until I
|
||||
// find a file in which the two differs (if one exists), we
|
||||
// can't know which is correct.
|
||||
for(int i; i<activeCount; i++)
|
||||
{
|
||||
v = nifFile.getVector4; // Quaternions
|
||||
debug(veryverbose)
|
||||
writefln(" %d: ", i, v.toString);
|
||||
}
|
||||
//writefln("%x", nifFile.position);
|
||||
}
|
||||
}
|
||||
|
||||
class NiPosData : Record
|
||||
{
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
int count = nifFile.getInt;
|
||||
debug(verbose) writefln("Count: ", count);
|
||||
int type = nifFile.wgetIntIs(1,2);
|
||||
for(int i; i<count; i++)
|
||||
{
|
||||
// TODO: Make a generalized struct of this? Seems to be the
|
||||
// same as in NiKeyframeData.
|
||||
float time = nifFile.getFloat;
|
||||
debug(verbose) writef("Time %f: ", time);
|
||||
Vector v;
|
||||
if(type == 1)
|
||||
{
|
||||
v = nifFile.getVector;
|
||||
debug(verbose) writef(v.toString);
|
||||
}
|
||||
else if(type == 2)
|
||||
{
|
||||
v = nifFile.getVector;
|
||||
debug(verbose) writef(v.toString, " ");
|
||||
v = nifFile.getVector;
|
||||
debug(verbose) writef(v.toString, " ");
|
||||
v = nifFile.getVector;
|
||||
debug(verbose) writef(v.toString);
|
||||
}
|
||||
else nifFile.fail("Unknown type");
|
||||
debug(verbose) writefln();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NiUVData : Record
|
||||
{
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
debug(verbose) writef("Count: ");
|
||||
int count = nifFile.wgetInt();
|
||||
|
||||
// TODO: This is claimed to be a "float animation key", which is
|
||||
// also used in FloatData and KeyframeData
|
||||
if(count != 5 && count != 3 && count != 2 && count != 0)
|
||||
nifFile.warn("Untested count");
|
||||
|
||||
if(count)
|
||||
{
|
||||
nifFile.wgetIntIs(2);
|
||||
debug(verbose) writefln("%d entries in list 1:", count);
|
||||
for(int i; i<count; i++)
|
||||
{
|
||||
float time = nifFile.getFloat;
|
||||
Vector v = nifFile.getVector;
|
||||
debug(verbose)
|
||||
writefln(" Time %f: ", time, v.toString);
|
||||
}
|
||||
}
|
||||
|
||||
count = nifFile.getInt;
|
||||
|
||||
if(count)
|
||||
{
|
||||
nifFile.wgetIntIs(2);
|
||||
debug(verbose) writefln("%d entries in list 2:", count);
|
||||
for(int i; i<count; i++)
|
||||
{
|
||||
float time = nifFile.getFloat;
|
||||
Vector v = nifFile.getVector;
|
||||
debug(verbose)
|
||||
writefln(" Time %f: ", time, v.toString);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
nifFile.getIntIs(0);
|
||||
nifFile.getIntIs(0);
|
||||
}
|
||||
}
|
||||
|
||||
class NiPixelData : Record
|
||||
{
|
||||
uint rmask, gmask, bmask, amask;
|
||||
int bpp;
|
||||
int mips;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
nifFile.wgetIntIs(0,1);
|
||||
rmask = nifFile.getUintIs(0xff);
|
||||
gmask = nifFile.getUintIs(0xff00);
|
||||
bmask = nifFile.getUintIs(0xff0000);
|
||||
amask = nifFile.getUintIs(0xff000000,0);
|
||||
|
||||
bpp = nifFile.getIntIs(24,32);
|
||||
|
||||
nifFile.wgetByte();
|
||||
|
||||
nifFile.wgetByteIs(8);
|
||||
nifFile.wgetUbyteIs(130);
|
||||
|
||||
nifFile.wgetByteIs(0,32);
|
||||
|
||||
nifFile.wgetByteIs(0);
|
||||
nifFile.wgetByteIs(65);
|
||||
|
||||
nifFile.wgetByteIs(0,12);
|
||||
|
||||
nifFile.wgetByteIs(0);
|
||||
nifFile.wgetIntIs(-1);
|
||||
|
||||
mips = nifFile.getInt;
|
||||
int bytes = nifFile.getIntIs(bpp>>3);
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
writefln("Red mask %8xh", rmask);
|
||||
writefln("Green mask %8xh", gmask);
|
||||
writefln("Blue mask %8xh", bmask);
|
||||
writefln("Alpha mask %8xh", amask);
|
||||
writefln("Bits per pixel: ", bpp);
|
||||
writefln("Number of mipmaps: ", mips);
|
||||
writefln("Bytes per pixel: ", bytes);
|
||||
}
|
||||
|
||||
for(int i; i<mips; i++)
|
||||
{
|
||||
int x = nifFile.getInt;
|
||||
int y = nifFile.getInt;
|
||||
uint offset = nifFile.getUint;
|
||||
debug(verbose)
|
||||
{
|
||||
writefln("Mipmap %d:", i);
|
||||
writefln(" Size: %dx%d", x, y);
|
||||
writefln(" Offset in data field: %xh", offset);
|
||||
}
|
||||
}
|
||||
|
||||
uint dataSize = nifFile.getUint;
|
||||
nifFile.seekCur(dataSize);
|
||||
}
|
||||
}
|
||||
|
||||
class NiColorData : Record
|
||||
{
|
||||
struct ColorData
|
||||
{
|
||||
float time, R, G, B, A;
|
||||
}
|
||||
static assert(ColorData.sizeof == 20);
|
||||
|
||||
ColorData colors[];
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
int count = nifFile.getInt;
|
||||
nifFile.getIntIs(1);
|
||||
colors = nifFile.getArraySize!(ColorData)(count);
|
||||
}
|
||||
}
|
||||
|
||||
class NiVisData : Record
|
||||
{
|
||||
align(1)
|
||||
struct VisData
|
||||
{
|
||||
float time;
|
||||
byte isSet;
|
||||
|
||||
static assert(VisData.sizeof == 5);
|
||||
}
|
||||
|
||||
VisData vis[];
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
int count = nifFile.getInt;
|
||||
vis = nifFile.getArraySize!(VisData)(count);
|
||||
debug(check)
|
||||
foreach(VisData v; vis)
|
||||
if(v.isSet!=0 && v.isSet!=1) nifFile.warn("Unknown bool value");
|
||||
}
|
||||
}
|
||||
|
||||
class NiFloatData : Record
|
||||
{
|
||||
struct FloatData
|
||||
{
|
||||
float time;
|
||||
float[3] alpha;
|
||||
}
|
||||
static assert(FloatData.sizeof == 16);
|
||||
|
||||
FloatData data[];
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
int cnt = nifFile.getInt;
|
||||
|
||||
nifFile.getIntIs(2);
|
||||
|
||||
data = nifFile.getArraySize!(FloatData)(cnt);
|
||||
|
||||
// This might be the "keyfloat" type, ie. a value and forward
|
||||
// and backward tangents. Might be used for animating in the
|
||||
// alpha space?
|
||||
debug(verbose)
|
||||
foreach(FloatData d; data)
|
||||
writefln("Time: %f Alpha: %f, %f, %f", d.time,
|
||||
d.alpha[0], d.alpha[1], d.alpha[2]);
|
||||
}
|
||||
}
|
||||
|
||||
class NiSkinData : Record
|
||||
{
|
||||
align(1)
|
||||
struct Weight
|
||||
{
|
||||
ushort vertex;
|
||||
float weight;
|
||||
|
||||
static assert(Weight.sizeof == 6);
|
||||
}
|
||||
|
||||
Weight weights[][];
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
Matrix m;
|
||||
Vector v;
|
||||
Vector4 v4;
|
||||
|
||||
nifFile.getMatrix(m);
|
||||
v = nifFile.getVector();
|
||||
nifFile.getFloatIs(1);
|
||||
debug(verbose)
|
||||
{
|
||||
writefln("Matrix:\n", m.toString);
|
||||
writefln("Vector: ", v.toString);
|
||||
writefln("Float: 1 (always)");
|
||||
}
|
||||
|
||||
int count = nifFile.getInt;
|
||||
debug(verbose) writefln("Bones: ", count);
|
||||
nifFile.getIntIs(-1);
|
||||
|
||||
nifFile.fitArray(70,count);
|
||||
|
||||
weights = nifRegion.allocateT!(Weight[])(count);
|
||||
|
||||
foreach(int i, ref Weight[] wl; weights)
|
||||
{
|
||||
nifFile.getMatrix(m); // Rotation offset of the skin from this
|
||||
// bone in bind position.
|
||||
v = nifFile.getVector(); // Translation -ditto-
|
||||
nifFile.getFloatIs(1); // Scale -ditto- ?
|
||||
debug(verbose)
|
||||
{
|
||||
writefln("Bone #%d:", i);
|
||||
writefln(" Rotation:\n", m.toString);
|
||||
writefln(" Translation: ", v.toString);
|
||||
writefln(" Scale: 1 (always)");
|
||||
}
|
||||
v4 = nifFile.getVector4;
|
||||
|
||||
// Number of vertex weights
|
||||
ushort cnt2 = nifFile.getUshort;
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
writefln(" 4-Vector: ", v4.toString);
|
||||
writefln(" Number of vertex weights: ", cnt2);
|
||||
}
|
||||
|
||||
wl = nifFile.getArraySize!(Weight)(cnt2);
|
||||
|
||||
debug(veryverbose)
|
||||
foreach(ref Weight w; wl)
|
||||
writefln(" %d: ", w.vertex, w.weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NiTriShapeData : ShapeData
|
||||
{
|
||||
short[] triangles;
|
||||
short[][] matches;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
ushort verts = cast(ushort)(vertices.length/3);
|
||||
|
||||
short tris = nifFile.getShort;
|
||||
debug(verbose) writefln("Number of faces: ", tris);
|
||||
if(tris)
|
||||
{
|
||||
// Must have three times as many vertices as triangles
|
||||
int cnt = nifFile.getIntIs(tris*3);
|
||||
triangles = nifFile.getArraySize!(short)(cnt);
|
||||
}
|
||||
|
||||
short match = nifFile.getShort;
|
||||
if(match)
|
||||
{
|
||||
nifFile.fitArray(match,2);
|
||||
matches = nifRegion.allocateT!(short[])(match);
|
||||
if(match != verts) nifFile.warn("Expected verts and match to be equal");
|
||||
foreach(ref short[] s; matches)
|
||||
{
|
||||
short sh = nifFile.getShort;
|
||||
if(sh>=verts || sh < 0)
|
||||
nifFile.warn("Presumed vertex index was out of bounds");
|
||||
s = nifFile.getArraySize!(short)(sh);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NiMorphData : Record
|
||||
{
|
||||
//float[] vertexList;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
int morphCount = nifFile.getInt;
|
||||
|
||||
int vertCount = nifFile.getInt;
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
writefln("Number of morphs: ", morphCount);
|
||||
writefln("Vertices: ", vertCount);
|
||||
}
|
||||
|
||||
nifFile.wgetByteIs(1);
|
||||
|
||||
//nifFile.getIntIs(0);
|
||||
//nifFile.getIntIs(1);
|
||||
//vertexList = nifFile.getArraySize!(float)(3*vertCount);
|
||||
|
||||
for(int i=0; i<morphCount; i++)
|
||||
{
|
||||
int magic = nifFile.getInt;
|
||||
|
||||
debug(veryverbose)
|
||||
{
|
||||
writefln("Getting morph batch %d", i);
|
||||
writefln(" Number of 4-vectors ", magic);
|
||||
}
|
||||
|
||||
nifFile.wgetIntIs(0,1,2); // Is always 1 for i=0, 0 or 2 otherwise
|
||||
|
||||
float[] l;
|
||||
|
||||
// Time, data, forward, backward tangents
|
||||
if(magic)
|
||||
l = nifFile.getArraySize!(float)(magic*4);
|
||||
|
||||
debug(veryverbose)
|
||||
for(int j; j<magic; j++)
|
||||
writefln(" Time %f : [", l[4*j], l[4*j+1],",", l[4*j+2],",", l[4*j+3],"]");
|
||||
|
||||
l = nifFile.getArraySize!(float)(vertCount*3);
|
||||
|
||||
debug(veryverbose)
|
||||
for(int j; j<vertCount; j++)
|
||||
writefln(" Vertex morph %d: ", j, " [%f, %f, %f]",
|
||||
l[j*3], l[j*3+1], l[j*3+2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NiKeyframeData : Record
|
||||
{
|
||||
// These should be moved out of this class, given their own reader
|
||||
// functions, and used elsewhere. But it currently works, and I
|
||||
// haven't really confirmed that these ARE used elsewhere, yet.
|
||||
|
||||
struct Rotation1
|
||||
{
|
||||
float time;
|
||||
float[4] quaternion;
|
||||
|
||||
static assert(Rotation1.sizeof == 5*4);
|
||||
}
|
||||
|
||||
struct Rotation3
|
||||
{
|
||||
float time;
|
||||
float[4] quaternion;
|
||||
float tension;
|
||||
float bias;
|
||||
float continuity;
|
||||
|
||||
static assert(Rotation3.sizeof == 8*4);
|
||||
}
|
||||
|
||||
struct Rotation4
|
||||
{
|
||||
float time;
|
||||
|
||||
struct R
|
||||
{
|
||||
struct R1
|
||||
{
|
||||
float time;
|
||||
float unknown;
|
||||
|
||||
static assert(R1.sizeof == 8);
|
||||
}
|
||||
|
||||
struct R2
|
||||
{
|
||||
float time;
|
||||
float unknown[3];
|
||||
|
||||
static assert(R2.sizeof == 16);
|
||||
}
|
||||
|
||||
int type;
|
||||
union
|
||||
{
|
||||
R1[] r1;
|
||||
R2[] r2;
|
||||
}
|
||||
}
|
||||
|
||||
R r3[3];
|
||||
}
|
||||
|
||||
struct Translation1
|
||||
{
|
||||
float time;
|
||||
float[3] translation;
|
||||
static assert(Translation1.sizeof==4*4);
|
||||
}
|
||||
struct Translation2
|
||||
{
|
||||
float time;
|
||||
float[3] translation;
|
||||
float[3] forward;
|
||||
float[3] backward;
|
||||
static assert(Translation2.sizeof==4*10);
|
||||
}
|
||||
struct Translation3
|
||||
{
|
||||
float time;
|
||||
float[3] translation;
|
||||
float tension;
|
||||
float bias;
|
||||
float continuity;
|
||||
static assert(Translation3.sizeof==4*7);
|
||||
}
|
||||
|
||||
struct Scale1
|
||||
{
|
||||
float time;
|
||||
float scale;
|
||||
static assert(Scale1.sizeof == 4*2);
|
||||
}
|
||||
struct Scale2
|
||||
{
|
||||
float time;
|
||||
float scale;
|
||||
float forward;
|
||||
float backward;
|
||||
static assert(Scale2.sizeof == 4*4);
|
||||
}
|
||||
struct Scale3
|
||||
{
|
||||
float time;
|
||||
float scale;
|
||||
float tension;
|
||||
float bias;
|
||||
float continuity;
|
||||
static assert(Scale3.sizeof == 4*5);
|
||||
}
|
||||
|
||||
// Data arrays. Only one of each type will be used. I used to have
|
||||
// them in unions, but then I realized that was just asking for trouble.
|
||||
|
||||
Rotation1 rot1[];
|
||||
Rotation3 rot3[];
|
||||
Rotation4 rot4[];
|
||||
|
||||
Translation1 tra1[];
|
||||
Translation2 tra2[];
|
||||
Translation3 tra3[];
|
||||
|
||||
Scale1 sca1[];
|
||||
Scale2 sca2[];
|
||||
Scale3 sca3[];
|
||||
|
||||
int rotType, traType, scaleType;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
int count;
|
||||
|
||||
count = nifFile.getInt;
|
||||
debug(verbose) writefln("Number of rotations: ", count);
|
||||
|
||||
if(count)
|
||||
{
|
||||
rotType = nifFile.getInt;
|
||||
|
||||
if(rotType == 1)
|
||||
rot1 = nifFile.getArraySize!(Rotation1)(count);
|
||||
else if(rotType == 3)
|
||||
rot3 = nifFile.getArraySize!(Rotation3)(count);
|
||||
else if(rotType == 4)
|
||||
{
|
||||
if(count!=1) nifFile.warn("Rotation type 4, but count was not 1, it was "
|
||||
~ .toString(count));
|
||||
nifFile.fitArray(count,6*4); // Minimum size
|
||||
rot4 = nifRegion.allocateT!(Rotation4)(count);
|
||||
|
||||
foreach(ref Rotation4 rr; rot4)
|
||||
{
|
||||
rr.time = nifFile.getFloat;
|
||||
foreach(ref rr.R r; rr.r3)
|
||||
{
|
||||
int cnt = nifFile.getInt;
|
||||
r.type = nifFile.getInt;
|
||||
if(r.type == 1)
|
||||
nifFile.getArraySize!(r.R1)(cnt);
|
||||
else if(r.type == 2)
|
||||
nifFile.getArraySize!(r.R2)(cnt);
|
||||
else nifFile.fail("Unknown rotation subtype " ~ .toString(r.type));
|
||||
}
|
||||
}
|
||||
}
|
||||
else nifFile.fail("Don't know how to handle rotation type " ~ .toString(rotType));
|
||||
} // End of rotations
|
||||
|
||||
count = nifFile.getInt;
|
||||
debug(verbose) writefln("Number of translations: ", count);
|
||||
|
||||
if(count)
|
||||
{
|
||||
traType = nifFile.getInt;
|
||||
|
||||
if(traType == 1)
|
||||
tra1 = nifFile.getArraySize!(Translation1)(count);
|
||||
else if(traType == 2)
|
||||
tra2 = nifFile.getArraySize!(Translation2)(count);
|
||||
else if(traType == 3)
|
||||
tra3 = nifFile.getArraySize!(Translation3)(count);
|
||||
else nifFile.fail("Don't know how to handle translation type "
|
||||
~ .toString(traType));
|
||||
} // End of translations
|
||||
|
||||
count = nifFile.getInt;
|
||||
debug(verbose) writefln("Number of scalings: ", count);
|
||||
|
||||
if(count)
|
||||
{
|
||||
int scaleType = nifFile.getInt;
|
||||
if(scaleType == 1) sca1 = nifFile.getArraySize!(Scale1)(count);
|
||||
else if(scaleType == 2) sca2 = nifFile.getArraySize!(Scale2)(count);
|
||||
else if(scaleType == 3) sca3 = nifFile.getArraySize!(Scale3)(count);
|
||||
else nifFile.fail("Don't know how to handle scaling type "
|
||||
~ .toString(scaleType));
|
||||
} // End of scalings
|
||||
|
||||
}
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.snaptoad.com/
|
||||
|
||||
This file (effect.d) 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/ .
|
||||
|
||||
*/
|
||||
|
||||
module nif.effect;
|
||||
import nif.record;
|
||||
|
||||
class Effect : Node {}
|
||||
|
||||
// Used for NiAmbientLight and NiDirectionalLight. Might also work for
|
||||
// NiPointLight and NiSpotLight?
|
||||
class Light : Effect
|
||||
{
|
||||
float dimmer;
|
||||
Vector ambient, diffuse, specular;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
debug(check)
|
||||
if(flags != 2 && flags != 4) nifFile.warn("Unknown flags");
|
||||
|
||||
nifFile.getIntIs(1);
|
||||
if(nifFile.getInt == 0) nifFile.warn("Light list might be missing");
|
||||
|
||||
dimmer = nifFile.getFloat;
|
||||
ambient = nifFile.getVector;
|
||||
diffuse = nifFile.getVector;
|
||||
specular = nifFile.getVector;
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
writefln("Dimmer: ", dimmer);
|
||||
writefln("Ambient: ", ambient.toString);
|
||||
writefln("Diffuse: ", diffuse.toString);
|
||||
writefln("Specular: ", specular.toString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NiTextureEffect : Effect
|
||||
{
|
||||
NiSourceTexture texture;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
debug(check)
|
||||
if(flags != 2 && flags != 4) nifFile.warn("Unknown flags");
|
||||
|
||||
int i = nifFile.wgetInt; // 0 or 1
|
||||
|
||||
if(i == 1) {if(nifFile.getInt == 0) nifFile.warn("List might be missing");}
|
||||
else if(i != 0) nifFile.warn("Unknown value");
|
||||
|
||||
for(int j; j<3; j++)
|
||||
{
|
||||
nifFile.wgetFloatIs(1);
|
||||
nifFile.wgetFloatIs(0);
|
||||
nifFile.wgetFloatIs(0);
|
||||
nifFile.wgetFloatIs(0);
|
||||
}
|
||||
|
||||
nifFile.wgetIntIs(2);
|
||||
nifFile.wgetIntIs(0,3);
|
||||
nifFile.wgetIntIs(2);
|
||||
nifFile.wgetIntIs(2);
|
||||
debug(verbose) writef("Source Texture ");
|
||||
getIndex();
|
||||
nifFile.wgetByteIs(0);
|
||||
|
||||
nifFile.wgetFloatIs(1);
|
||||
nifFile.wgetFloatIs(0);
|
||||
nifFile.wgetFloatIs(0);
|
||||
nifFile.wgetFloatIs(0);
|
||||
|
||||
nifFile.wgetShortIs(0);
|
||||
nifFile.wgetShortIs(-75);
|
||||
nifFile.wgetShortIs(0);
|
||||
}
|
||||
|
||||
void sortOut(Record[] list)
|
||||
{
|
||||
super.sortOut(list);
|
||||
texture = lookup!(NiSourceTexture)(list);
|
||||
}
|
||||
}
|
@ -1,212 +0,0 @@
|
||||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.snaptoad.com/
|
||||
|
||||
This file (extra.d) 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/ .
|
||||
|
||||
*/
|
||||
|
||||
module nif.extra;
|
||||
import nif.record;
|
||||
import nif.controlled;
|
||||
|
||||
import util.utfconvert;
|
||||
|
||||
abstract class Extra : Record
|
||||
{
|
||||
Extra extra;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
debug(verbose) nifFile.writef("Extra Data ");
|
||||
getIndex();
|
||||
}
|
||||
|
||||
void sortOut(Record[] list)
|
||||
{
|
||||
super.sortOut(list);
|
||||
extra = lookup!(Extra)(list);
|
||||
}
|
||||
}
|
||||
|
||||
class NiVertWeightsExtraData : Extra
|
||||
{
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
int i = nifFile.getInt;
|
||||
short s = nifFile.getShort; // = number of vertices
|
||||
|
||||
if(s*4+2 != i)
|
||||
nifFile.warn("Sizes don't add up");
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
writefln("Total bytes in record: ", i);
|
||||
writefln("Number of vertex weights: ", s);
|
||||
}
|
||||
|
||||
for(int j; j<s; j++)
|
||||
{
|
||||
float ff = nifFile.getFloat;
|
||||
debug(verbose) writefln("Float: ", ff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NiTextKeyExtraData : Extra
|
||||
{
|
||||
struct Key
|
||||
{
|
||||
float time;
|
||||
char[] string;
|
||||
}
|
||||
|
||||
Key[] keys;
|
||||
|
||||
override void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
nifFile.getIntIs(0);
|
||||
|
||||
int keynum = nifFile.getInt;
|
||||
|
||||
nifFile.fitArray(keynum,8);
|
||||
|
||||
keys = nifRegion.allocateT!(Key)(keynum);
|
||||
foreach(int i, ref Key k; keys)
|
||||
{
|
||||
k.time = nifFile.getFloat;
|
||||
k.string = nifFile.getString;
|
||||
|
||||
debug(verbose)
|
||||
writefln(" %d: %s @ %f ", i, makeUTF8(k.string).chomp(), k.time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NiStringExtraData : Extra
|
||||
{
|
||||
char[] string;
|
||||
|
||||
override void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
int size = nifFile.getInt;
|
||||
string = nifFile.getString;
|
||||
|
||||
if(size != string.length + 4)
|
||||
nifFile.warn("String size was incorrect.");
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
// "NCO" means 'no collision', I think
|
||||
writefln("String: %s", makeUTF8(string));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NiParticleGrowFade : Controlled
|
||||
{
|
||||
//float f1, f2;
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
nifFile.wgetFloat();
|
||||
nifFile.wgetFloat();
|
||||
}
|
||||
}
|
||||
|
||||
class NiParticleColorModifier : Controlled
|
||||
{
|
||||
NiColorData data;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
debug(verbose) writef("Color Data ");
|
||||
getIndex();
|
||||
}
|
||||
|
||||
void sortOut(Record[] list)
|
||||
{
|
||||
super.sortOut(list);
|
||||
lookup!(NiColorData)(list);
|
||||
}
|
||||
}
|
||||
|
||||
class NiGravity : Controlled
|
||||
{
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
nifFile.wgetFloat();
|
||||
nifFile.wgetFloat();
|
||||
nifFile.wgetInt();
|
||||
nifFile.wgetFloat();
|
||||
nifFile.wgetFloat();
|
||||
nifFile.wgetFloat();
|
||||
nifFile.wgetFloat();
|
||||
nifFile.wgetFloat();
|
||||
nifFile.wgetFloat();
|
||||
}
|
||||
}
|
||||
|
||||
class NiPlanarCollider : Controlled
|
||||
{
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
nifFile.wgetFloat();
|
||||
nifFile.wgetFloat();
|
||||
nifFile.wgetFloat();
|
||||
nifFile.wgetFloat();
|
||||
|
||||
nifFile.wgetVector();
|
||||
nifFile.wgetVector();
|
||||
nifFile.wgetVector();
|
||||
nifFile.wgetVector();
|
||||
}
|
||||
}
|
||||
|
||||
class NiParticleRotation : Controlled
|
||||
{
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
byte b = nifFile.wgetByteIs(0,1);
|
||||
|
||||
nifFile.wgetFloatIs(1);
|
||||
nifFile.wgetFloat();
|
||||
nifFile.wgetFloat();
|
||||
nifFile.wgetFloat();
|
||||
}
|
||||
}
|
@ -1,139 +0,0 @@
|
||||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.snaptoad.com/
|
||||
|
||||
This file (misc.d) 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/ .
|
||||
|
||||
*/
|
||||
|
||||
// This doesn't have to be part of the nif package at all.
|
||||
module nif.misc;
|
||||
|
||||
import std.string;
|
||||
import monster.util.string;
|
||||
|
||||
struct Vector
|
||||
{
|
||||
float array[3];
|
||||
|
||||
void set(float x, float y, float z)
|
||||
{
|
||||
array[0] = x;
|
||||
array[1] = y;
|
||||
array[2] = z;
|
||||
}
|
||||
|
||||
char[] toString()
|
||||
{
|
||||
return format(array);
|
||||
//return format("[", array[0], ",", array[1], ",", array[2], "]");
|
||||
}
|
||||
|
||||
int opEquals(ref Vector v)
|
||||
{
|
||||
return v.array == array;
|
||||
}
|
||||
|
||||
static assert(Vector.sizeof == 4*3);
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
Vector a, b;
|
||||
a.set(1,2,3);
|
||||
assert(a!=b);
|
||||
b = a;
|
||||
assert(a==b);
|
||||
}
|
||||
|
||||
struct Vector4
|
||||
{
|
||||
float array[4];
|
||||
|
||||
void set(float x, float y, float z, float a)
|
||||
{
|
||||
array[0] = x;
|
||||
array[1] = y;
|
||||
array[2] = z;
|
||||
array[3] = a;
|
||||
}
|
||||
|
||||
char[] toString()
|
||||
{
|
||||
return format(array);
|
||||
//return format("[", array[0], ",", array[1], ",", array[2], ",", array[3], "]");
|
||||
}
|
||||
|
||||
int opEquals(ref Vector4 v)
|
||||
{
|
||||
return v.array == array;
|
||||
}
|
||||
|
||||
static assert(Vector4.sizeof == 4*4);
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
Vector4 a, b;
|
||||
a.set(1,2,3,5);
|
||||
assert(a!=b);
|
||||
b = a;
|
||||
assert(a==b);
|
||||
}
|
||||
|
||||
align(1)
|
||||
struct Matrix
|
||||
{
|
||||
union
|
||||
{
|
||||
Vector v[3];
|
||||
float[9] array;
|
||||
}
|
||||
|
||||
char[] toString()
|
||||
{
|
||||
char[] res;
|
||||
res ~= " Right: " ~ v[0].toString;
|
||||
res ~= "\n Up: " ~ v[1].toString;
|
||||
res ~= "\n Front: " ~ v[2].toString;
|
||||
return res;
|
||||
}
|
||||
static assert(Matrix.sizeof == 3*3*4);
|
||||
}
|
||||
|
||||
|
||||
align(1)
|
||||
struct Transformation
|
||||
{
|
||||
Vector pos;
|
||||
Matrix rotation;
|
||||
float scale;
|
||||
Vector velocity;
|
||||
|
||||
char[] toString()
|
||||
{
|
||||
char[] res;
|
||||
res ~= " Translation: " ~ pos.toString();
|
||||
res ~= "\n" ~ rotation.toString();
|
||||
res ~= "\n Scale: " ~ format(scale);
|
||||
res ~= "\n Velocity: " ~ velocity.toString();
|
||||
return res;
|
||||
}
|
||||
|
||||
static assert(Transformation.sizeof == 5*Vector.sizeof + 4);
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.snaptoad.com/
|
||||
|
||||
This file (nif.d) 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/ .
|
||||
|
||||
*/
|
||||
|
||||
module nif.nif;
|
||||
|
||||
import nif.base;
|
||||
import nif.niffile;
|
||||
import nif.record;
|
||||
|
||||
import core.memory;
|
||||
|
||||
/* The NIFMesh struct will open, read and close a .nif file. Unlike
|
||||
* NIFFile (which it uses), it stores all the data from the NIF.
|
||||
*/
|
||||
NIFMesh nifMesh;
|
||||
|
||||
struct NIFMesh
|
||||
{
|
||||
public:
|
||||
Record records[];
|
||||
|
||||
// Read from a file
|
||||
void open(char[] file)
|
||||
{
|
||||
nifFile.open(file);
|
||||
loadData(); // Load the data
|
||||
}
|
||||
|
||||
// Read from a memory slice. The optitional file name is used for
|
||||
// error messages only.
|
||||
void open(void[] s, char[] name = "")
|
||||
{
|
||||
nifFile.open(s, name);
|
||||
loadData(); // Load the data
|
||||
}
|
||||
|
||||
// Clear all loaded data
|
||||
void clear()
|
||||
{
|
||||
records = null;
|
||||
|
||||
// Clear the region manager
|
||||
nifRegion.freeAll();
|
||||
}
|
||||
|
||||
/*
|
||||
void fail(char[] msg)
|
||||
{
|
||||
throw new NIFMeshException(msg);
|
||||
}
|
||||
*/
|
||||
|
||||
private void loadData()
|
||||
{
|
||||
// Remove any previously loaded data
|
||||
clear();
|
||||
|
||||
// The actual parsing is done somewhere else.
|
||||
nif.base.parseFile();
|
||||
|
||||
// Close the file
|
||||
nifFile.close();
|
||||
|
||||
// Resolve inter-record references into object pointers.
|
||||
foreach(int i, Record o; records)
|
||||
if(o !is null)
|
||||
{
|
||||
nifFile.setRec(i, o);
|
||||
o.sortOut(records);
|
||||
}
|
||||
|
||||
// Let the records do consistency checks on their data.
|
||||
foreach(int i, Record o; records)
|
||||
if(o !is null)
|
||||
{
|
||||
nifFile.setRec(i, o);
|
||||
o.check();
|
||||
}
|
||||
|
||||
// Internal state check, used for debugging
|
||||
debug(statecheck)
|
||||
foreach(Record o; records)
|
||||
if(o !is null) o.finalCheck();
|
||||
}
|
||||
|
||||
}
|
@ -1,482 +0,0 @@
|
||||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.snaptoad.com/
|
||||
|
||||
This file (niffile.d) 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/ .
|
||||
|
||||
*/
|
||||
|
||||
module nif.niffile;
|
||||
|
||||
public import std.stream;
|
||||
public import util.regions;
|
||||
|
||||
import std.string;
|
||||
import nif.misc;
|
||||
|
||||
public import core.memory;
|
||||
|
||||
// Exception class thrown by most exceptions originating within the
|
||||
// nif package
|
||||
class NIFFileException : Exception
|
||||
{
|
||||
this(char[] msg) {super("NIFFile Exception: " ~ msg);}
|
||||
}
|
||||
|
||||
/* The NIFFile struct is used for the task of reading from NIF
|
||||
files. It provides specialized methods for handling common types,
|
||||
records, etc., and also provides mechanisms for output and error
|
||||
handling. It does not store any (non-transient) NIF data.
|
||||
*/
|
||||
NIFFile nifFile;
|
||||
|
||||
struct NIFFile
|
||||
{
|
||||
private:
|
||||
Stream f; // Input stream
|
||||
BufferedFile bf; // Used and reused for direct file input.
|
||||
TArrayStream!(ubyte[]) ss; // For stream reading
|
||||
|
||||
// These are used for warnings and error messages only
|
||||
char[] filename;
|
||||
int recNum; // Record number
|
||||
char[] recName; // Record name
|
||||
Object recObj; // Record object
|
||||
public:
|
||||
|
||||
/* ------------------------------------------------
|
||||
* Object initialization and file management
|
||||
* ------------------------------------------------
|
||||
*/
|
||||
|
||||
// Open a file from the file system
|
||||
void open(char[] file)
|
||||
{
|
||||
close();
|
||||
|
||||
// Create a BufferedFile, and reuse it later.
|
||||
if(bf is null)
|
||||
bf = new BufferedFile();
|
||||
|
||||
bf.open(file);
|
||||
|
||||
f = bf;
|
||||
filename = file;
|
||||
}
|
||||
|
||||
// Open a memory slice, the name is used for error messages.
|
||||
void open(void[] data, char[] name)
|
||||
{
|
||||
close();
|
||||
|
||||
// Create a TArrayStream, and reuse it
|
||||
if(ss is null)
|
||||
{
|
||||
ss = new TArrayStream!(ubyte[])(cast(ubyte[])data);
|
||||
ss.writeable = false; // Read-only
|
||||
}
|
||||
else
|
||||
{
|
||||
// TArrayStream lacks the open() method. Instead, all the
|
||||
// members are public.
|
||||
ss.buf = cast(ubyte[])data;
|
||||
ss.len = ss.buf.length;
|
||||
ss.cur = 0;
|
||||
}
|
||||
|
||||
f = ss;
|
||||
filename = name;
|
||||
}
|
||||
|
||||
// Close the file handle
|
||||
void close()
|
||||
{
|
||||
// Close the buffered file
|
||||
if(f !is null && f is bf) f.close();
|
||||
f = null;
|
||||
|
||||
// Zero out variables
|
||||
filename = null;
|
||||
recNum = 0;
|
||||
recName = null;
|
||||
recObj = null;
|
||||
}
|
||||
|
||||
ulong size()
|
||||
in
|
||||
{
|
||||
if(f is null) fail("Cannot get size, file is not open");
|
||||
}
|
||||
body
|
||||
{
|
||||
return f.size();
|
||||
}
|
||||
|
||||
bool eof() { return f.eof(); }
|
||||
|
||||
void seekCur(long skip)
|
||||
in
|
||||
{
|
||||
if(f is null) fail("Cannot seek, file is not open");
|
||||
}
|
||||
body
|
||||
{
|
||||
f.seekCur(skip);
|
||||
}
|
||||
|
||||
ulong position()
|
||||
{
|
||||
return f.position();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------
|
||||
* Error reporting
|
||||
* ------------------------------------------------
|
||||
*/
|
||||
|
||||
// Used to format error messages
|
||||
private char[] makeError(char[] str)
|
||||
{
|
||||
if(recNum > 0)
|
||||
{
|
||||
if(recName == "" && recObj !is null) recName = recObj.toString;
|
||||
str = format("%s\n Record %d: %s", str, recNum-1, recName);
|
||||
}
|
||||
if(filename != "") str = format("%s\n File: %s", str, filename);
|
||||
if(f !is null)
|
||||
if(f.eof()) str = format("%s\n At end of file", str);
|
||||
else str = format("%s\n Offset 0x%x", str, f.position);
|
||||
return str ~ "\n";
|
||||
}
|
||||
|
||||
void fail(char[] msg)
|
||||
{
|
||||
throw new NIFFileException(makeError(msg));
|
||||
}
|
||||
|
||||
debug(warnstd) import std.stdio;
|
||||
|
||||
void warn(char[] msg)
|
||||
{
|
||||
debug(strict) fail("(fail on warning) " ~ msg);
|
||||
else
|
||||
{
|
||||
debug(warnstd) writefln("WARNING: ", makeError(msg));
|
||||
debug(warnlog) log.writefln("WARNING: ", makeError(msg));
|
||||
}
|
||||
}
|
||||
|
||||
void assertWarn(bool condition, char[] str)
|
||||
{ if(!condition) warn(str); }
|
||||
|
||||
/*
|
||||
void assertFail(bool condition, char[] str)
|
||||
{ if(!condition) fail(str); }
|
||||
*/
|
||||
|
||||
// Used for error message handling, hackish.
|
||||
void setRec(int i, char[] n)
|
||||
{
|
||||
recNum = i+1;
|
||||
recName = n;
|
||||
recObj = null;
|
||||
}
|
||||
|
||||
// This variant takes an object instead of a name. That way we only
|
||||
// need to call toString when the name is needed (which it mostly
|
||||
// isn't.)
|
||||
void setRec(int i, Object o)
|
||||
{
|
||||
recNum = i+1;
|
||||
recObj = o;
|
||||
recName = null;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------
|
||||
* Reader method templates (skip to next section
|
||||
* for usable methods)
|
||||
* ------------------------------------------------
|
||||
*/
|
||||
|
||||
// Read a variable of a given type T
|
||||
T getType(T)()
|
||||
{
|
||||
T t;
|
||||
f.read(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
// Read a variable and compare it to what we want it to be
|
||||
template getTypeIs(T)
|
||||
{
|
||||
T getTypeIs(T[] pt ...)
|
||||
{
|
||||
T t = getType!(T)();
|
||||
debug(check)
|
||||
{
|
||||
bool match;
|
||||
foreach(T a; pt)
|
||||
if(a == t) {match = true; break;}
|
||||
|
||||
if(!match)
|
||||
{
|
||||
char[] errMsg = format(typeid(T),
|
||||
" mismatch: got %s, expected ", t);
|
||||
if(pt.length > 1) errMsg ~= "one of: ";
|
||||
foreach(T a; pt)
|
||||
errMsg ~= format("%s ", a);
|
||||
warn(errMsg);
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
// Read a variable of a given type T and print it to screen
|
||||
template wgetType(T)
|
||||
{
|
||||
T wgetType()
|
||||
{
|
||||
T t;
|
||||
f.read(t);
|
||||
writefln(typeid(T), ": ", t);
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
// Read a variable and compare it to what we want it to be
|
||||
template wgetTypeIs(T)
|
||||
{
|
||||
T wgetTypeIs(T pt[] ...)
|
||||
{
|
||||
T t = getType!(T)();
|
||||
|
||||
char[] wanted;
|
||||
if(pt.length > 1) wanted = "one of: ";
|
||||
foreach(T a; pt) wanted ~= format("%s ", a);
|
||||
|
||||
writefln(typeid(T), ": ", t, " (wanted %s)", wanted);
|
||||
|
||||
debug(check)
|
||||
{
|
||||
bool match;
|
||||
foreach(T a; pt)
|
||||
if(a == t) {match = true; break;}
|
||||
|
||||
if(!match)
|
||||
{
|
||||
warn(format(typeid(T),
|
||||
" mismatch: got %s, expected %s", t, wanted));
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the provided array of Ts
|
||||
template getArrayLen(T)
|
||||
{
|
||||
T[] getArrayLen(T[] arr)
|
||||
{
|
||||
f.readExact(arr.ptr, arr.length*T.sizeof);
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the size of the provided array to 'count', and fill it.
|
||||
T[] getArraySize(T)(int count)
|
||||
{
|
||||
T[] arr;
|
||||
fitArray(count, T.sizeof);
|
||||
arr = cast(T[])nifRegion.allocate(count * T.sizeof);
|
||||
getArrayLen!(T)(arr);
|
||||
return arr;
|
||||
}
|
||||
|
||||
// Get an array of Ts preceded by an array length of type Index
|
||||
// (assumed to be an integer type)
|
||||
template getArray(T,Index)
|
||||
{
|
||||
T[] getArray()
|
||||
{
|
||||
// Read array length
|
||||
Index s = getType!(Index)();
|
||||
|
||||
// Is array larger than file?
|
||||
fitArray(s,T.sizeof);
|
||||
|
||||
// Allocate the buffer
|
||||
T[] result = cast(T[])nifRegion.allocate(s * T.sizeof);
|
||||
|
||||
// Read the buffer
|
||||
return getArrayLen!(T)(result);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------
|
||||
* Reader methods
|
||||
* ------------------------------------------------
|
||||
*/
|
||||
|
||||
// Strings
|
||||
alias getArrayLen!(char) getString;
|
||||
alias getArray!(char,int) getString;
|
||||
|
||||
// Other arrays
|
||||
alias getArray!(int,int) getInts;
|
||||
|
||||
// Checks if an array of size elements each of size esize could
|
||||
// possibly follow in the file.
|
||||
void fitArray(int size, int esize)
|
||||
in
|
||||
{
|
||||
assert(esize > 0);
|
||||
}
|
||||
body
|
||||
{
|
||||
if((size*esize+8) > (f.size() - f.position()) || size < 0)
|
||||
fail(format(
|
||||
"Array out of bounds: %d*%d + 8 bytes needed, but only %d bytes left in file",
|
||||
size,esize,(f.size-f.position)));
|
||||
}
|
||||
|
||||
// Base types
|
||||
alias getType!(byte) getByte;
|
||||
alias getType!(ubyte) getUbyte;
|
||||
alias getType!(short) getShort;
|
||||
alias getType!(ushort) getUshort;
|
||||
alias getType!(int) getInt;
|
||||
alias getType!(uint) getUint;
|
||||
alias getType!(float) getFloat;
|
||||
|
||||
// Base types with error checking
|
||||
alias getTypeIs!(short) getShortIs;
|
||||
alias getTypeIs!(ushort) getUshortIs;
|
||||
alias getTypeIs!(byte) getByteIs;
|
||||
alias getTypeIs!(ubyte) getUbyteIs;
|
||||
alias getTypeIs!(int) getIntIs;
|
||||
alias getTypeIs!(uint) getUintIs;
|
||||
alias getTypeIs!(float) getFloatIs;
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
// Base types
|
||||
alias wgetType!(byte) wgetByte;
|
||||
alias wgetType!(ubyte) wgetUbyte;
|
||||
alias wgetType!(short) wgetShort;
|
||||
alias wgetType!(ushort) wgetUshort;
|
||||
alias wgetType!(int) wgetInt;
|
||||
alias wgetType!(uint) wgetUint;
|
||||
alias wgetType!(float) wgetFloat;
|
||||
|
||||
// Base types with error checking
|
||||
alias wgetTypeIs!(short) wgetShortIs;
|
||||
alias wgetTypeIs!(ushort) wgetUshortIs;
|
||||
alias wgetTypeIs!(byte) wgetByteIs;
|
||||
alias wgetTypeIs!(ubyte) wgetUbyteIs;
|
||||
alias wgetTypeIs!(int) wgetIntIs;
|
||||
alias wgetTypeIs!(uint) wgetUintIs;
|
||||
alias wgetTypeIs!(float) wgetFloatIs;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Base types
|
||||
alias getByte wgetByte;
|
||||
alias getUbyte wgetUbyte;
|
||||
alias getShort wgetShort;
|
||||
alias getUshort wgetUshort;
|
||||
alias getInt wgetInt;
|
||||
alias getUint wgetUint;
|
||||
alias getFloat wgetFloat;
|
||||
|
||||
// Base types with error checking
|
||||
alias getByteIs wgetByteIs;
|
||||
alias getUbyteIs wgetUbyteIs;
|
||||
alias getShortIs wgetShortIs;
|
||||
alias getUshortIs wgetUshortIs;
|
||||
alias getIntIs wgetIntIs;
|
||||
alias getUintIs wgetUintIs;
|
||||
alias getFloatIs wgetFloatIs;
|
||||
}
|
||||
|
||||
// Vectors
|
||||
Vector getVector()
|
||||
{
|
||||
Vector v;
|
||||
getArrayLen!(float)(v.array);
|
||||
return v;
|
||||
}
|
||||
|
||||
Vector4 getVector4()
|
||||
{
|
||||
Vector4 v;
|
||||
getArrayLen!(float)(v.array);
|
||||
return v;
|
||||
}
|
||||
|
||||
Vector getVectorIs(float x, float y, float z)
|
||||
{
|
||||
Vector v = getVector();
|
||||
debug(check)
|
||||
{
|
||||
Vector u;
|
||||
u.set(x,y,z);
|
||||
if(v != u)
|
||||
warn("Vector mismatch: expected " ~ u.toString ~ ", got " ~ v.toString);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
Vector wgetVector()
|
||||
{
|
||||
Vector v = getVector();
|
||||
writefln("vector: ", v.toString);
|
||||
return v;
|
||||
}
|
||||
|
||||
Vector4 wgetVector4()
|
||||
{
|
||||
Vector4 v = getVector4();
|
||||
writefln("4-vector: ", v.toString);
|
||||
return v;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
alias getVector wgetVector;
|
||||
alias getVector4 wgetVector4;
|
||||
}
|
||||
|
||||
void getMatrix(ref Matrix m)
|
||||
{
|
||||
getArrayLen!(float)(m.array);
|
||||
}
|
||||
|
||||
void getTransformation(ref Transformation t)
|
||||
{
|
||||
t.pos = getVector();
|
||||
getMatrix(t.rotation);
|
||||
t.scale = getFloat/*Is(1)*/;
|
||||
t.velocity = getVectorIs(0,0,0);
|
||||
}
|
||||
}
|
@ -1,324 +0,0 @@
|
||||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.snaptoad.com/
|
||||
|
||||
This file (node.d) 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/ .
|
||||
|
||||
*/
|
||||
|
||||
module nif.node;
|
||||
|
||||
import nif.record;
|
||||
|
||||
// Tree node
|
||||
abstract class Node : Named
|
||||
{
|
||||
ushort flags;
|
||||
Transformation trafo;
|
||||
//Extra properties[];
|
||||
Property properties[];
|
||||
|
||||
Node parent;
|
||||
|
||||
// Bounding box info
|
||||
bool hasBounding;
|
||||
Vector boundPos;
|
||||
Matrix boundRot;
|
||||
Vector boundXYZ;
|
||||
|
||||
void setParent(Node p)
|
||||
{
|
||||
debug(veryverbose)
|
||||
writefln("Setting parent of ", toString, " to ", p);
|
||||
|
||||
if(parent !is null)
|
||||
{
|
||||
char str[] = toString() ~ " already has parent " ~ parent.toString()
|
||||
~ ", but setting ";
|
||||
if(p !is null) str ~= p.toString;
|
||||
else str ~= "to null";
|
||||
|
||||
nifFile.warn(str);
|
||||
}
|
||||
|
||||
parent = p;
|
||||
}
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
flags = nifFile.getUshort();
|
||||
nifFile.getTransformation(trafo);
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
writefln("Flags: %x", flags);
|
||||
writefln("Transformation:\n", trafo.toString);
|
||||
writef("Properties: ");
|
||||
}
|
||||
|
||||
properties = nifRegion.allocateT!(Property)(getIndexList());
|
||||
|
||||
if(nifFile.getInt != 0)
|
||||
{
|
||||
hasBounding = true;
|
||||
nifFile.getIntIs(1);
|
||||
boundPos = nifFile.getVector;
|
||||
nifFile.getMatrix(boundRot);
|
||||
boundXYZ = nifFile.getVector;
|
||||
//if(name != "Bounding Box") nifFile.warn("Node name is not 'Bounding Box'");
|
||||
debug(verbose)
|
||||
{
|
||||
writefln("Bounding box: ", boundPos.toString);
|
||||
writefln(boundRot.toString);
|
||||
writefln("XYZ: ", boundXYZ.toString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sortOut(Record[] list)
|
||||
{
|
||||
super.sortOut(list);
|
||||
lookupList!(Property)(list,properties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class NiCamera : Node
|
||||
{
|
||||
float left, right, top, bottom, near, far,
|
||||
vleft, vright, vtop, vbottom, LOD;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
left = nifFile.getFloat;
|
||||
right = nifFile.getFloat;
|
||||
top = nifFile.getFloat;
|
||||
bottom = nifFile.getFloat;
|
||||
near = nifFile.getFloat;
|
||||
far = nifFile.getFloat;
|
||||
|
||||
vleft = nifFile.getFloat;
|
||||
vright = nifFile.getFloat;
|
||||
vtop = nifFile.getFloat;
|
||||
vbottom = nifFile.getFloat;
|
||||
|
||||
LOD = nifFile.getFloat;
|
||||
|
||||
nifFile.getIntIs(-1);
|
||||
nifFile.getIntIs(0);
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
writefln("Camera frustrum:");
|
||||
writefln(" Left: ", left);
|
||||
writefln(" Right: ", right);
|
||||
writefln(" Top: ", top);
|
||||
writefln(" Bottom: ", bottom);
|
||||
writefln(" Near: ", near);
|
||||
writefln(" Far: ", far);
|
||||
|
||||
writefln("View port:");
|
||||
writefln(" Left: ", vleft);
|
||||
writefln(" Right: ", vright);
|
||||
writefln(" Top: ", vtop);
|
||||
writefln(" Bottom: ", vbottom);
|
||||
|
||||
writefln("LOD adjust: ", LOD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NiAutoNormalParticles : Node
|
||||
{
|
||||
NiAutoNormalParticlesData data;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
debug(check)
|
||||
{
|
||||
if(flags & 0xffff-6)
|
||||
nifFile.warn("Unknown flags");
|
||||
}
|
||||
|
||||
debug(verbose) writef("Particle Data ");
|
||||
getIndex();
|
||||
|
||||
nifFile.getIntIs(-1);
|
||||
}
|
||||
|
||||
void sortOut(Record[] list)
|
||||
{
|
||||
super.sortOut(list);
|
||||
data = lookup!(NiAutoNormalParticlesData)(list);
|
||||
|
||||
debug(check)
|
||||
{
|
||||
if(castCheck!(NiParticleSystemController)(controller)
|
||||
&& castCheck!(NiBSPArrayController)(controller))
|
||||
nifFile.warn("In " ~ toString ~ ": did not expect controller "
|
||||
~ controller.toString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NiRotatingParticles : Node
|
||||
{
|
||||
NiRotatingParticlesData data;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
debug(check)
|
||||
{
|
||||
if(flags & 0xffff-6)
|
||||
nifFile.warn("Unknown flags");
|
||||
}
|
||||
|
||||
//nifFile.getIntIs(0);
|
||||
|
||||
debug(verbose) writef("Particle Data ");
|
||||
getIndex();
|
||||
|
||||
nifFile.getIntIs(-1);
|
||||
}
|
||||
|
||||
void sortOut(Record[] list)
|
||||
{
|
||||
super.sortOut(list);
|
||||
data = lookup!(NiRotatingParticlesData)(list);
|
||||
|
||||
debug(check)
|
||||
{
|
||||
if(castCheck!(NiParticleSystemController)(controller)
|
||||
&& castCheck!(NiBSPArrayController)(controller) )
|
||||
nifFile.warn("In " ~ toString ~ ": did not expect controller "
|
||||
~ controller.toString);
|
||||
}
|
||||
//castWarn!(NiParticleSystemController)(controller);
|
||||
}
|
||||
}
|
||||
|
||||
class NiTriShape : Node
|
||||
{
|
||||
NiTriShapeData data;
|
||||
NiSkinInstance skin;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
// If this is correct, it suggests how one might decode
|
||||
// other flags. Check if this one is correct in sortOut().
|
||||
if(flags&0x40) writefln("Mesh has no normals?");
|
||||
}
|
||||
debug(check)
|
||||
{
|
||||
if(flags & (0xffff-0x47))
|
||||
nifFile.warn("Unknown flags were set");
|
||||
}
|
||||
|
||||
//nifFile.getIntIs(0);
|
||||
debug(verbose) writef("Mesh index: ");
|
||||
getIndex();
|
||||
debug(verbose) writef("Skin index: ");
|
||||
getIndex();
|
||||
}
|
||||
|
||||
void sortOut(Record[] list)
|
||||
{
|
||||
super.sortOut(list);
|
||||
data = lookup!(NiTriShapeData)(list);
|
||||
skin = lookup!(NiSkinInstance)(list);
|
||||
|
||||
debug(check)
|
||||
{
|
||||
if(data is null) nifFile.warn("Data missing from NiTriShape");
|
||||
|
||||
if(castCheck!(NiGeomMorpherController)(controller)
|
||||
&& castCheck!(NiUVController)(controller)
|
||||
&& castCheck!(NiVisController)(controller) )
|
||||
nifFile.warn("In " ~ toString ~ ": did not expect controller "
|
||||
~ controller.toString);
|
||||
|
||||
if((data.normals.length == 0) && (flags & 0x40 == 0) ||
|
||||
(data.normals.length != 0) && (flags & 0x40))
|
||||
nifFile.warn("0x40 present but normals missing, or vice versa");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NiNode : Node
|
||||
{
|
||||
Node children[];
|
||||
Effect effects[];
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
if(flags & 1) writefln(" 0x01 Hidden");
|
||||
if(flags & 2) writefln(" 0x02 Collision detection?");
|
||||
if(flags & 4) writefln(" 0x04 Collision detection2 ?");
|
||||
if(flags & 8) writefln(" 0x08 Unknown but common");
|
||||
if(flags & 0x20) writefln(" 0x20 Unknown");
|
||||
if(flags & 0x40) writefln(" 0x40 Unknown");
|
||||
if(flags & 0x80) writefln(" 0x80 Unknown");
|
||||
}
|
||||
debug(check)
|
||||
if(flags & 0x10) nifFile.warn("Unknown flags were set");
|
||||
|
||||
debug(verbose) writef("Child nodes: ");
|
||||
children = nifRegion.allocateT!(Node)(getIndexList());
|
||||
debug(verbose) writef("Effects: ");
|
||||
effects = nifRegion.allocateT!(Effect)(getIndexList());
|
||||
}
|
||||
|
||||
void sortOut(Record[] list)
|
||||
{
|
||||
super.sortOut(list);
|
||||
|
||||
lookupList!(Node)(list,children);
|
||||
lookupList!(Effect)(list,effects);
|
||||
|
||||
if(castCheck!(NiKeyframeController)(controller)
|
||||
&& castCheck!(NiVisController)(controller)
|
||||
&& castCheck!(NiPathController)(controller))
|
||||
nifFile.warn("In " ~ toString ~ ": did not expect controller "
|
||||
~ controller.toString);
|
||||
|
||||
foreach(Node n; children)
|
||||
n.setParent(this);
|
||||
}
|
||||
}
|
@ -1,313 +0,0 @@
|
||||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.snaptoad.com/
|
||||
|
||||
This file (property.d) 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/ .
|
||||
|
||||
*/
|
||||
|
||||
module nif.property;
|
||||
import nif.record;
|
||||
|
||||
abstract class Property : Named
|
||||
{
|
||||
// The meaning of these depends on the actual property type.
|
||||
ushort flags;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
flags = nifFile.getUshort;
|
||||
debug(verbose) writefln("Flags: %x", flags);
|
||||
}
|
||||
}
|
||||
|
||||
// Check the specs on this one again
|
||||
class NiTexturingProperty : Property
|
||||
{
|
||||
/* Apply mode:
|
||||
0 - replace
|
||||
1 - decal
|
||||
2 - modulate
|
||||
3 - hilight // These two are for PS2 only?
|
||||
4 - hilight2
|
||||
*/
|
||||
int apply;
|
||||
|
||||
struct Texture
|
||||
{
|
||||
bool inUse;
|
||||
NiSourceTexture 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
|
||||
*/
|
||||
|
||||
int clamp, set, filter;
|
||||
short ps2L, ps2K, unknown2;
|
||||
|
||||
void read(NiTexturingProperty caller)
|
||||
{
|
||||
if(nifFile.getInt == 0)
|
||||
{
|
||||
debug(verbose) writefln("No texture");
|
||||
return;
|
||||
}
|
||||
|
||||
inUse = 1;
|
||||
|
||||
debug(verbose) writef(" Texture ");
|
||||
caller.getIndex();
|
||||
|
||||
clamp = nifFile.getIntIs(0,1,2,3);
|
||||
filter = nifFile.getIntIs(0,1,2);
|
||||
set = nifFile.getInt;
|
||||
|
||||
// The combination 1222, 322, 212 was used in a bump map
|
||||
// NIF. Might just be bogus numbers, I should probably allow all
|
||||
// values here since we ignore them anyway.
|
||||
ps2L = nifFile.getShortIs(0,1222);
|
||||
ps2K = nifFile.getShortIs(-75,-2,322);
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
writefln(" Clamp ", clamp);
|
||||
writefln(" Filter ", filter);
|
||||
writefln(" Set? ", set);
|
||||
writefln(" ps2L ", ps2L);
|
||||
writefln(" ps2K ", ps2K);
|
||||
}
|
||||
|
||||
unknown2 = nifFile.wgetShortIs(0,257,212);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The textures in this list is 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
|
||||
*/
|
||||
Texture[7] textures;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
if(flags > 1) nifFile.warn("Unknown flags");
|
||||
|
||||
apply = nifFile.getInt;
|
||||
debug(verbose) writefln("Apply mode: ", apply);
|
||||
nifFile.getIntIs(7);
|
||||
|
||||
textures[0].read(this); // Base
|
||||
textures[1].read(this); // Dark
|
||||
textures[2].read(this); // Detail
|
||||
|
||||
nifFile.getIntIs(0); // Gloss
|
||||
|
||||
textures[4].read(this); // Glow
|
||||
textures[5].read(this); // Bump map
|
||||
|
||||
if(textures[5].inUse)
|
||||
{
|
||||
float lumaScale = nifFile.wgetFloat;
|
||||
float lumaOffset = nifFile.wgetFloat;
|
||||
Vector4 lumaMatrix = nifFile.wgetVector4;
|
||||
}
|
||||
|
||||
textures[6].read(this); // Decal
|
||||
}
|
||||
|
||||
void sortOut(Record[] list)
|
||||
{
|
||||
super.sortOut(list);
|
||||
foreach(ref Texture t; textures)
|
||||
if(t.inUse) t.texture = lookup!(NiSourceTexture)(list);
|
||||
}
|
||||
}
|
||||
|
||||
class NiMaterialProperty : Property
|
||||
{
|
||||
Vector ambient, diffuse, specular, emissive;
|
||||
float glossiness, alpha;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
if(flags != 1) nifFile.warn("Unknown flags");
|
||||
|
||||
ambient = nifFile.getVector;
|
||||
diffuse = nifFile.getVector;
|
||||
specular = nifFile.getVector;
|
||||
emissive = nifFile.getVector;
|
||||
glossiness = nifFile.getFloat;
|
||||
alpha = nifFile.getFloat; // Use Alpha Property when this is not 1
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
writefln("Ambient: ", ambient.toString);
|
||||
writefln("Diffuse: ", diffuse.toString);
|
||||
writefln("Specular: ", specular.toString);
|
||||
writefln("Emissive: ", emissive.toString);
|
||||
writefln("Glossiness: ", glossiness);
|
||||
writefln("Alpha: ", alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
class NiVertexColorProperty : Property
|
||||
{
|
||||
/* 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;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
if(flags != 0) nifFile.warn("Unknown flags");
|
||||
vertmode = nifFile.getIntIs(0,1,2);
|
||||
lightmode = nifFile.getIntIs(0,1);
|
||||
debug(verbose)
|
||||
{
|
||||
writefln("Vertex mode: ", vertmode);
|
||||
writefln("Lighting mode: ", lightmode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
alias NiWireframeProperty NiDitherProperty;
|
||||
alias NiWireframeProperty NiSpecularProperty;
|
||||
|
||||
class NiWireframeProperty : Property
|
||||
{
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
if(flags != 1) nifFile.warn("Unknown flags");
|
||||
}
|
||||
}
|
||||
|
||||
class NiShadeProperty : Property
|
||||
{
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
if(flags != 0) nifFile.warn("Unknown flags");
|
||||
}
|
||||
}
|
||||
|
||||
class NiAlphaProperty : Property
|
||||
{
|
||||
/*
|
||||
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.)
|
||||
ubyte threshold;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
ubyte b = nifFile.getUbyte;
|
||||
debug(verbose) writefln("Unknown byte: ", b);
|
||||
}
|
||||
}
|
||||
|
||||
class NiZBufferProperty : Property
|
||||
{
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
debug(verbose)
|
||||
{
|
||||
if(flags&1) writefln(" 0x01 ZTest");
|
||||
if(flags&2) writefln(" 0x02 ZWrite");
|
||||
}
|
||||
if(flags & 0xfc) nifFile.warn("Unknown flags");
|
||||
}
|
||||
}
|
@ -1,302 +0,0 @@
|
||||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.snaptoad.com/
|
||||
|
||||
This file (record.d) 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/ .
|
||||
|
||||
*/
|
||||
|
||||
module nif.record;
|
||||
|
||||
public
|
||||
{
|
||||
import util.regions;
|
||||
|
||||
import nif.base;
|
||||
import nif.controller;
|
||||
import nif.controlled;
|
||||
import nif.node;
|
||||
import nif.data;
|
||||
import nif.extra;
|
||||
import nif.property;
|
||||
import nif.effect;
|
||||
|
||||
import nif.niffile;
|
||||
import nif.misc;
|
||||
|
||||
import std.string;
|
||||
}
|
||||
|
||||
// Base class for all NIF records
|
||||
abstract class Record
|
||||
{
|
||||
protected:
|
||||
// List of dependency indices. We throw this away after the entire
|
||||
// file is loaded and the dependencies have been converted to object
|
||||
// pointers.
|
||||
RegionBuffer!(int) depList;
|
||||
|
||||
debug(statecheck)
|
||||
{
|
||||
// An internal 'state', this check is only intended for
|
||||
// debugging purposes (hence the surrounding debug block ;-)
|
||||
int state;
|
||||
/* 0 - nothing has been done
|
||||
* 1 - record has been read
|
||||
* 2 - sortOut has been called
|
||||
* 3 - check has been called
|
||||
*/
|
||||
}
|
||||
public:
|
||||
|
||||
// Allocate objects of this class (and all subclasses) in the
|
||||
// nifRegion. This means we can allocate and deallocate the entire
|
||||
// nif tree very quickly, without involving the GC, and with zero
|
||||
// chance of memory leaks or heap fragmentation.
|
||||
new(uint sz)
|
||||
{
|
||||
return nifRegion.allocate(sz).ptr;
|
||||
}
|
||||
|
||||
delete(void *p) { assert(0); }
|
||||
|
||||
debug(statecheck)
|
||||
final void finalCheck()
|
||||
{
|
||||
debug(veryverbose)
|
||||
writefln("Final check on ", this);
|
||||
assert(state==3);
|
||||
}
|
||||
|
||||
// Read record data from file f. All indices are stored temporarily
|
||||
// as integers, as they appear in the file.
|
||||
void read()
|
||||
{
|
||||
// Allocate the dependency list. 50 should be enough entries.
|
||||
depList = nifRegion.getBuffer!(int)(0,50);
|
||||
|
||||
debug(veryverbose)
|
||||
writefln("Reading ", this, " at offset %x", nifFile.position);
|
||||
debug(statecheck) assert(state++ == 0);
|
||||
}
|
||||
|
||||
// Sort out dependencies between records. Called after all records
|
||||
// have been read from file. Paramter 'list' contains all records in
|
||||
// the order they appear in the file. Used to convert integer
|
||||
// indices into object pointers, and checking that all types are
|
||||
// correct. Can also be used for data checks than only require this
|
||||
// objects dependencies.
|
||||
void sortOut(Record[] list)
|
||||
{
|
||||
debug(veryverbose) writefln("Sorting out ", this);
|
||||
debug(statecheck) assert(state++ == 1);
|
||||
}
|
||||
|
||||
// Consistancy check. Called after all dependencies have been
|
||||
// sorted. Can be used for checking that vertex counts are the same
|
||||
// in different records, etc. It checks the depList array.
|
||||
void check()
|
||||
{
|
||||
debug(veryverbose) writefln("Consistancy check on ", this);
|
||||
|
||||
// Check that all dependencies have been resolved. delList is
|
||||
// successively sliced down to zero when indices are looked up.
|
||||
assert(depList.length == 0);
|
||||
|
||||
debug(statecheck) assert(state++ == 2);
|
||||
}
|
||||
|
||||
// Convert an integer record index to an object pointer of type T.
|
||||
template lookup(T: Record)
|
||||
{
|
||||
T lookup(Record[] list)
|
||||
{
|
||||
// Get the next dependency from the list
|
||||
int i = depList[0];
|
||||
depList = depList[1..depList.length()];
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
T t = lookupCast!(T)(i, list);
|
||||
debug(veryverbose)
|
||||
{
|
||||
writef(" Resolved ", i, " to ");
|
||||
if(t is null) writefln("(null)");
|
||||
else writefln(t);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
else
|
||||
return lookupCast!(T)(i, list);
|
||||
}
|
||||
}
|
||||
|
||||
template lookupList(T: Record)
|
||||
{
|
||||
void lookupList(Record[] list, T[] deps)
|
||||
{
|
||||
foreach(ref T t; deps)
|
||||
t = lookup!(T)(list);
|
||||
}
|
||||
}
|
||||
|
||||
// Reads an index (int) and adds it to the list of
|
||||
// dependencies. These can be returned later by lookup().
|
||||
void getIndex()
|
||||
{
|
||||
depList ~= nifFile.getInt;
|
||||
debug(verbose) writefln("Index ", depList[depList.length()-1]);
|
||||
}
|
||||
|
||||
int getIndexList()
|
||||
{
|
||||
int[] l = nifFile.getInts;
|
||||
int num;
|
||||
|
||||
// Only add non-null references
|
||||
foreach(int i; l)
|
||||
if(i != -1)
|
||||
{
|
||||
depList ~= i;
|
||||
num++;
|
||||
}
|
||||
|
||||
//depList ~= l;
|
||||
|
||||
debug(verbose)
|
||||
{
|
||||
writef("Index list: ");
|
||||
foreach(int i; l)
|
||||
writef(i, " ");
|
||||
writefln("(%d kept)", num);
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup with casting and error checking.
|
||||
template lookupCast(T: Record)
|
||||
{
|
||||
T lookupCast(int i, Record[] list)
|
||||
{
|
||||
if(i == -1) return null;
|
||||
|
||||
if(i < 0 || i >= list.length)
|
||||
nifFile.fail(format("Record index %d out of bounds (got %d records.)",
|
||||
i, list.length));
|
||||
|
||||
Record r = list[i];
|
||||
|
||||
if(r is null)
|
||||
{
|
||||
nifFile.warn("Referenced an unknown record type");
|
||||
return null;
|
||||
}
|
||||
|
||||
T t = cast(T)r;
|
||||
if(t is null)
|
||||
nifFile.fail("Cannot convert " ~ r.toString() ~ " to " ~
|
||||
(cast(TypeInfo_Class)typeid(T)).info.name);
|
||||
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
template castCheck(T: Record)
|
||||
{
|
||||
bool castCheck(Record r)
|
||||
{
|
||||
if(r !is null && cast(T)r is null) return true;
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
|
||||
template castWarn(T: Record)
|
||||
{
|
||||
debug(check)
|
||||
{
|
||||
void castWarn(Record r)
|
||||
{
|
||||
if(castCheck!(T)(r))
|
||||
nifFile.warn("Could not cast " ~ r.toString() ~ " to " ~
|
||||
(cast(TypeInfo_Class)typeid(T)).info.name);
|
||||
}
|
||||
}
|
||||
else
|
||||
void castWarn(Record r) {};
|
||||
}
|
||||
|
||||
class NiSkinInstance : Record
|
||||
{
|
||||
NiSkinData data;
|
||||
NiNode root;
|
||||
NiNode bones[];
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
super.read();
|
||||
|
||||
debug(verbose) writef("Skin data ");
|
||||
getIndex();
|
||||
|
||||
debug(verbose) writef("Scene root ");
|
||||
getIndex();
|
||||
|
||||
debug(verbose) writef("Bone ");
|
||||
bones = nifRegion.allocateT!(NiNode)(getIndexList());
|
||||
}
|
||||
|
||||
void sortOut(Record[] l)
|
||||
{
|
||||
super.sortOut(l);
|
||||
data = lookup!(NiSkinData)(l);
|
||||
root = lookup!(NiNode)(l);
|
||||
lookupList!(NiNode)(l,bones);
|
||||
}
|
||||
|
||||
void check()
|
||||
{
|
||||
super.check();
|
||||
debug(check) if(data !is null)
|
||||
{
|
||||
if(bones.length != data.weights.length)
|
||||
nifFile.warn(format("NiSkinInstance has ", bones.length,
|
||||
" bones, but NiSkinData has ", data.weights.length));
|
||||
|
||||
/*
|
||||
int meshCount = root.getFirstMesh(r).data.vertices.length/3;
|
||||
|
||||
foreach(int i, NiSkinData.Weight[] wl; data.weights)
|
||||
{
|
||||
if(bones[i] is null)
|
||||
r.warn("Bone object missing!");
|
||||
|
||||
if(meshCount < data.weights[i].length)
|
||||
r.warn(format("More skin weights than vertices in bone %d (%d < %d)",
|
||||
i, meshCount, data.weights[i].length));
|
||||
|
||||
foreach(ref NiSkinData.Weight w; data.weights[i])
|
||||
if(w.vertex >= meshCount)
|
||||
r.warn("Skin weight vertex index out of bounds");
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue