summaryrefslogtreecommitdiff
path: root/NW4RTools/Models
diff options
context:
space:
mode:
Diffstat (limited to 'NW4RTools/Models')
-rw-r--r--NW4RTools/Models/OpenGL/GLDisplayList.cs31
-rw-r--r--NW4RTools/Models/OpenGL/GLModel.cs235
-rw-r--r--NW4RTools/Models/OpenGL/GLTexture.cs46
-rw-r--r--NW4RTools/Models/TextureInfo.cs3
-rw-r--r--NW4RTools/Models/VertexData.cs95
5 files changed, 404 insertions, 6 deletions
diff --git a/NW4RTools/Models/OpenGL/GLDisplayList.cs b/NW4RTools/Models/OpenGL/GLDisplayList.cs
new file mode 100644
index 0000000..180e89e
--- /dev/null
+++ b/NW4RTools/Models/OpenGL/GLDisplayList.cs
@@ -0,0 +1,31 @@
+using System;
+using OpenTK;
+using OpenTK.Graphics;
+using OpenTK.Graphics.OpenGL;
+
+namespace NW4RTools.Models.OpenGL {
+ public class GLDisplayList : IDisposable {
+ public readonly int ListID;
+
+ public GLDisplayList() {
+ ListID = GL.GenLists(1);
+ }
+
+ void IDisposable.Dispose() {
+ GL.DeleteLists(ListID, 1);
+ }
+
+ public void Begin() {
+ GL.NewList(ListID, ListMode.Compile);
+ }
+
+ public void End() {
+ GL.EndList();
+ }
+
+ public void Execute() {
+ GL.CallList(ListID);
+ }
+ }
+}
+
diff --git a/NW4RTools/Models/OpenGL/GLModel.cs b/NW4RTools/Models/OpenGL/GLModel.cs
new file mode 100644
index 0000000..2e5779e
--- /dev/null
+++ b/NW4RTools/Models/OpenGL/GLModel.cs
@@ -0,0 +1,235 @@
+using System;
+using System.Collections.Generic;
+using NW4RTools;
+using NW4RTools.Models;
+using OpenTK;
+using OpenTK.Graphics;
+using OpenTK.Graphics.OpenGL;
+
+namespace NW4RTools.Models.OpenGL {
+ public class GLModel {
+ public ResFile SourceFile {
+ get; private set;
+ }
+
+ public Model SourceModel {
+ get; private set;
+ }
+
+ public GLModel(ResFile rf, string modelName) {
+ SourceFile = rf;
+ SourceModel = rf.GetGroup<Model>("3DModels(NW4R)")[modelName];
+
+ // cache some stuff
+ m_textureGroup = rf.GetGroup<Texture>("Textures(NW4R)");
+ }
+
+
+
+
+ private ResDict<Texture> m_textureGroup;
+ private Dictionary<string, GLTexture> m_glTextures;
+ private Dictionary<Material, GLDisplayList> m_glMaterials;
+ private Dictionary<Shape, GLDisplayList> m_glShapes;
+
+ public void Prepare(IGraphicsContext context) {
+ // convert every texture
+ m_glTextures = new Dictionary<string, GLTexture>();
+
+ foreach (var material in SourceModel.Materials) {
+ foreach (var texInfo in material.Value.TextureInfos) {
+ if (!m_glTextures.ContainsKey(texInfo.TextureName)) {
+ var newTex = new GLTexture();
+ newTex.Load(m_textureGroup[texInfo.TextureName]);
+ m_glTextures.Add(texInfo.TextureName, newTex);
+ }
+ }
+ }
+
+ // convert every material
+ m_glMaterials = new Dictionary<Material, GLDisplayList>();
+
+ foreach (var material in SourceModel.Materials.Values) {
+ m_glMaterials.Add(material, MakeMaterialDisplayList(material));
+ }
+
+ // convert every shape
+ m_glShapes = new Dictionary<Shape, GLDisplayList>();
+
+ foreach (var shape in SourceModel.Shapes.Values) {
+ m_glShapes.Add(shape, MakeShapeDisplayList(shape));
+ }
+ }
+
+
+
+ private static readonly TextureUnit[] TextureUnits = new TextureUnit[] {
+ TextureUnit.Texture0, TextureUnit.Texture1, TextureUnit.Texture2, TextureUnit.Texture3,
+ TextureUnit.Texture4, TextureUnit.Texture5, TextureUnit.Texture6, TextureUnit.Texture7,
+ TextureUnit.Texture8, TextureUnit.Texture9, TextureUnit.Texture10, TextureUnit.Texture11,
+ TextureUnit.Texture12, TextureUnit.Texture13, TextureUnit.Texture14, TextureUnit.Texture15
+ };
+
+ private Material m_currentMaterial;
+
+ private void ClearCache() {
+ m_currentMaterial = null;
+ }
+
+ private void LoadMaterial(Material m) {
+ if (m == m_currentMaterial)
+ return;
+
+ m_currentMaterial = m;
+ m_glMaterials[m].Execute();
+ }
+
+ private void DrawShape(ByteCode.DrawShapeInstruction insn) {
+ LoadMaterial(SourceModel.Materials[insn.MaterialID]);
+
+ var shape = SourceModel.Shapes[insn.ShapeID];
+ m_glShapes[shape].Execute();
+ }
+
+ public void Render(IGraphicsContext context) {
+ // This'll be fun.
+
+ ClearCache();
+
+ foreach (var insn in SourceModel.Bytecode["DrawOpa"].Instructions) {
+ if (insn is ByteCode.DrawShapeInstruction) {
+ DrawShape(insn as ByteCode.DrawShapeInstruction);
+ }
+ }
+
+ if (SourceModel.Bytecode.ContainsKey("DrawXlu")) {
+ foreach (var insn in SourceModel.Bytecode["DrawXlu"].Instructions) {
+ if (insn is ByteCode.DrawShapeInstruction) {
+ DrawShape(insn as ByteCode.DrawShapeInstruction);
+ }
+ }
+ }
+ }
+
+
+
+ #region Display Lists
+ private GLDisplayList MakeMaterialDisplayList(Material m) {
+ var displayList = new GLDisplayList();
+ displayList.Begin();
+
+ if (m.TextureInfos.Count > 0) {
+ GL.Enable(EnableCap.Texture2D);
+
+ TextureInfo[] texInfoArray = new TextureInfo[8];
+
+ foreach (var texInfo in m.TextureInfos) {
+ texInfoArray[texInfo.TexMapID] = texInfo;
+ }
+
+ for (int i = 0; i < 8; i++) {
+ GL.ActiveTexture(TextureUnits[i]);
+ GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (int)TextureEnvMode.Decal);
+
+ if (texInfoArray[i] == null) {
+ GL.BindTexture(TextureTarget.Texture2D, 0);
+ } else {
+ m_glTextures[texInfoArray[i].TextureName].Bind(TextureTarget.Texture2D);
+ }
+ }
+
+ } else {
+ GL.Disable(EnableCap.Texture2D);
+ }
+
+ displayList.End();
+ return displayList;
+ }
+
+ private GLDisplayList MakeShapeDisplayList(Shape shape) {
+ var displayList = new GLDisplayList();
+ displayList.Begin();
+
+ var firstDL = new InputStream(shape.DisplayList1);
+ firstDL.Seek(0x0C);
+ UInt32 vtxDesc1 = firstDL.ReadUInt32();
+ firstDL.Seek(0x12);
+ UInt32 vtxDesc2 = firstDL.ReadUInt32();
+ firstDL.Seek(0x22);
+ UInt32 vtxAttr1 = firstDL.ReadUInt32();
+ firstDL.Seek(0x28);
+ UInt32 vtxAttr2 = firstDL.ReadUInt32();
+ firstDL.Seek(0x2E);
+ UInt32 vtxAttr3 = firstDL.ReadUInt32();
+
+ var vs = new VertexSettings();
+ vs.SetDesc(vtxDesc1, vtxDesc2);
+ vs.SetAttrFmt(vtxAttr1, vtxAttr2, vtxAttr3);
+
+
+ InputStream dl = new InputStream(shape.DisplayList2);
+
+ while (true) {
+ if (dl.AtEnd)
+ break;
+
+ byte cmd = dl.ReadByte();
+ if (cmd == 0)
+ break;
+
+ PrimitiveType prim = (PrimitiveType)((cmd >> 3) & 7);
+ int vtxCount = dl.ReadUInt16();
+
+ // first, parse it into a list of vertices
+ GXIndexedVertex[] vtxs = new GXIndexedVertex[vtxCount];
+ for (int i = 0; i < vtxCount; i++) {
+ vtxs[i].LoadFrom(dl, vs);
+ }
+
+ switch (prim) {
+ case PrimitiveType.Quads:
+ GL.Begin(BeginMode.Quads);
+ break;
+ case PrimitiveType.Triangles:
+ GL.Begin(BeginMode.Triangles);
+ break;
+ case PrimitiveType.TriangleStrip:
+ GL.Begin(BeginMode.TriangleStrip);
+ break;
+ case PrimitiveType.TriangleFan:
+ GL.Begin(BeginMode.TriangleFan);
+ break;
+ case PrimitiveType.Lines:
+ GL.Begin(BeginMode.Lines);
+ break;
+ case PrimitiveType.LineStrip:
+ GL.Begin(BeginMode.LineStrip);
+ break;
+ case PrimitiveType.Points:
+ GL.Begin(BeginMode.Points);
+ break;
+ default:
+ Console.WriteLine("UNIMPLEMENTED PRIMITIVE TYPE {0}", prim);
+ throw new NotImplementedException();
+ }
+
+ for (int i = 0; i < vtxCount; i++) {
+ for (int j = 0; j < 8; j++) {
+ if (vs.TexCoordDesc[j] != VertexSettings.DescType.None)
+ GL.MultiTexCoord2(TextureUnits[j], shape.TexCoordData[j].Data[vtxs[i].TexCoords[j]]);
+ }
+
+ GL.Vertex3(shape.PosData.Data[vtxs[i].Position]);
+ }
+
+ GL.End();
+ }
+
+ displayList.End();
+ return displayList;
+ }
+
+ #endregion
+ }
+}
+
diff --git a/NW4RTools/Models/OpenGL/GLTexture.cs b/NW4RTools/Models/OpenGL/GLTexture.cs
new file mode 100644
index 0000000..c4cdee6
--- /dev/null
+++ b/NW4RTools/Models/OpenGL/GLTexture.cs
@@ -0,0 +1,46 @@
+using System;
+using NW4RTools;
+using NW4RTools.Models;
+using OpenTK;
+using OpenTK.Graphics;
+using OpenTK.Graphics.OpenGL;
+
+namespace NW4RTools.Models.OpenGL {
+ public class GLTexture : IDisposable {
+ public readonly int TextureID;
+
+ public GLTexture() {
+ TextureID = GL.GenTexture();
+ }
+
+ void IDisposable.Dispose() {
+ GL.DeleteTexture(TextureID);
+ }
+
+ public void Load(Texture tex) {
+ Bind(TextureTarget.Texture2D);
+
+ // todo: check if this is configurable
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMagFilter.Linear);
+
+ //byte[] pixelData = new byte[tex.BaseImage.Width * tex.BaseImage.Height * 4];
+
+ var lb = tex.BaseImage.LockBits(
+ new System.Drawing.Rectangle(0, 0, tex.BaseImage.Width, tex.BaseImage.Height),
+ System.Drawing.Imaging.ImageLockMode.ReadOnly,
+ System.Drawing.Imaging.PixelFormat.Format32bppArgb);
+
+ GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Four,
+ tex.BaseImage.Width, tex.BaseImage.Height, 0,
+ PixelFormat.Bgra, PixelType.UnsignedByte, lb.Scan0);
+
+ tex.BaseImage.UnlockBits(lb);
+ }
+
+ public void Bind(TextureTarget target) {
+ GL.BindTexture(target, TextureID);
+ }
+ }
+}
+
diff --git a/NW4RTools/Models/TextureInfo.cs b/NW4RTools/Models/TextureInfo.cs
index c8bc9a4..57a7169 100644
--- a/NW4RTools/Models/TextureInfo.cs
+++ b/NW4RTools/Models/TextureInfo.cs
@@ -3,7 +3,8 @@ using System;
namespace NW4RTools.Models {
public class TextureInfo {
public string TextureName, PaletteName;
- public UInt32 TexMapID, TlutID, WrapS, WrapT, MinFilt, MagFilt;
+ public UInt32 TexMapID, TlutID, MinFilt, MagFilt;
+ public TextureWrapType WrapS, WrapT;
public float LODBias;
public UInt32 MaxAniso;
public bool BiasClamp, DoEdgeLOD;
diff --git a/NW4RTools/Models/VertexData.cs b/NW4RTools/Models/VertexData.cs
index 5b1a50a..187b9aa 100644
--- a/NW4RTools/Models/VertexData.cs
+++ b/NW4RTools/Models/VertexData.cs
@@ -10,23 +10,24 @@ namespace NW4RTools.Models {
public UInt16 EntryCount;
// Todo, decode data when reading
- public byte[] Data;
+ public byte[] RawData;
+ public float[][] Data;
public VertexDataBase() {
}
- public float[] GetEntry(int index) {
+ public virtual float[] GetEntry(int index) {
float[] ret = new float[GetRealCount()];
float scale = 1.0f / (1 << Fraction);
- //Console.WriteLine("Getting index {0}, count {1} size {2} length {3} offset {4}", index, EntryCount, EntrySize, Data.Length, index * EntrySize);
- InputStream ins = new InputStream(Data);
+ InputStream ins = new InputStream(RawData);
ins.Seek(index * EntrySize);
for (int i = 0; i < ret.Length; i++) {
switch (ComponentType) {
case VertexSettings.CompType.UInt8:
+ ret[i] = (float)ins.ReadByte() * scale;
break;
case VertexSettings.CompType.Int8:
ret[i] = (float)ins.ReadSByte() * scale;
@@ -36,7 +37,6 @@ namespace NW4RTools.Models {
break;
case VertexSettings.CompType.Int16:
ret[i] = (float)ins.ReadInt16() * scale;
- //Console.WriteLine("Output into {0}: {3:X} {1} with scale {2}", i, ret[i], scale, v);
break;
case VertexSettings.CompType.Float32:
ret[i] = ins.ReadFloat();
@@ -49,6 +49,18 @@ namespace NW4RTools.Models {
return ret;
}
+ public void Parse() {
+ if (Data != null)
+ return;
+
+ Data = new float[EntryCount][];
+
+ for (int i = 0; i < EntryCount; i++) {
+ Data[i] = GetEntry(i);
+ }
+ }
+
+
public abstract int GetRealCount();
}
@@ -101,6 +113,79 @@ namespace NW4RTools.Models {
return -1;
}
}
+
+ public override float[] GetEntry(int index) {
+ float[] ret = new float[GetRealCount()];
+
+ InputStream ins = new InputStream(RawData);
+ ins.Seek(index * EntrySize);
+
+ byte r, g, b, a;
+
+ // todo: better solution instead of this cast
+ switch ((VertexSettings.CompClrType)ComponentType) {
+ case VertexSettings.CompClrType.RGB565:
+ ushort v565 = ins.ReadUInt16();
+ r = (byte)((v565 & 0xF800) >> 8);
+ g = (byte)((v565 & 0x07E0) >> 3);
+ b = (byte)((v565 & 0x001F) << 3);
+ a = 255;
+ break;
+
+ case VertexSettings.CompClrType.RGB8:
+ r = ins.ReadByte();
+ g = ins.ReadByte();
+ b = ins.ReadByte();
+ a = 255;
+ break;
+
+ case VertexSettings.CompClrType.RGBX8:
+ r = ins.ReadByte();
+ g = ins.ReadByte();
+ b = ins.ReadByte();
+ a = 255;
+ ins.Skip(1);
+ break;
+
+ case VertexSettings.CompClrType.RGBA4:
+ ushort vA4 = ins.ReadUInt16();
+ r = (byte)((vA4 & 0xF000) >> 8);
+ g = (byte)((vA4 & 0x0F00) >> 4);
+ b = (byte)(vA4 & 0x00F0);
+ a = (byte)((vA4 & 0x000F) << 4);
+ break;
+
+ case VertexSettings.CompClrType.RGBA6:
+ byte v1 = ins.ReadByte();
+ byte v2 = ins.ReadByte();
+ byte v3 = ins.ReadByte();
+ r = (byte)(v1 & 0xF8);
+ g = (byte)(((v1 & 7) << 6) | ((v2 & 0xF0) >> 2));
+ b = (byte)(((v2 & 0xF) << 4) | ((v3 & 0xC) >> 4));
+ a = (byte)((v3 & 0x3F) << 2);
+ break;
+
+ case VertexSettings.CompClrType.RGBA8:
+ r = ins.ReadByte();
+ g = ins.ReadByte();
+ b = ins.ReadByte();
+ a = ins.ReadByte();
+ break;
+
+ default:
+ throw new NotImplementedException(String.Format("unimplemented type {0}", (int)ComponentType));
+ }
+
+ // this is the easiest way I can think of to handle this atm
+ // just use floats for EVERYTHING
+ ret[0] = r * (1.0f / 256);
+ ret[1] = g * (1.0f / 256);
+ ret[2] = b * (1.0f / 256);
+ if (ret.Length > 3)
+ ret[3] = a * (1.0f / 256);
+
+ return ret;
+ }
}