private void RefreshTextures() { if (RootJOBJ == null) { return; } Surfaces.Clear(); tobjToSurface.Clear(); var tobjs = GetTOBJS(); List <HSD_Image> Process = new List <HSD_Image>(); foreach (var tobj in tobjs) { var surface = new SBSurface(); surface.Name = "TOBJ_" + tobjs.IndexOf(tobj); surface.Arrays.Add(new MipArray() { Mipmaps = new List <byte[]>() { tobj.GetDecodedImageData() } }); surface.Width = tobj.ImageData.Width; surface.Height = tobj.ImageData.Height; surface.PixelFormat = PixelFormat.Bgra; surface.PixelType = PixelType.UnsignedByte; surface.InternalFormat = InternalFormat.Rgba; tobjToSurface.Add(tobj._s, surface); Surfaces.Add(surface); } }
/// <summary> /// Binds a surface to the shader /// </summary> /// <param name="shader"></param> /// <param name="ssbhScene"></param> /// <param name="surface"></param> /// <param name="surfaceInfo"></param> /// <param name="attributeName"></param> /// <param name="TextureUnit"></param> private void BindSurface(Shader shader, SBSceneSSBH ssbhScene, SBSurface surface, DefaultTextureName surfaceInfo, string attributeName, int TextureUnit) { if (surface != null) { if (ssbhScene.surfaceToRenderTexture.ContainsKey(surface)) { shader.SetTexture(attributeName, ssbhScene.surfaceToRenderTexture[surface], TextureUnit); return; } } shader.SetTexture(attributeName, DefaultTextures.Instance.GetTextureByName(surfaceInfo.DefaultTexture), TextureUnit); }
private SBSurface OpenFile(string FilePath) { SBSurface Surface = null; if (FilePath.EndsWith(".nutexb")) { Surface = IO_NUTEXB.Open(FilePath); } if (FilePath.EndsWith(".dds")) { Surface = IO_DDS.Import(FilePath); } return(Surface); }
/// <summary> /// Binds the material to shader /// </summary> /// <param name="scene"></param> /// <param name="shader"></param> public void Bind(SBScene scene, Shader shader) { var ssbhScene = (SBSceneSSBH)scene; BindRasterizerState(); int TextureUnit = 0; foreach (var p in Properties) { if (shader.GetUniformLocation(p.Name) != -1 || shader.GetAttribLocation(p.Name) != -1) { if (p.PropertyType == typeof(SBMatAttrib <string>)) { var value = ((SBMatAttrib <string>)MatAttribs[p.Name]).AnimatedValue; SBSurface surface = ssbhScene.nameToSurface.ContainsKey(value) ? ssbhScene.nameToSurface[value] : null; var surfaceInfo = nameToDefaultTexture[p.Name]; BindSurface(shader, ssbhScene, surface, surfaceInfo, p.Name, TextureUnit++); } else if (p.PropertyType == typeof(SBMatAttrib <Vector4>)) { shader.SetVector4(p.Name, ((SBMatAttrib <Vector4>)MatAttribs[p.Name]).AnimatedValue); } else if (p.PropertyType == typeof(SBMatAttrib <float>)) { shader.SetFloat(p.Name, ((SBMatAttrib <float>)MatAttribs[p.Name]).AnimatedValue); } else if (p.PropertyType == typeof(SBMatAttrib <bool>)) { shader.SetBoolToInt(p.Name, ((SBMatAttrib <bool>)MatAttribs[p.Name]).AnimatedValue); } else if (p.PropertyType == typeof(bool)) { var value = p.GetValue(this); // slow shader.SetBoolToInt(p.Name, (bool)value); } } } shader.SetTexture("diffusePbrCube", DefaultTextures.Instance.diffusePbr, TextureUnit++); shader.SetTexture("specularPbrCube", DefaultTextures.Instance.specularPbr, TextureUnit++); shader.SetTexture("iblLut", DefaultTextures.Instance.iblLut, TextureUnit++); shader.SetTexture("uvPattern", DefaultTextures.Instance.uvPattern, TextureUnit++); }
public static void Export(string FileName, SBSurface surface) { using (BinaryWriter writer = new BinaryWriter(new FileStream(FileName, FileMode.Create))) { writer.Write(SwitchSwizzler.CreateBuffer(surface)); uint ImageSize = (uint)writer.BaseStream.Position; foreach (var mip in surface.Arrays) { foreach (var m in mip.Mipmaps) { writer.Write(m.Length); } for (int i = mip.Mipmaps.Count; i < 0x10; i++) { writer.Write(0); } } writer.Write(new char[] { ' ', 'X', 'N', 'T' }); writer.Write(surface.Name.ToCharArray()); writer.Write(new byte[0x40 - surface.Name.Length]); writer.Write(surface.Width); writer.Write(surface.Height); writer.Write(surface.Depth); writer.Write((byte)TexFormatByInternalFormat(surface.InternalFormat)); // format writer.Write((byte)4); // unknown usually 4 writer.Write((short)0); // pad writer.Write(surface.IsCubeMap ? 9 : 4); // unknown usually 4 9 for cubemap writer.Write(surface.Arrays[0].Mipmaps.Count); writer.Write(0x1000); // alignment writer.Write(surface.Arrays.Count); // array count writer.Write(ImageSize); writer.Write(new char[] { ' ', 'X', 'E', 'T' }); writer.Write((short)1); // version major writer.Write((short)2); // version minor } }
/** * Ported from https://github.com/KillzXGaming/Switch-Toolbox */ public static byte[] GetImageData(SBSurface surface, byte[] ImageData, int ArrayLevel, int MipLevel, int MipCount, int target = 1) { uint bpp = TextureFormatInfo.GetBPP(surface.InternalFormat); uint blkWidth = TextureFormatInfo.GetBlockWidth(surface.InternalFormat); uint blkHeight = TextureFormatInfo.GetBlockHeight(surface.InternalFormat); uint blkDepth = TextureFormatInfo.GetBlockDepth(surface.InternalFormat); uint blockHeight = GetBlockHeight(DivRoundUp((uint)surface.Height, blkHeight)); int BlockHeightLog2 = Convert.ToString(blockHeight, 2).Length - 1; uint Pitch = 0; uint DataAlignment = 512; uint TileMode = 0; int linesPerBlockHeight = (1 << BlockHeightLog2) * 8; uint ArrayOffset = 0; for (int arrayLevel = 0; arrayLevel < surface.ArrayCount; arrayLevel++) { uint SurfaceSize = 0; int blockHeightShift = 0; List <uint> MipOffsets = new List <uint>(); for (int mipLevel = 0; mipLevel < MipCount; mipLevel++) { uint width = (uint)Math.Max(1, surface.Width >> mipLevel); uint height = (uint)Math.Max(1, surface.Height >> mipLevel); uint depth = (uint)Math.Max(1, surface.Depth >> mipLevel); uint size = DivRoundUp(width, blkWidth) * DivRoundUp(height, blkHeight) * bpp; if (Pow2RoundUp(DivRoundUp(height, blkWidth)) < linesPerBlockHeight) { blockHeightShift += 1; } //SBConsole.WriteLine(height + " " + blkWidth + " " + Pow2RoundUp(DivRoundUp(height, blkWidth)) + " " + blockHeight + " " + blockHeightShift + " " + linesPerBlockHeight + " " + BlockHeightLog2 + " " + (int)Math.Max(0, BlockHeightLog2 - blockHeightShift)); uint width__ = DivRoundUp(width, blkWidth); uint height__ = DivRoundUp(height, blkHeight); //Calculate the mip size instead byte[] AlignedData = new byte[(RoundUp(SurfaceSize, DataAlignment) - SurfaceSize)]; SurfaceSize += (uint)AlignedData.Length; MipOffsets.Add(SurfaceSize); //Get the first mip offset and current one and the total image size int msize = (int)((MipOffsets[0] + ImageData.Length - MipOffsets[mipLevel]) / surface.ArrayCount); if (msize > ImageData.Length - (ArrayOffset + MipOffsets[mipLevel])) { msize = (int)(ImageData.Length - (ArrayOffset + MipOffsets[mipLevel])); } byte[] data_ = new byte[msize]; if (ArrayLevel == arrayLevel && MipLevel == mipLevel) { Array.Copy(ImageData, ArrayOffset + MipOffsets[mipLevel], data_, 0, msize); } try { Pitch = RoundUp(width__ * bpp, 64); SurfaceSize += Pitch * RoundUp(height__, Math.Max(1, blockHeight >> blockHeightShift) * 8); if (ArrayLevel == arrayLevel && MipLevel == mipLevel) { //SBConsole.WriteLine($"{width} {height} {blkWidth} {blkHeight} {target} {bpp} {TileMode} {(int)Math.Max(0, BlockHeightLog2 - blockHeightShift)} {data_.Length}"); byte[] result = Deswizzle(width, height, depth, blkWidth, blkHeight, blkDepth, target, bpp, TileMode, (int)Math.Max(0, BlockHeightLog2 - blockHeightShift), data_); //Create a copy and use that to remove uneeded data byte[] result_ = new byte[size]; Array.Copy(result, 0, result_, 0, size); result = null; return(result_); } } catch (Exception e) { System.Windows.Forms.MessageBox.Show($"Failed to swizzle texture {surface.Name}!"); Console.WriteLine(e); return(new byte[0]); } } ArrayOffset += (uint)(ImageData.Length / surface.ArrayCount); } return(new byte[0]); }
public static byte[] CreateBuffer(SBSurface surface, int target = 1) { List <byte> ImageData = new List <byte>(); if (surface.Arrays.Count == 0) { return(ImageData.ToArray()); } var MipCount = surface.Arrays[0].Mipmaps.Count; uint bpp = TextureFormatInfo.GetBPP(surface.InternalFormat); uint blkWidth = TextureFormatInfo.GetBlockWidth(surface.InternalFormat); uint blkHeight = TextureFormatInfo.GetBlockHeight(surface.InternalFormat); uint blkDepth = TextureFormatInfo.GetBlockDepth(surface.InternalFormat); uint blockHeight = GetBlockHeight(DivRoundUp((uint)surface.Height, blkHeight)); uint BlockHeightLog2 = (uint)Convert.ToString(blockHeight, 2).Length - 1; uint Pitch = 0; uint DataAlignment = 512; uint TileMode = 0; int linesPerBlockHeight = (1 << (int)BlockHeightLog2) * 8; //uint ArrayOffset = 0; for (int arrayLevel = 0; arrayLevel < surface.Arrays.Count; arrayLevel++) { uint SurfaceSize = 0; int blockHeightShift = 0; List <uint> MipOffsets = new List <uint>(); for (int mipLevel = 0; mipLevel < MipCount; mipLevel++) { uint width = (uint)Math.Max(1, surface.Width >> mipLevel); uint height = (uint)Math.Max(1, surface.Height >> mipLevel); uint depth = (uint)Math.Max(1, surface.Depth >> mipLevel); uint size = DivRoundUp(width, blkWidth) * DivRoundUp(height, blkHeight) * bpp; if (Pow2RoundUp(DivRoundUp(height, blkWidth)) < linesPerBlockHeight) { blockHeightShift += 1; } uint width__ = DivRoundUp(width, blkWidth); uint height__ = DivRoundUp(height, blkHeight); //Calculate the mip size instead byte[] AlignedData = new byte[(RoundUp(SurfaceSize, DataAlignment) - SurfaceSize)]; SurfaceSize += (uint)AlignedData.Length; MipOffsets.Add(SurfaceSize); //Get the first mip offset and current one and the total image size int msize = (int)((MipOffsets[0] + surface.Arrays[arrayLevel].Mipmaps[mipLevel].Length - MipOffsets[mipLevel]) / surface.ArrayCount); //try { Pitch = RoundUp(width__ * bpp, 64); SurfaceSize += Pitch * RoundUp(height__, Math.Max(1, blockHeight >> blockHeightShift) * 8); //Console.WriteLine($"{width} {height} {blkWidth} {blkHeight} {target} {bpp} {TileMode} {(int)Math.Max(0, BlockHeightLog2 - blockHeightShift)}"); var mipData = surface.Arrays[arrayLevel].Mipmaps[mipLevel]; /*byte[] padded = new byte[mipData.Length * 2]; * Array.Copy(mipData, 0, padded, 0, mipData.Length); * mipData = padded;*/ byte[] result = Swizzle(width, height, depth, blkWidth, blkHeight, blkDepth, target, bpp, TileMode, (int)Math.Max(0, BlockHeightLog2 - blockHeightShift), mipData); //Console.WriteLine(result.Length + " " + surface.Mipmaps[mipLevel].Length); ImageData.AddRange(result); } /*catch (Exception e) * { * System.Windows.Forms.MessageBox.Show($"Failed to swizzle texture {surface.Name}!"); * Console.WriteLine(e); * * return new byte[0]; * }*/ } // alignment if (arrayLevel != surface.Arrays.Count - 1) { ImageData.AddRange(new byte[0x1000 - (ImageData.Count % 0x1000)]); } } return(ImageData.ToArray()); }
public static void Open(string FilePath, SBScene Scene) { using (BinaryReader reader = new BinaryReader(new FileStream(FilePath, FileMode.Open))) { // TODO: Why are there empty streams? if (reader.BaseStream.Length == 0) { return; } SBSurface surface = new SBSurface(); reader.BaseStream.Position = reader.BaseStream.Length - 0xB0; int[] mipmapSizes = new int[16]; for (int i = 0; i < mipmapSizes.Length; i++) { mipmapSizes[i] = reader.ReadInt32(); } reader.ReadChars(4); // TNX magic string texName = ReadTexName(reader); surface.Name = texName; surface.Width = reader.ReadInt32(); surface.Height = reader.ReadInt32(); surface.Depth = reader.ReadInt32(); var Format = (NUTEX_FORMAT)reader.ReadByte(); // hack surface.IsSRGB = Format.ToString().ToLower().Contains("srgb"); SBConsole.WriteLine($"Loaded NUTEX: {surface.Name} Format: {Format.ToString()}"); if (pixelFormatByNuTexFormat.ContainsKey(Format)) { surface.PixelFormat = pixelFormatByNuTexFormat[Format]; } if (internalFormatByNuTexFormat.ContainsKey(Format)) { surface.InternalFormat = internalFormatByNuTexFormat[Format]; } reader.ReadByte(); ushort Padding = reader.ReadUInt16(); reader.ReadUInt32(); int MipCount = reader.ReadInt32(); int Alignment = reader.ReadInt32(); int ArrayCount = reader.ReadInt32(); int ImageSize = reader.ReadInt32(); char[] Magic = reader.ReadChars(4); int MajorVersion = reader.ReadInt16(); int MinorVersion = reader.ReadInt16(); uint blkWidth = (uint)blkDims[Format].X; uint blkHeight = (uint)blkDims[Format].Y; uint blockHeight = Tools.SwitchSwizzler.GetBlockHeight(Tools.SwitchSwizzler.DivRoundUp((uint)surface.Height, blkHeight)); uint BlockHeightLog2 = (uint)Convert.ToString(blockHeight, 2).Length - 1; uint tileMode = 0; uint bpp = GetBpps(Format); //TODO: Read mipmaps reader.BaseStream.Position = 0; int blockHeightShift = 0; for (int i = 0; i < 1; i++) { int size = mipmapSizes[i]; if (i == 0 && size % Alignment != 0) { size += Alignment - (size % Alignment); } byte[] deswiz = Tools.SwitchSwizzler.Deswizzle((uint)surface.Width, (uint)surface.Height, blkWidth, blkHeight, 0, bpp, tileMode, (int)Math.Max(0, BlockHeightLog2 - blockHeightShift), reader.ReadBytes(ImageSize)); byte[] trimmed = new byte[mipmapSizes[0]]; Array.Copy(deswiz, 0, trimmed, 0, trimmed.Length); surface.Mipmaps.Add(trimmed); } Scene.Surfaces.Add(surface); } }
public static SBSurface Open(string FilePath) { using (BinaryReader reader = new BinaryReader(new FileStream(FilePath, FileMode.Open))) { // TODO: Why are there empty streams? if (reader.BaseStream.Length == 0) { return(null); } SBSurface surface = new SBSurface(); reader.BaseStream.Position = reader.BaseStream.Length - 0xB0; int[] mipmapSizes = new int[16]; for (int i = 0; i < mipmapSizes.Length; i++) { mipmapSizes[i] = reader.ReadInt32(); } reader.ReadChars(4); // TNX magic string texName = ReadTexName(reader); surface.Name = texName; surface.Width = reader.ReadInt32(); surface.Height = reader.ReadInt32(); surface.Depth = reader.ReadInt32(); var Format = (NUTEX_FORMAT)reader.ReadByte(); reader.ReadByte(); ushort Padding = reader.ReadUInt16(); reader.ReadUInt32(); int MipCount = reader.ReadInt32(); int Alignment = reader.ReadInt32(); surface.ArrayCount = reader.ReadInt32(); int ImageSize = reader.ReadInt32(); char[] Magic = reader.ReadChars(4); int MajorVersion = reader.ReadInt16(); int MinorVersion = reader.ReadInt16(); if (pixelFormatByNuTexFormat.ContainsKey(Format)) { surface.PixelFormat = pixelFormatByNuTexFormat[Format]; } if (internalFormatByNuTexFormat.ContainsKey(Format)) { surface.InternalFormat = internalFormatByNuTexFormat[Format]; } surface.PixelType = GetPixelType(Format); reader.BaseStream.Position = 0; byte[] ImageData = reader.ReadBytes(ImageSize); for (int array = 0; array < surface.ArrayCount; array++) { MipArray arr = new MipArray(); for (int i = 0; i < MipCount; i++) { byte[] deswiz = SwitchSwizzler.GetImageData(surface, ImageData, array, i, MipCount); arr.Mipmaps.Add(deswiz); } surface.Arrays.Add(arr); } return(surface); } }
public static SBSurface Import(string FileName) { SBSurface surface = new SBSurface(); using (BinaryReaderExt reader = new BinaryReaderExt(new FileStream(FileName, FileMode.Open))) { DDS_Header header = new DDS_Header(); header.Read(reader); surface.Name = Path.GetFileNameWithoutExtension(FileName); surface.Width = header.dwWidth; surface.Height = header.dwHeight; if (header.dwFlags.HasFlag(DDSD.DEPTH)) { surface.Depth = header.dwDepth; } else { surface.Depth = 1; } if (header.ddspf.dwFourCC == 0x31545844) { surface.InternalFormat = InternalFormat.CompressedRgbaS3tcDxt1Ext; } else if (header.ddspf.dwFourCC == 0x30315844) { surface.InternalFormat = DXGItoInternal(header.DXT10Header.dxgiFormat); if (surface.InternalFormat == 0) { System.Windows.Forms.MessageBox.Show("DDS format not supported " + header.DXT10Header.dxgiFormat); return(null); } } else { if (((FourCC_DXGI)header.ddspf.dwFourCC) == FourCC_DXGI.D3DFMT_A32B32G32R32F) { surface.InternalFormat = InternalFormat.Rgba32f; surface.PixelFormat = PixelFormat.Rgba; surface.PixelType = PixelType.Float; } else { System.Windows.Forms.MessageBox.Show("DDS format not supported " + header.ddspf.dwFourCC.ToString("X")); return(null); } } // TODO: read other mips int w = surface.Width; int h = surface.Height; //SBConsole.WriteLine(header.dwCaps.ToString() + " " + header.dwCaps2.ToString() + " " + header.dwFlags.ToString()); for (int array = 0; array < (header.dwCaps2.HasFlag(DDSCAPS2.CUBEMAP_ALLFACES) ? 6 : 1); array++) { w = surface.Width; h = surface.Height; var mip = new MipArray(); for (int i = 0; i < (header.dwFlags.HasFlag(DDSD.MIPMAPCOUNT) ? header.dwMipMapCount : 1); i++) { var mipSize = Math.Max(1, ((w + 3) / 4)) * Math.Max(1, ((h + 3) / 4)) * (int)TextureFormatInfo.GetBPP(surface.InternalFormat); if (mipSize % TextureFormatInfo.GetBPP(surface.InternalFormat) != 0) { mipSize += (int)(TextureFormatInfo.GetBPP(surface.InternalFormat) - (mipSize % TextureFormatInfo.GetBPP(surface.InternalFormat))); } var data = reader.ReadBytes(mipSize); mip.Mipmaps.Add(data); w /= 2; h /= 2; } surface.Arrays.Add(mip); } if (reader.Position != reader.BaseStream.Length) { SBConsole.WriteLine("Warning: error reading dds " + reader.Position.ToString("X")); } } return(surface); }
public static void Export(string fileName, SBSurface surface) { if (!internalFormatToDXGI.ContainsKey(surface.InternalFormat) && !internalFormatToD3D.ContainsKey(surface.InternalFormat)) { System.Windows.Forms.MessageBox.Show("Unsupported DDS export format " + surface.InternalFormat.ToString()); return; } var Header = new DDS_Header() { dwFlags = (DDSD.CAPS | DDSD.HEIGHT | DDSD.WIDTH | DDSD.PIXELFORMAT | DDSD.LINEARSIZE), dwHeight = surface.Height, dwWidth = surface.Width, dwPitchOrLinearSize = GetPitchOrLinearSize(surface.InternalFormat, surface.Width), dwDepth = surface.Depth, dwMipMapCount = surface.Arrays[0].Mipmaps.Count, dwReserved1 = new uint[11], ddspf = new DDS_PIXELFORMAT() { }, dwCaps = 0, dwCaps2 = 0 }; if (surface.Arrays.Count > 0 && surface.Arrays[0].Mipmaps.Count > 1) { Header.dwFlags |= DDSD.MIPMAPCOUNT; } if (surface.IsCubeMap) { Header.dwCaps |= DDSCAPS.TEXTURE | DDSCAPS.COMPLEX; Header.dwCaps2 |= DDSCAPS2.CUBEMAP_ALLFACES; } //TODO: format Header.ddspf.dwFlags = DDPF.FOURCC; Header.ddspf.dwFourCC = 0x30315844; if (internalFormatToD3D.ContainsKey(surface.InternalFormat)) { Header.ddspf.dwFourCC = (int)internalFormatToD3D[surface.InternalFormat]; } if (internalFormatToDXGI.ContainsKey(surface.InternalFormat)) { Header.DXT10Header.dxgiFormat = internalFormatToDXGI[surface.InternalFormat]; Header.DXT10Header.resourceDimension = D3D10_RESOURCE_DIMENSION.D3D10_RESOURCE_DIMENSION_TEXTURE2D; Header.DXT10Header.arraySize = (uint)surface.Arrays.Count; } using (BinaryWriterExt writer = new BinaryWriterExt(new FileStream(fileName, FileMode.Create))) { Header.Write(writer); foreach (var arr in surface.Arrays) { foreach (var mip in arr.Mipmaps) { writer.Write(mip); } } } }
public SBAnimation ImportSBAnimation(string FileName, SBSkeleton skeleton) { SBAnimation anim = new SBAnimation(); HSDRawFile f = new HSDRawFile(FileName); foreach (var root in f.Roots) { if (root == null || root.Data == null) { continue; } anim.Name = root.Name; if (root.Data is HSD_AnimJoint joint) { var joints = joint.BreathFirstList; int nodeIndex = -1; foreach (var j in joints) { nodeIndex++; if (j.AOBJ == null || j.AOBJ.FObjDesc == null) { continue; } SBConsole.WriteLine(nodeIndex + " " + j.Flags.ToString("X8") + " " + j.AOBJ.Flags.ToString()); SBTransformAnimation a = new SBTransformAnimation(); if (nodeIndex < skeleton.Bones.Length) { a.Name = skeleton.Bones[nodeIndex].Name; } else { a.Name = "JOBJ_" + nodeIndex; } anim.TransformNodes.Add(a); anim.FrameCount = Math.Max(anim.FrameCount, j.AOBJ.EndFrame); foreach (var fobj in j.AOBJ.FObjDesc.List) { a.Tracks.Add(DecodeFOBJ(fobj.ToFOBJ())); } } } if (root.Data is HSD_FigaTree tree) { anim.FrameCount = tree.FrameCount; int nodeIndex = 0; foreach (var node in tree.Nodes) { SBTransformAnimation a = new SBTransformAnimation(); a.Name = skeleton.Bones[nodeIndex++].Name; anim.TransformNodes.Add(a); foreach (var att in node.Tracks) { if (att.FOBJ == null) { continue; } a.Tracks.Add(DecodeFOBJ(att.FOBJ)); } } } if (root.Data is HSD_MatAnimJoint matjoint) { var joints = matjoint.BreathFirstList; anim.FrameCount = 0; int nodeIndex = -1; foreach (var j in joints) { if (j.MaterialAnimation == null) { continue; } var matAnims = j.MaterialAnimation.List; foreach (var manim in matAnims) { nodeIndex++; var aobj = manim.AnimationObject; if (aobj != null) { anim.FrameCount = Math.Max(anim.FrameCount, aobj.EndFrame); } var texanim = manim.TextureAnimation; if (texanim == null) { continue; } var texAOBJ = texanim.AnimationObject; if (texAOBJ == null || texAOBJ.FObjDesc == null) { continue; } anim.FrameCount = Math.Max(anim.FrameCount, texAOBJ.EndFrame); //TODO: tex anim is a list if (texanim != null) { SBTextureAnimation textureAnim = new SBTextureAnimation(); anim.TextureNodes.Add(textureAnim); textureAnim.MeshName = "DOBJ_" + nodeIndex; textureAnim.TextureAttibute = texanim.GXTexMapID.ToString(); textureAnim.Keys = DecodeFOBJ(texAOBJ.FObjDesc.ToFOBJ()).Keys; for (int i = 0; i < texanim.ImageCount; i++) { HSD_TOBJ tobj = new HSD_TOBJ(); tobj.ImageData = texanim.ImageBuffers.Array[i].Data; if (texanim.TlutCount > i) { tobj.TlutData = texanim.TlutBuffers.Array[i].Data; } var surface = new SBSurface(); surface.Arrays.Add(new MipArray() { Mipmaps = new List <byte[]>() { tobj.GetDecodedImageData() } }); surface.Width = tobj.ImageData.Width; surface.Height = tobj.ImageData.Height; surface.PixelFormat = PixelFormat.Bgra; surface.PixelType = PixelType.UnsignedByte; surface.InternalFormat = InternalFormat.Rgba; textureAnim.Surfaces.Add(surface); } } } } SBConsole.WriteLine(nodeIndex); } } return(anim); }