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("3DModels(NW4R)")[modelName]; // cache some stuff m_textureGroup = rf.GetGroup("Textures(NW4R)"); } private ResDict m_textureGroup; private Dictionary m_glTextures; private Dictionary m_glMaterials; private Dictionary m_glShapes; public void Prepare(IGraphicsContext context) { // convert every texture m_glTextures = new Dictionary(); 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(); foreach (var material in SourceModel.Materials.Values) { m_glMaterials.Add(material, MakeMaterialDisplayList(material)); } // convert every shape m_glShapes = new Dictionary(); 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 } }