/// <summary> /// Model Matrix Data /// </summary> /// <param name="data"></param> /// <param name="ignoreVersion"></param> /// <param name="sectionHeader"></param> public G1MMatrix(Span <byte> data, bool ignoreVersion, ResourceSectionHeader sectionHeader) { if (sectionHeader.Magic != DataType.G1MM) { throw new InvalidOperationException("Not an G1MM stream"); } Section = sectionHeader; if (!ignoreVersion && Section.Version.ToVersion() != SupportedVersion) { throw new NotSupportedException($"G1MM version {Section.Version.ToVersion()} is not supported!"); } if (Section.Size == 0xC) { return; } var count = MemoryMarshal.Read <int>(data); if (count == 0) { return; } Matrices = MemoryMarshal.Cast <byte, Matrix4x4>(data.Slice(0x4)).ToArray(); }
/// <summary> /// Initialize with no data. /// </summary> public G1Model() { Section = new ResourceSectionHeader { Magic = DataType.Model, Size = -1, Version = SupportedVersion.ToVersionA() }; SectionRoot = new PackedResource(); }
/// <summary> /// Extra data found in G1M models. /// </summary> /// <param name="data"></param> /// <param name="ignoreVersion"></param> /// <param name="sectionHeader"></param> public G1Extra(Span <byte> data, bool ignoreVersion, ResourceSectionHeader sectionHeader) { if (sectionHeader.Magic != DataType.EXTR) { throw new InvalidOperationException("Not an EXTR stream"); } Section = sectionHeader; if (!ignoreVersion && Section.Version.ToVersion() != SupportedVersion) { throw new NotSupportedException($"EXTR version {Section.Version.ToVersion()} is not supported!"); } }
/// <summary> /// Model Skeleton Data. /// </summary> /// <param name="data"></param> /// <param name="ignoreVersion"></param> /// <param name="sectionHeader"></param> public G1MSkeleton(Span <byte> data, bool ignoreVersion, ResourceSectionHeader sectionHeader) { if (sectionHeader.Magic != DataType.G1MS) { throw new InvalidOperationException("Not an G1MS stream"); } Section = sectionHeader; if (!ignoreVersion && Section.Version.ToVersion() != SupportedVersion) { throw new NotSupportedException($"G1MS version {Section.Version.ToVersion()} is not supported!"); } Header = MemoryMarshal.Read <ModelSkeletonHeader>(data); BoneIndices = MemoryMarshal.Cast <byte, short>(data.Slice(SizeHelper.SizeOf <ModelSkeletonHeader>(), Header.BoneTableCount)).ToArray(); Bones = MemoryMarshal.Cast <byte, ModelSkeletonBone>(data.Slice(Header.DataOffset - SizeHelper.SizeOf <ResourceSectionHeader>(), Header.BoneCount * SizeHelper.SizeOf <ModelSkeletonBone>())).ToArray(); }
/// <summary> /// Model Skeleton Data. /// </summary> /// <param name="data"></param> /// <param name="ignoreVersion"></param> /// <param name="sectionHeader"></param> public G1MSkeleton(Span <byte> data, bool ignoreVersion, ResourceSectionHeader sectionHeader) { if (sectionHeader.Magic != DataType.G1MS) { throw new InvalidOperationException("Not an G1MS stream"); } Section = sectionHeader; if (!ignoreVersion && Section.Version.ToVersion() != SupportedVersion) { throw new NotSupportedException($"G1MS version {Section.Version.ToVersion()} is not supported!"); } Header = MemoryMarshal.Read <ModelSkeletonHeader>(data); BoneIndices = MemoryMarshal.Cast <byte, short>(data.Slice(SizeHelper.SizeOf <ModelSkeletonHeader>(), Header.BoneTableCount)).ToArray(); Bones = MemoryMarshal.Cast <byte, ModelSkeletonBone>(data.Slice(Header.DataOffset - SizeHelper.SizeOf <ResourceSectionHeader>(), Header.BoneCount * SizeHelper.SizeOf <ModelSkeletonBone>())).ToArray(); WorldBones = new ModelSkeletonBone[Bones.Length]; for (var index = 0; index < Bones.Length; index++) { var bone = Bones[index]; if (!bone.HasParent()) { WorldBones[index] = bone; continue; } if (bone.Parent > index) { throw new InvalidOperationException("Bone calculated before parent bone"); } var parentBone = WorldBones[bone.Parent]; WorldBones[index] = new ModelSkeletonBone { Length = bone.Length, Parent = bone.Parent, Scale = (parentBone.Scale.ToOpenTK() * bone.Scale.ToOpenTK()).ToDragon(), Rotation = (parentBone.Rotation.ToOpenTK() * bone.Rotation.ToOpenTK()).ToDragon() }; var local = parentBone.Rotation.ToOpenTK() * new Quaternion(bone.Position.ToOpenTK(), 0f) * new Quaternion(-parentBone.Rotation.X, -parentBone.Rotation.Y, -parentBone.Rotation.Z, parentBone.Rotation.W); WorldBones[index].Position = new Vector3(local.X + parentBone.Position.X, local.Y + parentBone.Position.Y, local.Z + parentBone.Position.Z); } }
private static void Main(string[] args) { Logger.PrintVersion("Nyotengu"); var flags = CommandLineFlags.ParseFlags <KTIDFlags>(CommandLineFlags.PrintHelp, args); if (flags == null) { return; } var ndb = new NDB(); if (!string.IsNullOrEmpty(flags.NDBPath) && File.Exists(flags.NDBPath)) { ndb = new NDB(File.ReadAllBytes(flags.NDBPath)); } var objdb = new OBJDB(File.ReadAllBytes(flags.OBJDBPath)); var filelist = Cethleann.ManagedFS.Nyotengu.LoadKTIDFileList(flags.FileList, flags.GameId); var textureSection = new ResourceSectionHeader { Magic = DataType.TextureGroup, Version = 0x30303630 }; var textureHeader = new TextureGroupHeader { System = objdb.Header.System }; foreach (var ktid in flags.Paths) { if (!File.Exists(ktid)) { continue; } var ktidgroup = new KTIDTextureSet(File.ReadAllBytes(ktid)); var ktidsystem = ktidgroup.Textures.Select(x => objdb.Entries.TryGetValue(x, out var tuple) ? tuple : default).ToArray();
/// <summary> /// Model Geometry /// </summary> /// <param name="data"></param> /// <param name="ignoreVersion"></param> /// <param name="sectionHeader"></param> public G1MGeometry(Span <byte> data, bool ignoreVersion, ResourceSectionHeader sectionHeader) { if (sectionHeader.Magic != DataType.G1MG) { throw new InvalidOperationException("Not an G1MG stream"); } Section = sectionHeader; if (!ignoreVersion && Section.Version.ToVersion() != SupportedVersion) { throw new NotSupportedException($"G1MG version {Section.Version.ToVersion()} is not supported!"); } Header = MemoryMarshal.Read <ModelGeometryHeader>(data); var offset = SizeHelper.SizeOf <ModelGeometryHeader>(); while (offset < data.Length) { var subSectionHeader = MemoryMarshal.Read <ModelSection>(data.Slice(offset)); var block = data.Slice(offset + SizeHelper.SizeOf <ModelSection>(), subSectionHeader.Size - SizeHelper.SizeOf <ModelSection>()); offset += subSectionHeader.Size; var section = subSectionHeader.Magic switch { ModelGeometrySectionType.Socket => (IG1MGSection) new G1MGSocket(block, subSectionHeader), ModelGeometrySectionType.Material => new G1MGMaterial(block, subSectionHeader), ModelGeometrySectionType.ShaderParam => new G1MGShaderParam(block, subSectionHeader), ModelGeometrySectionType.VertexBuffer => new G1MGVertexBuffer(block, subSectionHeader), ModelGeometrySectionType.VertexAttribute => new G1MGVertexAttribute(block, subSectionHeader), ModelGeometrySectionType.Bone => new G1MGBone(block, subSectionHeader), ModelGeometrySectionType.IndexBuffer => new G1MGIndexBuffer(block, subSectionHeader), ModelGeometrySectionType.SubMesh => new G1MGSubMesh(block, subSectionHeader), ModelGeometrySectionType.Mesh => new G1MGMesh(block, subSectionHeader), _ => throw new NotSupportedException($"Can't handle G1MG section {subSectionHeader.Magic:F}") }; SubSections.Add(section); } }
/// <summary> /// Create a file with the given parameters /// </summary> /// <param name="header"></param> /// <param name="target"></param> /// <param name="version"></param> /// <typeparam name="T"></typeparam> /// <returns></returns> public Span <byte> WriteWithHeader <T>(T header, DataType target, int version) where T : struct { var length = SizeHelper.SizeOf <T>() + Sections.Sum(x => x.Length) + SizeHelper.SizeOf <ResourceSectionHeader>(); var buffer = new Span <byte>(new byte[length]); var resourceHeader = new ResourceSectionHeader { Magic = target, Size = length, Version = version.ToVersionA() }; MemoryMarshal.Write(buffer, ref resourceHeader); var pointer = SizeHelper.SizeOf <ResourceSectionHeader>(); MemoryMarshal.Write(buffer.Slice(pointer), ref header); pointer += SizeHelper.SizeOf <T>(); foreach (var stream in Sections) { stream.Span.CopyTo(buffer.Slice(pointer)); pointer += stream.Length; } return(buffer); }
/// <summary> /// Initialize with memory block /// </summary> /// <param name="dataBlock"></param> /// <param name="sectionHeader"></param> public G1MStub(Span <byte> dataBlock, ResourceSectionHeader sectionHeader) { Data = new Memory <byte>(dataBlock.ToArray()); Section = sectionHeader; }