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);
        }