/// <summary> /// Reads a Chunk attach from a byte array /// </summary> /// <param name="source">Byte source</param> /// <param name="address">Address at which the attach is located</param> /// <param name="imagebase">Imagebase for every address</param> /// <param name="labels">C struct labels</param> /// <returns></returns> public static ChunkAttach Read(byte[] source, uint address, uint imagebase, Dictionary <uint, string> labels) { string name = labels.ContainsKey(address) ? labels[address] : "attach_" + address.ToString("X8"); string identifier = GenerateIdentifier(); uint vertexAddress = source.ToUInt32(address); string vertexName = "vertex_" + identifier; VertexChunk[] vertexChunks = null; if (vertexAddress != 0) { vertexAddress -= imagebase; vertexName = labels.ContainsKey(vertexAddress) ? labels[vertexAddress] : "vertex_" + vertexAddress.ToString("X8"); List <VertexChunk> chunks = new(); VertexChunk cnk = VertexChunk.Read(source, ref vertexAddress); while (cnk != null) { chunks.Add(cnk); cnk = VertexChunk.Read(source, ref vertexAddress); } vertexChunks = chunks.ToArray(); } uint polyAddress = source.ToUInt32(address += 4); string polyName = "poly_" + identifier; PolyChunk[] polyChunks = null; if (polyAddress != 0) { polyAddress -= imagebase; polyName = labels.ContainsKey(polyAddress) ? labels[polyAddress] : "poly_" + polyAddress.ToString("X8"); List <PolyChunk> chunks = new(); PolyChunk cnk = PolyChunk.Read(source, ref polyAddress); while (cnk != null && cnk.Type != ChunkType.End) { chunks.Add(cnk); cnk = PolyChunk.Read(source, ref polyAddress); } polyChunks = chunks.ToArray(); } address += 4; return(new ChunkAttach(vertexChunks, polyChunks) { Name = name, VertexName = vertexName, PolyName = polyName, MeshBounds = Bounds.Read(source, ref address) }); }
public void Load(byte[] data) { int dataOffset = 0; var header = new Header(); header.Load(data, ref dataOffset); for (int i = 0; i < header.ChunkCount; i++) { BaseChunk baseChunk; //Read the first four bytes to get the tag. string tagName = FSHelpers.ReadString(data, dataOffset, 4); switch (tagName) { case "INF1": baseChunk = new InfoChunk(); Info = baseChunk as InfoChunk; break; case "VTX1": baseChunk = new VertexChunk(); Vertexes = baseChunk as VertexChunk; break; case "EVP1": baseChunk = new EnvelopeChunk(); Envelopes = baseChunk as EnvelopeChunk; break; case "DRW1": baseChunk = new DrawChunk(); Draw = baseChunk as DrawChunk; break; case "JNT1": baseChunk = new JointChunk(); Joints = baseChunk as JointChunk; break; case "SHP1": baseChunk = new ShapeChunk(); Shapes = baseChunk as ShapeChunk; break; case "TEX1": baseChunk = new TextureChunk(); Textures = baseChunk as TextureChunk; break; case "MAT3": baseChunk = new Material3Chunk(); Materials = baseChunk as Material3Chunk; break; case "ANK1": default: Console.WriteLine("Found unknown chunk {0}!", tagName); baseChunk = new DefaultChunk(); break; } baseChunk.Load(data, ref dataOffset); } }
/// <summary> /// Reserve a chunk of vertexs inside the buffer's heap. /// </summary> /// <param name="count">Number of vertexs to reserve.</param> /// <returns>An handle to the vertex array resource.</returns> public new VertexChunk Alloc(uint count) { VertexChunk newChunk = new VertexChunk(this, count); return(base.Alloc(newChunk) as VertexChunk); }
static void Main(string[] args) { string filename; if (args.Length > 0) { filename = args[0]; Console.WriteLine("File: {0}", filename); } else { Console.Write("File: "); filename = Console.ReadLine(); } ModelFile model = new ModelFile(filename); switch (model.Format) { case ModelFormat.Basic: foreach (NJS_OBJECT obj in model.Model.GetObjects().Where(obj => obj.Attach is BasicAttach)) { BasicAttach basatt = (BasicAttach)obj.Attach; ChunkAttach cnkatt = new ChunkAttach(true, true) { Name = basatt.Name + "_cnk", Bounds = basatt.Bounds }; obj.Attach = cnkatt; VertexChunk vcnk; bool hasnormal = basatt.Normal?.Length > 0; bool hasvcolor = basatt.Mesh.Any(a => a.VColor != null); if (hasvcolor) { vcnk = new VertexChunk(ChunkType.Vertex_VertexDiffuse8); } else if (hasnormal) { vcnk = new VertexChunk(ChunkType.Vertex_VertexNormal); } else { vcnk = new VertexChunk(ChunkType.Vertex_Vertex); } List <CachedVertex> cache = new List <CachedVertex>(basatt.Vertex.Length); List <List <Strip> > strips = new List <List <Strip> >(); List <List <List <UV> > > uvs = new List <List <List <UV> > >(); foreach (NJS_MESHSET mesh in basatt.Mesh) { List <Strip> polys = new List <Strip>(); List <List <UV> > us = null; bool hasUV = mesh.UV != null; bool hasVColor = mesh.VColor != null; int currentstriptotal = 0; switch (mesh.PolyType) { case Basic_PolyType.Triangles: { List <ushort> tris = new List <ushort>(); Dictionary <ushort, UV> uvmap = new Dictionary <ushort, UV>(); foreach (Poly poly in mesh.Poly) { for (int i = 0; i < 3; i++) { ushort ind = (ushort)cache.AddUnique(new CachedVertex( basatt.Vertex[poly.Indexes[i]], basatt.Normal[poly.Indexes[i]], hasVColor ? mesh.VColor[currentstriptotal] : Color.White, mesh.UV?[currentstriptotal])); if (hasUV) { uvmap[ind] = mesh.UV[currentstriptotal]; } ++currentstriptotal; tris.Add(ind); } } if (hasUV) { us = new List <List <UV> >(); } nvStripifier.GenerateStrips(tris.ToArray(), out var primitiveGroups); // Add strips for (var i = 0; i < primitiveGroups.Length; i++) { var primitiveGroup = primitiveGroups[i]; System.Diagnostics.Debug.Assert(primitiveGroup.Type == PrimitiveType.TriangleStrip); var stripIndices = new ushort[primitiveGroup.Indices.Length]; List <UV> stripuv = new List <UV>(); for (var j = 0; j < primitiveGroup.Indices.Length; j++) { var vertexIndex = primitiveGroup.Indices[j]; stripIndices[j] = vertexIndex; if (hasUV) { stripuv.Add(uvmap[vertexIndex]); } } polys.Add(new Strip(stripIndices, false)); if (hasUV) { us.Add(stripuv); } } } break; case Basic_PolyType.Quads: { List <ushort> tris = new List <ushort>(); Dictionary <ushort, UV> uvmap = new Dictionary <ushort, UV>(); foreach (Poly poly in mesh.Poly) { ushort[] quad = new ushort[4]; for (int i = 0; i < 4; i++) { ushort ind = (ushort)cache.AddUnique(new CachedVertex( basatt.Vertex[poly.Indexes[i]], basatt.Normal[poly.Indexes[i]], hasVColor ? mesh.VColor[currentstriptotal] : Color.White, mesh.UV?[currentstriptotal])); if (hasUV) { uvmap[ind] = mesh.UV[currentstriptotal]; } ++currentstriptotal; quad[i] = ind; } tris.Add(quad[0]); tris.Add(quad[1]); tris.Add(quad[2]); tris.Add(quad[2]); tris.Add(quad[1]); tris.Add(quad[3]); } if (hasUV) { us = new List <List <UV> >(); } nvStripifier.GenerateStrips(tris.ToArray(), out var primitiveGroups); // Add strips for (var i = 0; i < primitiveGroups.Length; i++) { var primitiveGroup = primitiveGroups[i]; System.Diagnostics.Debug.Assert(primitiveGroup.Type == PrimitiveType.TriangleStrip); var stripIndices = new ushort[primitiveGroup.Indices.Length]; List <UV> stripuv = new List <UV>(); for (var j = 0; j < primitiveGroup.Indices.Length; j++) { var vertexIndex = primitiveGroup.Indices[j]; stripIndices[j] = vertexIndex; if (hasUV) { stripuv.Add(uvmap[vertexIndex]); } } polys.Add(new Strip(stripIndices, false)); if (hasUV) { us.Add(stripuv); } } } break; case Basic_PolyType.NPoly: case Basic_PolyType.Strips: if (hasUV) { us = new List <List <UV> >(); } foreach (Strip poly in mesh.Poly.Cast <Strip>()) { List <UV> stripuv = new List <UV>(); ushort[] inds = (ushort[])poly.Indexes.Clone(); for (int i = 0; i < poly.Indexes.Length; i++) { inds[i] = (ushort)cache.AddUnique(new CachedVertex( basatt.Vertex[poly.Indexes[i]], basatt.Normal[poly.Indexes[i]], hasVColor ? mesh.VColor[currentstriptotal] : Color.White)); if (hasUV) { stripuv.Add(mesh.UV[currentstriptotal]); } ++currentstriptotal; } polys.Add(new Strip(inds, poly.Reversed)); if (hasUV) { us.Add(stripuv); } } break; } strips.Add(polys); uvs.Add(us); } foreach (var item in cache) { vcnk.Vertices.Add(item.vertex); if (hasnormal) { vcnk.Normals.Add(item.normal); } if (hasvcolor) { vcnk.Diffuse.Add(item.color); } } vcnk.VertexCount = (ushort)cache.Count; switch (vcnk.Type) { case ChunkType.Vertex_Vertex: vcnk.Size = (ushort)(vcnk.VertexCount * 3 + 1); break; case ChunkType.Vertex_VertexDiffuse8: vcnk.Size = (ushort)(vcnk.VertexCount * 4 + 1); break; case ChunkType.Vertex_VertexNormal: vcnk.Size = (ushort)(vcnk.VertexCount * 6 + 1); break; case ChunkType.Vertex_VertexNormalDiffuse8: vcnk.Size = (ushort)(vcnk.VertexCount * 7 + 1); break; } cnkatt.Vertex.Add(vcnk); for (int i = 0; i < basatt.Mesh.Count; i++) { NJS_MESHSET mesh = basatt.Mesh[i]; NJS_MATERIAL mat = null; if (basatt.Material != null && mesh.MaterialID < basatt.Material.Count) { mat = basatt.Material[mesh.MaterialID]; cnkatt.Poly.Add(new PolyChunkTinyTextureID() { ClampU = mat.ClampU, ClampV = mat.ClampV, FilterMode = mat.FilterMode, FlipU = mat.FlipU, FlipV = mat.FlipV, SuperSample = mat.SuperSample, TextureID = (ushort)mat.TextureID }); cnkatt.Poly.Add(new PolyChunkMaterial() { SourceAlpha = mat.SourceAlpha, DestinationAlpha = mat.DestinationAlpha, Diffuse = mat.DiffuseColor, Specular = mat.SpecularColor, SpecularExponent = (byte)mat.Exponent }); } PolyChunkStrip strip; if (mesh.UV != null) { strip = new PolyChunkStrip(ChunkType.Strip_StripUVN); } else { strip = new PolyChunkStrip(ChunkType.Strip_Strip); } if (mat != null) { strip.IgnoreLight = mat.IgnoreLighting; strip.IgnoreSpecular = mat.IgnoreSpecular; strip.UseAlpha = mat.UseAlpha; strip.DoubleSide = mat.DoubleSided; strip.FlatShading = mat.FlatShading; strip.EnvironmentMapping = mat.EnvironmentMap; } for (int i1 = 0; i1 < strips[i].Count; i1++) { Strip item = strips[i][i1]; UV[] uv2 = null; if (mesh.UV != null) { uv2 = uvs[i][i1].ToArray(); } strip.Strips.Add(new PolyChunkStrip.Strip(item.Reversed, item.Indexes, uv2, null)); } cnkatt.Poly.Add(strip); } } ModelFile.CreateFile(System.IO.Path.ChangeExtension(filename, "sa2mdl"), model.Model, null, null, null, null, null, ModelFormat.Chunk); break; case ModelFormat.Chunk: Vertex[] VertexBuffer = new Vertex[0]; Vertex[] NormalBuffer = new Vertex[0]; foreach (NJS_OBJECT obj in model.Model.GetObjects().Where(obj => obj.Attach is ChunkAttach)) { ChunkAttach cnkatt = (ChunkAttach)obj.Attach; BasicAttach basatt = new BasicAttach() { Name = cnkatt.Name, Bounds = cnkatt.Bounds }; obj.Attach = basatt; if (cnkatt.Vertex != null) { foreach (VertexChunk chunk in cnkatt.Vertex) { if (VertexBuffer.Length < chunk.IndexOffset + chunk.VertexCount) { Array.Resize(ref VertexBuffer, chunk.IndexOffset + chunk.VertexCount); Array.Resize(ref NormalBuffer, chunk.IndexOffset + chunk.VertexCount); } Array.Copy(chunk.Vertices.ToArray(), 0, VertexBuffer, chunk.IndexOffset, chunk.Vertices.Count); Array.Copy(chunk.Normals.ToArray(), 0, NormalBuffer, chunk.IndexOffset, chunk.Normals.Count); } } NJS_MATERIAL material = new NJS_MATERIAL() { UseTexture = true }; int minVtx = int.MaxValue; int maxVtx = int.MinValue; foreach (PolyChunk chunk in cnkatt.Poly) { switch (chunk.Type) { case ChunkType.Bits_BlendAlpha: { PolyChunkBitsBlendAlpha c2 = (PolyChunkBitsBlendAlpha)chunk; material.SourceAlpha = c2.SourceAlpha; material.DestinationAlpha = c2.DestinationAlpha; } break; case ChunkType.Bits_MipmapDAdjust: break; case ChunkType.Bits_SpecularExponent: material.Exponent = ((PolyChunkBitsSpecularExponent)chunk).SpecularExponent; break; case ChunkType.Tiny_TextureID: case ChunkType.Tiny_TextureID2: { PolyChunkTinyTextureID c2 = (PolyChunkTinyTextureID)chunk; material.ClampU = c2.ClampU; material.ClampV = c2.ClampV; material.FilterMode = c2.FilterMode; material.FlipU = c2.FlipU; material.FlipV = c2.FlipV; material.SuperSample = c2.SuperSample; material.TextureID = c2.TextureID; } break; case ChunkType.Material_Diffuse: case ChunkType.Material_Ambient: case ChunkType.Material_DiffuseAmbient: case ChunkType.Material_Specular: case ChunkType.Material_DiffuseSpecular: case ChunkType.Material_AmbientSpecular: case ChunkType.Material_DiffuseAmbientSpecular: case ChunkType.Material_Diffuse2: case ChunkType.Material_Ambient2: case ChunkType.Material_DiffuseAmbient2: case ChunkType.Material_Specular2: case ChunkType.Material_DiffuseSpecular2: case ChunkType.Material_AmbientSpecular2: case ChunkType.Material_DiffuseAmbientSpecular2: { PolyChunkMaterial c2 = (PolyChunkMaterial)chunk; material.SourceAlpha = c2.SourceAlpha; material.DestinationAlpha = c2.DestinationAlpha; if (c2.Diffuse.HasValue) { material.DiffuseColor = c2.Diffuse.Value; } if (c2.Specular.HasValue) { material.SpecularColor = c2.Specular.Value; material.Exponent = c2.SpecularExponent; } } break; case ChunkType.Strip_Strip: case ChunkType.Strip_StripUVN: case ChunkType.Strip_StripUVH: case ChunkType.Strip_StripNormal: case ChunkType.Strip_StripUVNNormal: case ChunkType.Strip_StripUVHNormal: case ChunkType.Strip_StripColor: case ChunkType.Strip_StripUVNColor: case ChunkType.Strip_StripUVHColor: case ChunkType.Strip_Strip2: case ChunkType.Strip_StripUVN2: case ChunkType.Strip_StripUVH2: { PolyChunkStrip c2 = (PolyChunkStrip)chunk; material.DoubleSided = c2.DoubleSide; material.EnvironmentMap = c2.EnvironmentMapping; material.FlatShading = c2.FlatShading; material.IgnoreLighting = c2.IgnoreLight; material.IgnoreSpecular = c2.IgnoreSpecular; material.UseAlpha = c2.UseAlpha; bool hasVColor = false; switch (chunk.Type) { case ChunkType.Strip_StripColor: case ChunkType.Strip_StripUVNColor: case ChunkType.Strip_StripUVHColor: hasVColor = true; break; } bool hasUV = false; switch (chunk.Type) { case ChunkType.Strip_StripUVN: case ChunkType.Strip_StripUVH: case ChunkType.Strip_StripUVNColor: case ChunkType.Strip_StripUVHColor: case ChunkType.Strip_StripUVN2: case ChunkType.Strip_StripUVH2: hasUV = true; break; } List <Strip> strips = new List <Strip>(c2.StripCount); List <UV> uvs = hasUV ? new List <UV>() : null; List <Color> vcolors = hasVColor ? new List <Color>() : null; foreach (PolyChunkStrip.Strip strip in c2.Strips) { minVtx = Math.Min(minVtx, strip.Indexes.Min()); maxVtx = Math.Max(maxVtx, strip.Indexes.Max()); strips.Add(new Strip((ushort[])strip.Indexes.Clone(), strip.Reversed)); if (hasUV) { uvs.AddRange(strip.UVs); } if (hasVColor) { vcolors.AddRange(strip.VColors); } } NJS_MESHSET mesh = new NJS_MESHSET(strips.ToArray(), false, hasUV, hasVColor); if (hasUV) { uvs.CopyTo(mesh.UV); } if (hasVColor) { vcolors.CopyTo(mesh.VColor); } mesh.MaterialID = (ushort)basatt.Material.Count; basatt.Mesh.Add(mesh); basatt.Material.Add(material); material = new NJS_MATERIAL(material.GetBytes(), 0); } break; } } int numVtx = maxVtx - minVtx + 1; basatt.ResizeVertexes(numVtx); Array.Copy(VertexBuffer, minVtx, basatt.Vertex, 0, numVtx); Array.Copy(NormalBuffer, minVtx, basatt.Normal, 0, numVtx); foreach (NJS_MESHSET mesh in basatt.Mesh) { foreach (Poly poly in mesh.Poly) { for (int i = 0; i < poly.Indexes.Length; i++) { poly.Indexes[i] = (ushort)(poly.Indexes[i] - minVtx); } } } } ModelFile.CreateFile(System.IO.Path.ChangeExtension(filename, "sa1mdl"), model.Model, null, null, null, null, null, ModelFormat.Basic); break; } }
private static void ConvertCOL(List <COL> newcollist, Dictionary <string, Attach> visitedAttaches, COL col) { if ((col.SurfaceFlags & SurfaceFlags.Visible) == SurfaceFlags.Visible) { BasicAttach basatt = (BasicAttach)col.Model.Attach; COL newcol = new COL() { Bounds = col.Bounds }; newcol.SurfaceFlags = SurfaceFlags.Visible; newcol.Model = new NJS_OBJECT() { Name = col.Model.Name + "_cnk" }; newcol.Model.Position = col.Model.Position; newcol.Model.Rotation = col.Model.Rotation; newcol.Model.Scale = col.Model.Scale; string newname = basatt.Name + "_cnk"; if (visitedAttaches != null && visitedAttaches.ContainsKey(newname)) { newcol.Model.Attach = visitedAttaches[newname]; } else { ChunkAttach cnkatt = new ChunkAttach(true, true) { Name = newname, Bounds = basatt.Bounds }; if (visitedAttaches != null) { visitedAttaches[newname] = cnkatt; } newcol.Model.Attach = cnkatt; VertexChunk vcnk; bool hasvcolor = basatt.Mesh.Any(a => a.VColor != null); bool hasnormal = !hasvcolor && basatt.Normal?.Length > 0; if (hasvcolor) { vcnk = new VertexChunk(ChunkType.Vertex_VertexDiffuse8); } else if (hasnormal) { vcnk = new VertexChunk(ChunkType.Vertex_VertexNormal); } else { vcnk = new VertexChunk(ChunkType.Vertex_Vertex); } List <CachedVertex> cache = new List <CachedVertex>(basatt.Vertex.Length); List <List <Strip> > strips = new List <List <Strip> >(); List <List <List <UV> > > uvs = new List <List <List <UV> > >(); foreach (NJS_MESHSET mesh in basatt.Mesh) { List <Strip> polys = new List <Strip>(); List <List <UV> > us = null; bool hasUV = mesh.UV != null; bool hasVColor = mesh.VColor != null; int currentstriptotal = 0; switch (mesh.PolyType) { case Basic_PolyType.Triangles: { List <ushort> tris = new List <ushort>(); Dictionary <ushort, UV> uvmap = new Dictionary <ushort, UV>(); foreach (Poly poly in mesh.Poly) { for (int i = 0; i < 3; i++) { ushort ind = (ushort)cache.AddUnique(new CachedVertex( basatt.Vertex[poly.Indexes[i]], basatt.Normal[poly.Indexes[i]], hasVColor ? mesh.VColor[currentstriptotal] : Color.White, mesh.UV?[currentstriptotal])); if (hasUV) { uvmap[ind] = mesh.UV[currentstriptotal]; } ++currentstriptotal; tris.Add(ind); } } if (hasUV) { us = new List <List <UV> >(); } System.Diagnostics.Debug.Assert(nvStripifier.GenerateStrips(tris.ToArray(), out var primitiveGroups)); // Add strips for (var i = 0; i < primitiveGroups.Length; i++) { var primitiveGroup = primitiveGroups[i]; System.Diagnostics.Debug.Assert(primitiveGroup.Type == NvTriStripDotNet.PrimitiveType.TriangleStrip); var stripIndices = new ushort[primitiveGroup.Indices.Length]; List <UV> stripuv = new List <UV>(); for (var j = 0; j < primitiveGroup.Indices.Length; j++) { var vertexIndex = primitiveGroup.Indices[j]; stripIndices[j] = vertexIndex; if (hasUV) { stripuv.Add(uvmap[vertexIndex]); } } polys.Add(new Strip(stripIndices, false)); if (hasUV) { us.Add(stripuv); } } } break; case Basic_PolyType.Quads: { List <ushort> tris = new List <ushort>(); Dictionary <ushort, UV> uvmap = new Dictionary <ushort, UV>(); foreach (Poly poly in mesh.Poly) { ushort[] quad = new ushort[4]; for (int i = 0; i < 4; i++) { ushort ind = (ushort)cache.AddUnique(new CachedVertex( basatt.Vertex[poly.Indexes[i]], basatt.Normal[poly.Indexes[i]], hasVColor ? mesh.VColor[currentstriptotal] : Color.White, mesh.UV?[currentstriptotal])); if (hasUV) { uvmap[ind] = mesh.UV[currentstriptotal]; } ++currentstriptotal; quad[i] = ind; } tris.Add(quad[0]); tris.Add(quad[1]); tris.Add(quad[2]); tris.Add(quad[2]); tris.Add(quad[1]); tris.Add(quad[3]); } if (hasUV) { us = new List <List <UV> >(); } System.Diagnostics.Debug.Assert(nvStripifier.GenerateStrips(tris.ToArray(), out var primitiveGroups)); // Add strips for (var i = 0; i < primitiveGroups.Length; i++) { var primitiveGroup = primitiveGroups[i]; System.Diagnostics.Debug.Assert(primitiveGroup.Type == NvTriStripDotNet.PrimitiveType.TriangleStrip); var stripIndices = new ushort[primitiveGroup.Indices.Length]; List <UV> stripuv = new List <UV>(); for (var j = 0; j < primitiveGroup.Indices.Length; j++) { var vertexIndex = primitiveGroup.Indices[j]; stripIndices[j] = vertexIndex; if (hasUV) { stripuv.Add(uvmap[vertexIndex]); } } polys.Add(new Strip(stripIndices, false)); if (hasUV) { us.Add(stripuv); } } } break; case Basic_PolyType.NPoly: case Basic_PolyType.Strips: if (hasUV) { us = new List <List <UV> >(); } foreach (Strip poly in mesh.Poly.Cast <Strip>()) { List <UV> stripuv = new List <UV>(); ushort[] inds = (ushort[])poly.Indexes.Clone(); for (int i = 0; i < poly.Indexes.Length; i++) { inds[i] = (ushort)cache.AddUnique(new CachedVertex( basatt.Vertex[poly.Indexes[i]], basatt.Normal[poly.Indexes[i]], hasVColor ? mesh.VColor[currentstriptotal] : Color.White)); if (hasUV) { stripuv.Add(mesh.UV[currentstriptotal]); } ++currentstriptotal; } polys.Add(new Strip(inds, poly.Reversed)); if (hasUV) { us.Add(stripuv); } } break; } strips.Add(polys); uvs.Add(us); } foreach (var item in cache) { vcnk.Vertices.Add(item.vertex); if (hasnormal) { vcnk.Normals.Add(item.normal); } if (hasvcolor) { vcnk.Diffuse.Add(item.color); } } vcnk.VertexCount = (ushort)cache.Count; switch (vcnk.Type) { case ChunkType.Vertex_Vertex: vcnk.Size = (ushort)(vcnk.VertexCount * 3 + 1); break; case ChunkType.Vertex_VertexDiffuse8: vcnk.Size = (ushort)(vcnk.VertexCount * 4 + 1); break; case ChunkType.Vertex_VertexNormal: vcnk.Size = (ushort)(vcnk.VertexCount * 6 + 1); break; } cnkatt.Vertex.Add(vcnk); for (int i = 0; i < basatt.Mesh.Count; i++) { NJS_MESHSET mesh = basatt.Mesh[i]; NJS_MATERIAL mat = null; if (basatt.Material != null && mesh.MaterialID < basatt.Material.Count) { mat = basatt.Material[mesh.MaterialID]; cnkatt.Poly.Add(new PolyChunkTinyTextureID() { ClampU = mat.ClampU, ClampV = mat.ClampV, FilterMode = mat.FilterMode, FlipU = mat.FlipU, FlipV = mat.FlipV, SuperSample = mat.SuperSample, TextureID = (ushort)mat.TextureID }); cnkatt.Poly.Add(new PolyChunkMaterial() { SourceAlpha = mat.SourceAlpha, DestinationAlpha = mat.DestinationAlpha, Diffuse = mat.DiffuseColor, Specular = mat.SpecularColor, SpecularExponent = (byte)mat.Exponent }); } PolyChunkStrip strip; if (mesh.UV != null) { strip = new PolyChunkStrip(ChunkType.Strip_StripUVN); } else { strip = new PolyChunkStrip(ChunkType.Strip_Strip); } if (mat != null) { strip.IgnoreLight = mat.IgnoreLighting; strip.IgnoreSpecular = mat.IgnoreSpecular; strip.UseAlpha = mat.UseAlpha; strip.DoubleSide = mat.DoubleSided; strip.FlatShading = mat.FlatShading; strip.EnvironmentMapping = mat.EnvironmentMap; } for (int i1 = 0; i1 < strips[i].Count; i1++) { Strip item = strips[i][i1]; UV[] uv2 = null; if (mesh.UV != null) { uv2 = uvs[i][i1].ToArray(); } strip.Strips.Add(new PolyChunkStrip.Strip(item.Reversed, item.Indexes, uv2, null)); } cnkatt.Poly.Add(strip); } } newcollist.Add(newcol); } if ((col.SurfaceFlags & ~SurfaceFlags.Visible) != 0) { int newflags = col.Flags & 0xF; if (col.SurfaceFlags.HasFlag(SurfaceFlags.Diggable)) { newflags |= 0x20; } if (col.SurfaceFlags.HasFlag(SurfaceFlags.Unclimbable)) { newflags |= 0x80; } if (col.SurfaceFlags.HasFlag(SurfaceFlags.Hurt)) { newflags |= 0x400; } if (col.SurfaceFlags.HasFlag(SurfaceFlags.CannotLand)) { newflags |= 0x1000; } col.Flags = newflags; newcollist.Add(col); } }
static void Main(string[] args) { string filename; if (args.Length > 0) { filename = args[0]; Console.WriteLine("File: {0}", filename); } else { Console.Write("File: "); filename = Console.ReadLine(); } LandTable level = LandTable.LoadFromFile(filename); switch (level.Format) { case LandTableFormat.SA1: { List <COL> newcollist = new List <COL>(); foreach (COL col in level.COL.Where((col) => col.Model != null && col.Model.Attach != null)) { if ((col.SurfaceFlags & SurfaceFlags.Visible) == SurfaceFlags.Visible) { COL newcol = new COL() { Bounds = col.Bounds }; newcol.SurfaceFlags = SurfaceFlags.Visible; newcol.Model = new SonicRetro.SAModel.NJS_OBJECT() { Name = col.Model.Name + "_cnk" }; newcol.Model.Position = col.Model.Position; newcol.Model.Rotation = col.Model.Rotation; newcol.Model.Scale = col.Model.Scale; BasicAttach basatt = (BasicAttach)col.Model.Attach; ChunkAttach cnkatt = new ChunkAttach(true, true) { Name = basatt.Name + "_cnk", Bounds = basatt.Bounds }; newcol.Model.Attach = cnkatt; VertexChunk vcnk; if (basatt.Normal != null && basatt.Normal.Length > 0) { vcnk = new VertexChunk(ChunkType.Vertex_VertexNormal); } else { vcnk = new VertexChunk(ChunkType.Vertex_Vertex); } vcnk.Vertices = new List <Vertex>(basatt.Vertex); if (basatt.Normal != null) { vcnk.Normals = new List <Vertex>(basatt.Normal); } vcnk.VertexCount = (ushort)basatt.Vertex.Length; vcnk.Size = (ushort)((vcnk.Type == ChunkType.Vertex_VertexNormal ? vcnk.VertexCount * 6 : vcnk.VertexCount * 3) + 1); cnkatt.Vertex.Add(vcnk); foreach (NJS_MESHSET mesh in basatt.Mesh) { if (mesh.PolyType != Basic_PolyType.Strips) { Console.WriteLine("Warning: Skipping non-strip mesh in {0} ({1}).", basatt.MeshName, mesh.PolyType); continue; } NJS_MATERIAL mat = null; if (basatt.Material != null && mesh.MaterialID < basatt.Material.Count) { mat = basatt.Material[mesh.MaterialID]; cnkatt.Poly.Add(new PolyChunkBitsBlendAlpha() { SourceAlpha = mat.SourceAlpha, DestinationAlpha = mat.DestinationAlpha }); cnkatt.Poly.Add(new PolyChunkTinyTextureID() { ClampU = mat.ClampU, ClampV = mat.ClampV, FilterMode = mat.FilterMode, FlipU = mat.FlipU, FlipV = mat.FlipV, SuperSample = mat.SuperSample, TextureID = (ushort)mat.TextureID }); cnkatt.Poly.Add(new PolyChunkMaterial() { Diffuse = mat.DiffuseColor, Specular = mat.SpecularColor, SpecularExponent = (byte)mat.Exponent }); } PolyChunkStrip strip; if (mesh.UV != null & mesh.VColor != null) { strip = new PolyChunkStrip(ChunkType.Strip_StripUVNColor); } else if (mesh.UV != null) { strip = new PolyChunkStrip(ChunkType.Strip_StripUVN); } else if (mesh.VColor != null) { strip = new PolyChunkStrip(ChunkType.Strip_StripColor); } else { strip = new PolyChunkStrip(ChunkType.Strip_Strip); } if (mat != null) { strip.IgnoreLight = mat.IgnoreLighting; strip.IgnoreSpecular = mat.IgnoreSpecular; strip.UseAlpha = mat.UseAlpha; strip.DoubleSide = mat.DoubleSided; strip.FlatShading = mat.FlatShading; strip.EnvironmentMapping = mat.EnvironmentMap; } int striptotal = 0; foreach (Strip item in mesh.Poly.Cast <Strip>()) { UV[] uvs = null; if (mesh.UV != null) { uvs = new UV[item.Indexes.Length]; Array.Copy(mesh.UV, striptotal, uvs, 0, item.Indexes.Length); } Color[] vcolors = null; if (mesh.VColor != null) { vcolors = new Color[item.Indexes.Length]; Array.Copy(mesh.VColor, striptotal, vcolors, 0, item.Indexes.Length); } strip.Strips.Add(new PolyChunkStrip.Strip(item.Reversed, item.Indexes, uvs, vcolors)); striptotal += item.Indexes.Length; } cnkatt.Poly.Add(strip); } newcollist.Add(newcol); } if ((col.SurfaceFlags & ~SurfaceFlags.Visible) != 0) { col.SurfaceFlags &= ~SurfaceFlags.Visible; newcollist.Add(col); } } level.COL = newcollist; } level.Anim = new List <GeoAnimData>(); level.Tool = "SA Tools Level Converter"; level.SaveToFile(System.IO.Path.ChangeExtension(filename, "sa2lvl"), LandTableFormat.SA2); break; case LandTableFormat.SA2: Vertex[] VertexBuffer = new Vertex[0]; Vertex[] NormalBuffer = new Vertex[0]; foreach (COL col in level.COL.Where((col) => col.Model != null && col.Model.Attach is ChunkAttach)) { ChunkAttach cnkatt = (ChunkAttach)col.Model.Attach; BasicAttach basatt = new BasicAttach() { Name = cnkatt.Name, Bounds = cnkatt.Bounds }; if (cnkatt.Vertex != null) { foreach (VertexChunk chunk in cnkatt.Vertex) { if (VertexBuffer.Length < chunk.IndexOffset + chunk.VertexCount) { Array.Resize(ref VertexBuffer, chunk.IndexOffset + chunk.VertexCount); Array.Resize(ref NormalBuffer, chunk.IndexOffset + chunk.VertexCount); } Array.Copy(chunk.Vertices.ToArray(), 0, VertexBuffer, chunk.IndexOffset, chunk.Vertices.Count); Array.Copy(chunk.Normals.ToArray(), 0, NormalBuffer, chunk.IndexOffset, chunk.Normals.Count); } } NJS_MATERIAL material = new NJS_MATERIAL() { UseTexture = true }; int minVtx = int.MaxValue; int maxVtx = int.MinValue; foreach (PolyChunk chunk in cnkatt.Poly) { switch (chunk.Type) { case ChunkType.Bits_BlendAlpha: { PolyChunkBitsBlendAlpha c2 = (PolyChunkBitsBlendAlpha)chunk; material.SourceAlpha = c2.SourceAlpha; material.DestinationAlpha = c2.DestinationAlpha; } break; case ChunkType.Bits_MipmapDAdjust: break; case ChunkType.Bits_SpecularExponent: material.Exponent = ((PolyChunkBitsSpecularExponent)chunk).SpecularExponent; break; case ChunkType.Tiny_TextureID: case ChunkType.Tiny_TextureID2: { PolyChunkTinyTextureID c2 = (PolyChunkTinyTextureID)chunk; material.ClampU = c2.ClampU; material.ClampV = c2.ClampV; material.FilterMode = c2.FilterMode; material.FlipU = c2.FlipU; material.FlipV = c2.FlipV; material.SuperSample = c2.SuperSample; material.TextureID = c2.TextureID; } break; case ChunkType.Material_Diffuse: case ChunkType.Material_Ambient: case ChunkType.Material_DiffuseAmbient: case ChunkType.Material_Specular: case ChunkType.Material_DiffuseSpecular: case ChunkType.Material_AmbientSpecular: case ChunkType.Material_DiffuseAmbientSpecular: case ChunkType.Material_Diffuse2: case ChunkType.Material_Ambient2: case ChunkType.Material_DiffuseAmbient2: case ChunkType.Material_Specular2: case ChunkType.Material_DiffuseSpecular2: case ChunkType.Material_AmbientSpecular2: case ChunkType.Material_DiffuseAmbientSpecular2: { PolyChunkMaterial c2 = (PolyChunkMaterial)chunk; if (c2.Diffuse.HasValue) { material.DiffuseColor = c2.Diffuse.Value; } if (c2.Specular.HasValue) { material.SpecularColor = c2.Specular.Value; material.Exponent = c2.SpecularExponent; } } break; case ChunkType.Strip_Strip: case ChunkType.Strip_StripUVN: case ChunkType.Strip_StripUVH: case ChunkType.Strip_StripNormal: case ChunkType.Strip_StripUVNNormal: case ChunkType.Strip_StripUVHNormal: case ChunkType.Strip_StripColor: case ChunkType.Strip_StripUVNColor: case ChunkType.Strip_StripUVHColor: case ChunkType.Strip_Strip2: case ChunkType.Strip_StripUVN2: case ChunkType.Strip_StripUVH2: { PolyChunkStrip c2 = (PolyChunkStrip)chunk; material.DoubleSided = c2.DoubleSide; material.EnvironmentMap = c2.EnvironmentMapping; material.FlatShading = c2.FlatShading; material.IgnoreLighting = c2.IgnoreLight; material.IgnoreSpecular = c2.IgnoreSpecular; material.UseAlpha = c2.UseAlpha; bool hasVColor = false; switch (chunk.Type) { case ChunkType.Strip_StripColor: case ChunkType.Strip_StripUVNColor: case ChunkType.Strip_StripUVHColor: hasVColor = true; break; } bool hasUV = false; switch (chunk.Type) { case ChunkType.Strip_StripUVN: case ChunkType.Strip_StripUVH: case ChunkType.Strip_StripUVNColor: case ChunkType.Strip_StripUVHColor: case ChunkType.Strip_StripUVN2: case ChunkType.Strip_StripUVH2: hasUV = true; break; } List <Strip> strips = new List <Strip>(c2.StripCount); List <UV> uvs = hasUV ? new List <UV>() : null; List <Color> vcolors = hasVColor ? new List <Color>() : null; foreach (PolyChunkStrip.Strip strip in c2.Strips) { minVtx = Math.Min(minVtx, strip.Indexes.Min()); maxVtx = Math.Max(maxVtx, strip.Indexes.Max()); strips.Add(new Strip(strip.Indexes, strip.Reversed)); if (hasUV) { uvs.AddRange(strip.UVs); } if (hasVColor) { vcolors.AddRange(strip.VColors); } } NJS_MESHSET mesh = new NJS_MESHSET(strips.ToArray(), false, hasUV, hasVColor); if (hasUV) { uvs.CopyTo(mesh.UV); } if (hasVColor) { vcolors.CopyTo(mesh.VColor); } mesh.MaterialID = (ushort)basatt.Material.Count; basatt.Mesh.Add(mesh); basatt.Material.Add(material); material = new NJS_MATERIAL(material.GetBytes(), 0); } break; } } int numVtx = maxVtx - minVtx + 1; basatt.ResizeVertexes(numVtx); Array.Copy(VertexBuffer, minVtx, basatt.Vertex, 0, numVtx); Array.Copy(NormalBuffer, minVtx, basatt.Normal, 0, numVtx); foreach (NJS_MESHSET mesh in basatt.Mesh) { foreach (Poly poly in mesh.Poly) { for (int i = 0; i < poly.Indexes.Length; i++) { poly.Indexes[i] = (ushort)(poly.Indexes[i] - minVtx); } } } col.Model.Attach = basatt; } level.Anim = new List <GeoAnimData>(); level.Tool = "SA Tools Level Converter"; level.SaveToFile(System.IO.Path.ChangeExtension(filename, "sa1lvl"), LandTableFormat.SA1); break; } }
public static ChunkAttach ToChunk(this BasicAttach basatt) { ChunkAttach cnkatt = new ChunkAttach(true, true) { Name = basatt.Name + "_cnk", Bounds = basatt.Bounds }; VertexChunk vcnk; bool hasnormal = basatt.Normal?.Length > 0; bool hasvcolor = basatt.Mesh.Any(a => a.VColor != null); if (hasvcolor) { vcnk = new VertexChunk(ChunkType.Vertex_VertexDiffuse8); hasnormal = false; } else if (hasnormal) { vcnk = new VertexChunk(ChunkType.Vertex_VertexNormal); } else { vcnk = new VertexChunk(ChunkType.Vertex_Vertex); } List <CachedVertex> cache = new List <CachedVertex>(basatt.Vertex.Length); List <List <Strip> > strips = new List <List <Strip> >(); List <List <List <UV> > > uvs = new List <List <List <UV> > >(); foreach (NJS_MESHSET mesh in basatt.Mesh) { List <Strip> polys = new List <Strip>(); List <List <UV> > us = null; bool hasUV = mesh.UV != null; bool hasVColor = mesh.VColor != null; int currentstriptotal = 0; switch (mesh.PolyType) { case Basic_PolyType.Triangles: { List <ushort> tris = new List <ushort>(); Dictionary <ushort, UV> uvmap = new Dictionary <ushort, UV>(); foreach (Poly poly in mesh.Poly) { for (int i = 0; i < 3; i++) { ushort ind = (ushort)cache.AddUnique(new CachedVertex( basatt.Vertex[poly.Indexes[i]], basatt.Normal[poly.Indexes[i]], hasVColor ? mesh.VColor[currentstriptotal] : Color.White, mesh.UV?[currentstriptotal])); if (hasUV) { uvmap[ind] = mesh.UV[currentstriptotal]; } ++currentstriptotal; tris.Add(ind); } } if (hasUV) { us = new List <List <UV> >(); } nvStripifier.GenerateStrips(tris.ToArray(), out var primitiveGroups); // Add strips for (var i = 0; i < primitiveGroups.Length; i++) { var primitiveGroup = primitiveGroups[i]; System.Diagnostics.Debug.Assert(primitiveGroup.Type == PrimitiveType.TriangleStrip); var stripIndices = new ushort[primitiveGroup.Indices.Length]; List <UV> stripuv = new List <UV>(); for (var j = 0; j < primitiveGroup.Indices.Length; j++) { var vertexIndex = primitiveGroup.Indices[j]; stripIndices[j] = vertexIndex; if (hasUV) { stripuv.Add(uvmap[vertexIndex]); } } polys.Add(new Strip(stripIndices, false)); if (hasUV) { us.Add(stripuv); } } } break; case Basic_PolyType.Quads: { List <ushort> tris = new List <ushort>(); Dictionary <ushort, UV> uvmap = new Dictionary <ushort, UV>(); foreach (Poly poly in mesh.Poly) { ushort[] quad = new ushort[4]; for (int i = 0; i < 4; i++) { ushort ind = (ushort)cache.AddUnique(new CachedVertex( basatt.Vertex[poly.Indexes[i]], basatt.Normal[poly.Indexes[i]], hasVColor ? mesh.VColor[currentstriptotal] : Color.White, mesh.UV?[currentstriptotal])); if (hasUV) { uvmap[ind] = mesh.UV[currentstriptotal]; } ++currentstriptotal; quad[i] = ind; } tris.Add(quad[0]); tris.Add(quad[1]); tris.Add(quad[2]); tris.Add(quad[2]); tris.Add(quad[1]); tris.Add(quad[3]); } if (hasUV) { us = new List <List <UV> >(); } nvStripifier.GenerateStrips(tris.ToArray(), out var primitiveGroups); // Add strips for (var i = 0; i < primitiveGroups.Length; i++) { var primitiveGroup = primitiveGroups[i]; System.Diagnostics.Debug.Assert(primitiveGroup.Type == PrimitiveType.TriangleStrip); var stripIndices = new ushort[primitiveGroup.Indices.Length]; List <UV> stripuv = new List <UV>(); for (var j = 0; j < primitiveGroup.Indices.Length; j++) { var vertexIndex = primitiveGroup.Indices[j]; stripIndices[j] = vertexIndex; if (hasUV) { stripuv.Add(uvmap[vertexIndex]); } } polys.Add(new Strip(stripIndices, false)); if (hasUV) { us.Add(stripuv); } } } break; case Basic_PolyType.NPoly: case Basic_PolyType.Strips: if (hasUV) { us = new List <List <UV> >(); } foreach (Strip poly in mesh.Poly.Cast <Strip>()) { List <UV> stripuv = new List <UV>(); ushort[] inds = (ushort[])poly.Indexes.Clone(); for (int i = 0; i < poly.Indexes.Length; i++) { inds[i] = (ushort)cache.AddUnique(new CachedVertex( basatt.Vertex[poly.Indexes[i]], basatt.Normal[poly.Indexes[i]], hasVColor ? mesh.VColor[currentstriptotal] : Color.White)); if (hasUV) { stripuv.Add(mesh.UV[currentstriptotal]); } ++currentstriptotal; } polys.Add(new Strip(inds, poly.Reversed)); if (hasUV) { us.Add(stripuv); } } break; } strips.Add(polys); uvs.Add(us); } foreach (var item in cache) { vcnk.Vertices.Add(item.vertex); if (hasnormal) { vcnk.Normals.Add(item.normal); } if (hasvcolor) { vcnk.Diffuse.Add(item.color); } } cnkatt.Vertex.Add(vcnk); for (int i = 0; i < basatt.Mesh.Count; i++) { NJS_MESHSET mesh = basatt.Mesh[i]; NJS_MATERIAL mat = null; if (basatt.Material != null && mesh.MaterialID < basatt.Material.Count) { mat = basatt.Material[mesh.MaterialID]; cnkatt.Poly.Add(new PolyChunkTinyTextureID() { ClampU = mat.ClampU, ClampV = mat.ClampV, FilterMode = mat.FilterMode, FlipU = mat.FlipU, FlipV = mat.FlipV, SuperSample = mat.SuperSample, TextureID = (ushort)mat.TextureID }); cnkatt.Poly.Add(new PolyChunkMaterial() { SourceAlpha = mat.SourceAlpha, DestinationAlpha = mat.DestinationAlpha, Diffuse = mat.DiffuseColor, Specular = mat.SpecularColor, SpecularExponent = (byte)mat.Exponent }); } PolyChunkStrip strip; if (mesh.UV != null) { strip = new PolyChunkStrip(ChunkType.Strip_StripUVN); } else { strip = new PolyChunkStrip(ChunkType.Strip_Strip); } if (mat != null) { strip.IgnoreLight = mat.IgnoreLighting; strip.IgnoreSpecular = mat.IgnoreSpecular; strip.UseAlpha = mat.UseAlpha; strip.DoubleSide = mat.DoubleSided; strip.FlatShading = mat.FlatShading; strip.EnvironmentMapping = mat.EnvironmentMap; } for (int i1 = 0; i1 < strips[i].Count; i1++) { Strip item = strips[i][i1]; UV[] uv2 = null; if (mesh.UV != null) { uv2 = uvs[i][i1].ToArray(); } strip.Strips.Add(new PolyChunkStrip.Strip(item.Reversed, item.Indexes, uv2, null)); } cnkatt.Poly.Add(strip); } return(cnkatt); }
public static void ConvertModelFromChunk(NJObject model, bool optimize = true) { if (model.Parent != null) { throw new FormatException($"Model {model.Name} is not hierarchy root!"); } HashSet <ChunkAttach> attaches = new(); NJObject[] models = model.GetObjects(); foreach (NJObject obj in models) { if (obj.Attach == null) { continue; } if (obj.Attach.Format != AttachFormat.CHUNK) { throw new FormatException("Not all Attaches inside the model are a CHUNK attaches! Cannot convert"); } ChunkAttach atc = (ChunkAttach)obj.Attach; attaches.Add(atc); } Array.Clear(PolyChunkCache, 0, PolyChunkCache.Length); foreach (ChunkAttach atc in attaches) { List <BufferMesh> meshes = new(); BufferVertex[] vertices = null; bool continueWeight = false; if (atc.VertexChunks != null) { for (int i = 0; i < atc.VertexChunks.Length; i++) { VertexChunk cnk = atc.VertexChunks[i]; List <BufferVertex> vertexList = new(); if (!cnk.HasWeight) { for (int j = 0; j < cnk.Vertices.Length; j++) { ChunkVertex vtx = cnk.Vertices[j]; vertexList.Add(new BufferVertex(vtx.Position, vtx.Normal, (ushort)(j + cnk.IndexOffset))); } } else { for (int j = 0; j < cnk.Vertices.Length; j++) { ChunkVertex vtx = cnk.Vertices[j]; vertexList.Add(new BufferVertex(vtx.Position, vtx.Normal, (ushort)(vtx.Index + cnk.IndexOffset), vtx.Weight)); } } vertices = vertexList.ToArray(); continueWeight = cnk.WeightStatus != WeightStatus.Start; if (i < atc.VertexChunks.Length - 1) { meshes.Add(new BufferMesh(vertices, continueWeight)); } } } List <PolyChunk> active = new(); if (atc.PolyChunks != null) { int cacheID = -1; foreach (PolyChunk cnk in atc.PolyChunks) { switch (cnk.Type) { case ChunkType.Bits_CachePolygonList: PolyChunkCachePolygonList cacheListCnk = (PolyChunkCachePolygonList)cnk; cacheID = cacheListCnk.List; if (PolyChunkCache.Length <= cacheID) { Array.Resize(ref PolyChunkCache, cacheID + 1); } PolyChunkCache[cacheID] = new List <PolyChunk>(); break; case ChunkType.Bits_DrawPolygonList: PolyChunkDrawPolygonList drawListCnk = (PolyChunkDrawPolygonList)cnk; active.AddRange(PolyChunkCache[drawListCnk.List]); break; default: if (cacheID > -1) { PolyChunkCache[cacheID].Add(cnk); } else { active.Add(cnk); } break; } } } if (active.Count > 0) { BufferMaterial material = new() { MaterialAttributes = MaterialAttributes.useTexture }; foreach (PolyChunk cnk in active) { switch (cnk.Type) { case ChunkType.Bits_BlendAlpha: PolyChunkBlendAlpha blendCnk = (PolyChunkBlendAlpha)cnk; material.SourceBlendMode = blendCnk.SourceAlpha; material.DestinationBlendmode = blendCnk.DestinationAlpha; break; case ChunkType.Bits_MipmapDAdjust: PolyChunksMipmapDAdjust mipmapCnk = (PolyChunksMipmapDAdjust)cnk; material.MipmapDistanceAdjust = mipmapCnk.MipmapDAdjust; break; case ChunkType.Bits_SpecularExponent: PolyChunkSpecularExponent specularCnk = (PolyChunkSpecularExponent)cnk; material.SpecularExponent = specularCnk.SpecularExponent; break; case ChunkType.Tiny_TextureID: case ChunkType.Tiny_TextureID2: PolyChunkTextureID textureCnk = (PolyChunkTextureID)cnk; material.TextureIndex = textureCnk.TextureID; material.MirrorU = textureCnk.MirrorU; material.MirrorV = textureCnk.MirrorV; material.ClampU = textureCnk.ClampU; material.ClampV = textureCnk.ClampV; material.AnisotropicFiltering = textureCnk.SuperSample; material.TextureFiltering = textureCnk.FilterMode; break; case ChunkType.Material: case ChunkType.Material_Diffuse: case ChunkType.Material_Ambient: case ChunkType.Material_DiffuseAmbient: case ChunkType.Material_Specular: case ChunkType.Material_DiffuseSpecular: case ChunkType.Material_AmbientSpecular: case ChunkType.Material_DiffuseAmbientSpecular: case ChunkType.Material_Diffuse2: case ChunkType.Material_Ambient2: case ChunkType.Material_DiffuseAmbient2: case ChunkType.Material_Specular2: case ChunkType.Material_DiffuseSpecular2: case ChunkType.Material_AmbientSpecular2: case ChunkType.Material_DiffuseAmbientSpecular2: PolyChunkMaterial materialCnk = (PolyChunkMaterial)cnk; material.SourceBlendMode = materialCnk.SourceAlpha; material.DestinationBlendmode = materialCnk.DestinationAlpha; if (materialCnk.Diffuse.HasValue) { material.Diffuse = materialCnk.Diffuse.Value; } if (materialCnk.Ambient.HasValue) { material.Ambient = materialCnk.Ambient.Value; } if (materialCnk.Specular.HasValue) { material.Specular = materialCnk.Specular.Value; material.SpecularExponent = materialCnk.SpecularExponent; } break; case ChunkType.Strip_Strip: case ChunkType.Strip_StripUVN: case ChunkType.Strip_StripUVH: case ChunkType.Strip_StripNormal: case ChunkType.Strip_StripUVNNormal: case ChunkType.Strip_StripUVHNormal: case ChunkType.Strip_StripColor: case ChunkType.Strip_StripUVNColor: case ChunkType.Strip_StripUVHColor: case ChunkType.Strip_Strip2: case ChunkType.Strip_StripUVN2: case ChunkType.Strip_StripUVH2: PolyChunkStrip stripCnk = (PolyChunkStrip)cnk; material.SetAttribute(MaterialAttributes.Flat, stripCnk.FlatShading); material.SetAttribute(MaterialAttributes.noAmbient, stripCnk.IgnoreAmbient); material.SetAttribute(MaterialAttributes.noDiffuse, stripCnk.IgnoreLight); material.SetAttribute(MaterialAttributes.noSpecular, stripCnk.IgnoreSpecular); material.SetAttribute(MaterialAttributes.normalMapping, stripCnk.EnvironmentMapping); material.UseAlpha = stripCnk.UseAlpha; material.Culling = !stripCnk.DoubleSide; List <BufferCorner> corners = new(); List <uint> triangles = new(); foreach (var s in stripCnk.Strips) { uint l = (uint)corners.Count; bool rev = s.Reversed; for (uint i = 2; i < s.Corners.Length; i++) { uint li = l + i; if (!rev) { triangles.AddRange(new uint[] { li - 2, li - 1, li }); } else { triangles.AddRange(new uint[] { li - 1, li - 2, li }); } rev = !rev; } foreach (var c in s.Corners) { corners.Add(new BufferCorner(c.Index, c.Color, c.Texcoord)); } } if (vertices != null) { meshes.Add(new BufferMesh(vertices, continueWeight, corners.ToArray(), triangles.ToArray(), material.Clone())); vertices = null; } else { meshes.Add(new BufferMesh(corners.ToArray(), triangles.ToArray(), material.Clone())); } break; } } } else if (vertices != null) { meshes.Add(new BufferMesh(vertices, continueWeight)); } if (optimize) { for (int i = 0; i < meshes.Count; i++) { meshes[i].Optimize(); } } atc.MeshData = meshes.ToArray(); } }