public void Convert(ObjFile Obj, int Group, List<NTexture> Textures, uint BaseOffset) { /* Illegal group number? */ if (Obj.Groups.Count < Group) return; /* Create lists, etc. */ List<byte> DList = new List<byte>(); List<SurfaceBundle> SurfBundles = new List<SurfaceBundle>(); List<NVertex> VertList = new List<NVertex>(); List<byte> VertData = new List<byte>(); /* Parse all known materials */ foreach (ObjFile.Material Mat in Obj.Materials) { /* Create new surface bundle */ SurfaceBundle Surf = new SurfaceBundle(); /* Assign material and create triangle list */ Surf.Material = Mat; Surf.Triangles = new List<ObjFile.Triangle>(); /* Parse triangles and group appropriate tris to bundle */ foreach (ObjFile.Triangle Tri in Obj.Groups[Group].Triangles) { /* If tri's material name matches current material, add it to bundle */ if (Tri.MaterialName == Mat.Name) Surf.Triangles.Add(Tri); } /* Add new surface bundle to list */ if (Surf.Triangles.Count != 0) SurfBundles.Add(Surf); } /* Parse surface bundles to create the actual display list */ foreach (SurfaceBundle Surf in SurfBundles) { /* General variables, etc. */ List<byte> AsmTris = new List<byte>(); bool CommToggle = true; /* Generate initial commands */ Helpers.Append64(ref DList, NoParam(GBI.G_RDPPIPESYNC)); Helpers.Append64(ref DList, SetTextureLOD(GBI.G_TL_TILE)); Helpers.Append64(ref DList, Texture(-1, -1, 0, GBI.G_TX_RENDERTILE, GBI.G_ON)); /* Get texture information */ NTexture ThisTexture = Textures[Obj.Materials.IndexOf(Surf.Material)]; /* Texture variables */ float TexXR = Surf.Material.Width / (32.0f * TexScale); float TexYR = Surf.Material.Height / (32.0f * TexScale); int TexPal = 0; /* Insert texture loading commands */ InsertTextureLoad(ref DList, Surf.Material.Width, Surf.Material.Height, ThisTexture, TexPal, GBI.G_TX_RENDERTILE, Obj.Groups[Group].TileS, Obj.Groups[Group].TileT, 0, 0); if (Obj.Groups[Group].MultiTexMaterial != -1) InsertTextureLoad(ref DList, Obj.Materials[Obj.Groups[Group].MultiTexMaterial].Width, Obj.Materials[Obj.Groups[Group].MultiTexMaterial].Height, Textures[Obj.Groups[Group].MultiTexMaterial], TexPal, GBI.G_TX_RENDERTILE + 1, Obj.Groups[Group].TileS, Obj.Groups[Group].TileT, Obj.Groups[Group].ShiftS, Obj.Groups[Group].ShiftT); /* Is surface translucent? (needed later) */ bool IsTranslucent = ((TintAlpha >> 24) != 255); /* Generate GeometryMode commands */ //Helpers.Append64(ref DList, ClearGeometryMode(GBI.G_TEXTURE_GEN | GBI.G_TEXTURE_GEN_LINEAR | (Culling == false ? GBI.G_CULL_BACK : 0))); //Helpers.Append64(ref DList, SetGeometryMode(GBI.G_FOG | GBI.G_LIGHTING | (IsOutdoors == true ? 0 : GBI.G_SHADING_SMOOTH))); if (IsOutdoors == true) { if (IsTranslucent == true) { Helpers.Append64(ref DList, 0xD9F3FBFF00000000); Helpers.Append64(ref DList, 0xD9FFFFFF00030000); } else { Helpers.Append64(ref DList, 0xD9F3FFFF00000000); Helpers.Append64(ref DList, 0xD9FFFFFF00030400); } } else { if (IsTranslucent == true) { Helpers.Append64(ref DList, 0xD9F1FBFF00000000); Helpers.Append64(ref DList, 0xD9FFFFFF00010000); } else { Helpers.Append64(ref DList, 0xD9F1FFFF00000000); Helpers.Append64(ref DList, 0xD9FFFFFF00010400); } } /* Generate SetCombine/RenderMode commands */ if (IsTranslucent == true) { /* Translucent surface */ Helpers.Append64(ref DList, SetCombine(0x167E03, 0xFF0FFDFF)); Helpers.Append64(ref DList, SetRenderMode(0x1C, 0xC81049D8)); } else if (ThisTexture.HasAlpha == true) { /* Texture with alpha channel */ Helpers.Append64(ref DList, SetCombine(0x127E03, 0xFFFFF3F8)); Helpers.Append64(ref DList, SetRenderMode(0x1C, 0xC8103078)); } else { /* Solid surface */ if (Obj.Groups[Group].MultiTexMaterial != -1) Helpers.Append64(ref DList, SetCombine(0x267E04, 0x1FFCFDF8)); else Helpers.Append64(ref DList, SetCombine(0x127E03, 0xFFFFFDF8)); Helpers.Append64(ref DList, SetRenderMode(0x1C, 0xC8113078)); } /* Insert SetPrimColor command */ Helpers.Append64(ref DList, SetPrimColor(TintAlpha)); /* Parse triangles, generate VTX and TRI commands */ /* Very heavily based on code from spinout's .obj importer r13 */ foreach (ObjFile.Triangle Tri in Surf.Triangles) { int TriIndex = Surf.Triangles.IndexOf(Tri); int[] TriPoints = new int[3]; for (int i = 0; i < 3; i++) { NVertex NewVert = new NVertex( new Vector3d(Obj.Vertices[Tri.VertIndex[i]].X, Obj.Vertices[Tri.VertIndex[i]].Y, Obj.Vertices[Tri.VertIndex[i]].Z), new Vector2d(Obj.TextureCoordinates[Tri.TexCoordIndex[i]].U * TexXR, Obj.TextureCoordinates[Tri.TexCoordIndex[i]].V * TexYR), new Color4(Obj.Materials[Obj.Materials.IndexOf(Surf.Material)].Kd[0] * 255, Obj.Materials[Obj.Materials.IndexOf(Surf.Material)].Kd[1] * 255, Obj.Materials[Obj.Materials.IndexOf(Surf.Material)].Kd[2] * 255, 0xFF), //new Color4(0x7F, 0x7F, 0x7F, 0xFF), // dunno <.< Obj.Vertices[Tri.VertIndex[i]].VN); int VtxNo = VertList.FindIndex(FindObj => FindObj.Position == NewVert.Position && FindObj.TexCoord == NewVert.TexCoord && FindObj.Colors == NewVert.Colors && FindObj.Normals == NewVert.Normals); if (VtxNo == -1) { if (VertList.Count <= 29 + i) VertList.Add(NewVert); else throw new Exception("Vertex buffer overflow; this should never happen!"); VtxNo = VertList.Count - 1; } TriPoints[i] = (VtxNo << 1); } Helpers.Append32(ref AsmTris, (uint)(((CommToggle ? 0x06 : 0x00) << 24) | (TriPoints[0] << 16) | (TriPoints[1] << 8) | TriPoints[2])); CommToggle = !CommToggle; if (VertList.Count > 29 || TriIndex == Surf.Triangles.Count - 1) { uint VertOffset = BaseOffset + (uint)VertData.Count; for (int j = 0; j < VertList.Count; j++) { Helpers.Append16(ref VertData, (ushort)(System.Convert.ToInt16(VertList[j].Position.X * Scale))); Helpers.Append16(ref VertData, (ushort)(System.Convert.ToInt16(VertList[j].Position.Y * Scale))); Helpers.Append16(ref VertData, (ushort)(System.Convert.ToInt16(VertList[j].Position.Z * Scale))); Helpers.Append16(ref VertData, 0); Helpers.Append16(ref VertData, (ushort)(System.Convert.ToInt32(VertList[j].TexCoord.X * 1024.0f) & 0xFFFF)); Helpers.Append16(ref VertData, (ushort)(System.Convert.ToInt32(VertList[j].TexCoord.Y * 1024.0f) & 0xFFFF)); if (IsOutdoors == true) { VertData.Add((byte)System.Convert.ToByte(((int)(VertList[j].Normals.X * 255.0f)) & 0xFF)); VertData.Add((byte)System.Convert.ToByte(((int)(VertList[j].Normals.Y * 255.0f)) & 0xFF)); VertData.Add((byte)System.Convert.ToByte(((int)(VertList[j].Normals.Z * 255.0f)) & 0xFF)); } else { uint Color = (uint)VertList[j].Colors.ToArgb(); VertData.Add((byte)((Color >> 16) & 0xFF)); VertData.Add((byte)((Color >> 8) & 0xFF)); VertData.Add((byte)(Color & 0xFF)); } VertData.Add(0xFF); } if ((AsmTris.Count & 4) != 0) { AsmTris[AsmTris.Count - 4] = 0x05; Helpers.Append32(ref AsmTris, 0); } Helpers.Append64(ref DList, ((ulong)(Helpers.ShiftL(GBI.G_VTX, 24, 8) | (uint)(VertList.Count << 12) | (uint)(VertList.Count * 2)) << 32) | (0x03 << 24 | VertOffset)); DList.AddRange(AsmTris); /* Determine minimum/maximum coordinate changes... */ foreach (NVertex Vtx in VertList) { /* Minimum... */ MinCoordinate.X = Math.Min(MinCoordinate.X, Vtx.Position.X * Scale); MinCoordinate.Y = Math.Min(MinCoordinate.Y, Vtx.Position.Y * Scale); MinCoordinate.Z = Math.Min(MinCoordinate.Z, Vtx.Position.Z * Scale); /* Maximum... */ MaxCoordinate.X = Math.Max(MaxCoordinate.X, Vtx.Position.X * Scale); MaxCoordinate.Y = Math.Max(MaxCoordinate.Y, Vtx.Position.Y * Scale); MaxCoordinate.Z = Math.Max(MaxCoordinate.Z, Vtx.Position.Z * Scale); } VertList.Clear(); AsmTris.Clear(); CommToggle = true; } } } /* End of display list */ Helpers.Append64(ref DList, NoParam(GBI.G_ENDDL)); /* Finish conversion */ List<byte> FinalData = new List<byte>(); FinalData.AddRange(VertData); FinalData.AddRange(DList); Data = FinalData.ToArray(); Offset = (uint)(BaseOffset + VertData.Count); }