public override void ProcessShapeMotionVertexData(NJS_MOTION motion, float frame, int animindex) { if (!motion.Models.ContainsKey(animindex)) { ProcessVertexData(); return; } #if modellog Extensions.Log("Processing Chunk Attach " + Name + Environment.NewLine); #endif if (Vertex != null) { foreach (VertexChunk chunk in Vertex) { #if modellog Extensions.Log("Vertex Declaration: " + chunk.IndexOffset + "-" + (chunk.IndexOffset + chunk.VertexCount - 1) + Environment.NewLine); #endif if (VertexBuffer.Length < chunk.IndexOffset + chunk.VertexCount) { Array.Resize(ref VertexBuffer, chunk.IndexOffset + chunk.VertexCount); } Vertex[] vertdata = chunk.Vertices.ToArray(); Vertex[] normdata = chunk.Normals.ToArray(); AnimModelData data = motion.Models[animindex]; if (data.Vertex.Count > 0) { vertdata = data.GetVertex(frame); } if (data.Normal.Count > 0) { normdata = data.GetNormal(frame); } for (int i = 0; i < chunk.VertexCount; i++) { VertexBuffer[i + chunk.IndexOffset] = new VertexData(vertdata[i]); if (normdata.Length > 0) { VertexBuffer[i + chunk.IndexOffset].Normal = normdata[i]; } if (chunk.Diffuse.Count > 0) { VertexBuffer[i + chunk.IndexOffset].Color = chunk.Diffuse[i]; } } } } List <MeshInfo> result = new List <MeshInfo>(); if (Poly != null) { result = ProcessPolyList(PolyName, Poly, 0); } MeshInfo = result.ToArray(); }
static NJS_MOTION ProcessMotion(byte[] file, int verbose = 0, int startaddress = 0) { int frames; InterpolationMode intmode; AnimFlags animtype; int curaddr = 0; do { AnimSections section_type = (AnimSections)BitConverter.ToInt16(file, curaddr); int section_size = BitConverter.ToInt32(file, curaddr + 4); int section_addr = curaddr + 8; if (verbose > 1) { Console.Write("Subsection type {0}, size {1}, data begins at {2}\n", section_type.ToString(), section_size, section_addr.ToString("X")); } switch (section_type) { case AnimSections.MKEY_F: if (verbose > 1) { Console.Write(", calculated item count: {0}\n", (float)section_size / 16.0f); } break; case AnimSections.MKEY_A: if (verbose > 1) { Console.Write(", calculated item count: {0}\n", (float)section_size / 16.0f); } break; case AnimSections.UNKNOWN: if (verbose > 1) { Console.Write(", calculated item count: {0}\n", (float)section_size / 16.0f); } break; case AnimSections.MDATA2_HEADER: if (verbose > 1) { Console.Write(", number of MDATA entries: {0}\n", BitConverter.ToInt32(file, section_addr) >> BitConverter.ToInt32(file, section_addr + 4)); } break; case AnimSections.MOTION: if (verbose > 0) { Console.WriteLine("Motion at {0}", (startaddress + section_addr).ToString("X")); } frames = BitConverter.ToInt32(file, section_addr + 4); intmode = (InterpolationMode)BitConverter.ToInt16(file, section_addr + 10); animtype = (AnimFlags)BitConverter.ToInt16(file, section_addr + 8); int mdataaddr = BitConverter.ToInt32(file, section_addr); if (verbose > 1) { Console.Write("\nMDATA header at: {0}, frames: {1}, flags: {2}, interpolation: {3}", mdataaddr.ToString("X"), frames, animtype.ToString(), intmode.ToString()); } // Create motion stub NJS_MOTION mot = new NJS_MOTION(); mot.Name = "animation_" + section_addr.ToString("X8"); mot.MdataName = mot.Name + "_mdat"; mot.Frames = frames; mot.InterpolationMode = intmode; // Read the MDATA header and get the number of MDATA entries mot.ModelParts = BitConverter.ToInt32(file, mdataaddr) >> BitConverter.ToInt32(file, mdataaddr + 4); if (verbose > 1) { Console.WriteLine(", model parts: {0}", mot.ModelParts); } int tmpaddr = mdataaddr + 8; //Start of actual MDATA array for (int u = 0; u < mot.ModelParts; u++) { //Console.Write("\nMotion data {0}", u.ToString()); AnimModelData data = new AnimModelData(); if (animtype.HasFlag(AnimFlags.Position)) { uint posoff = ByteConverter.ToUInt32(file, tmpaddr + u * 16); data.PositionName = mot.Name + "_mkey_" + u.ToString() + "_pos_" + posoff.ToString("X8"); int pos_count = ByteConverter.ToInt32(file, tmpaddr + u * 16 + 8); //Console.Write(", position at: {0} ({1} entries)", posoff.ToString("X"), pos_count); for (int p = 0; p < pos_count; p++) { int index = ByteConverter.ToInt32(file, (int)posoff + p * 16); float pos_x = ByteConverter.ToSingle(file, (int)posoff + p * 16 + 4); float pos_y = ByteConverter.ToSingle(file, (int)posoff + p * 16 + 8); float pos_z = ByteConverter.ToSingle(file, (int)posoff + p * 16 + 12); //Console.WriteLine("\nAdded position index {3}: X: {0} Y: {1} Z: {2}", pos_x, pos_y, pos_z, index); data.Position.Add(index, new Vertex(pos_x, pos_y, pos_z)); } } if (animtype.HasFlag(AnimFlags.Rotation)) { uint rotoff = ByteConverter.ToUInt32(file, tmpaddr + u * 16 + 4); data.RotationName = mot.Name + "_mkey_" + u.ToString() + "_rot_" + rotoff.ToString("X8"); int rot_count = ByteConverter.ToInt32(file, tmpaddr + u * 16 + 12); //Console.Write(", rotation at: {0} ({1} entries)", rotoff.ToString("X"), rot_count); for (int p = 0; p < rot_count; p++) { int index = ByteConverter.ToInt32(file, (int)rotoff + p * 16); int rot_x = ByteConverter.ToInt32(file, (int)rotoff + p * 16 + 4); int rot_y = ByteConverter.ToInt32(file, (int)rotoff + p * 16 + 8); int rot_z = ByteConverter.ToInt32(file, (int)rotoff + p * 16 + 12); //Console.WriteLine("\nAdded rotation index {3}: X: {0} Y: {1} Z: {2}", rot_x, rot_y, rot_z, index); data.Rotation.Add(index, new Rotation(rot_x, rot_y, rot_z)); } } mot.Models.Add(u, data); } return(mot); } curaddr += 8 + section_size; }while (curaddr < file.Length); return(null); }
static byte[] GetSections(NJS_MOTION motion, int verbose = 0) { List <byte> result = new List <byte>(); int[] mkey_f_addr = new int[motion.ModelParts]; int[] mkey_a_addr = new int[motion.ModelParts]; int mkey_f_section = 0; int mkey_f_count = 0; // Get MKEY_F foreach (var anim in motion.Models) { AnimModelData model = anim.Value; if (model.Position != null && model.Position.Count > 0) { mkey_f_addr[anim.Key] = result.Count; if (verbose > 1) { Console.WriteLine("MKEY_F: {0} at {1}", anim.Key, result.Count.ToString("X")); } foreach (var item in model.Position) { result.AddRange(BitConverter.GetBytes(item.Key)); result.AddRange(BitConverter.GetBytes(item.Value.X)); result.AddRange(BitConverter.GetBytes(item.Value.Y)); result.AddRange(BitConverter.GetBytes(item.Value.Z)); mkey_f_count++; } } } if (mkey_f_count > 0) { result.InsertRange(mkey_f_section, BitConverter.GetBytes((uint)0)); result.InsertRange(mkey_f_section + 4, BitConverter.GetBytes((uint)mkey_f_count * 16)); } // Get MKEY_A int mkey_a_section = result.Count; int mkey_a_count = 0; foreach (var anim in motion.Models) { AnimModelData model = anim.Value; if (model.Rotation != null && model.Rotation.Count > 0) { mkey_a_addr[anim.Key] = result.Count; if (verbose > 1) { Console.WriteLine("MKEY_A: {0} at {1}", anim.Key, result.Count.ToString("X")); } foreach (var item in model.Rotation) { result.AddRange(BitConverter.GetBytes(item.Key)); result.AddRange(BitConverter.GetBytes(item.Value.X)); result.AddRange(BitConverter.GetBytes(item.Value.Y)); result.AddRange(BitConverter.GetBytes(item.Value.Z)); mkey_a_count++; } } } if (mkey_a_count > 0) { result.InsertRange(mkey_a_section, BitConverter.GetBytes((uint)1)); result.InsertRange(mkey_a_section + 4, BitConverter.GetBytes((uint)mkey_a_count * 16)); } // Get MDATA2 header int mdata_section = result.Count; for (int m = 0; m < motion.ModelParts; m++) { if (motion.Models.ContainsKey(m)) { AnimModelData model = motion.Models[m]; if (model.Position != null && model.Position.Count > 0) { result.AddRange(BitConverter.GetBytes((uint)(mkey_f_addr[m] + 8))); } else { result.AddRange(BitConverter.GetBytes((uint)0)); } if (model.Rotation != null && model.Rotation.Count > 0) { result.AddRange(BitConverter.GetBytes((uint)(mkey_a_addr[m] + 8))); } else { result.AddRange(BitConverter.GetBytes((uint)0)); } if (model.Position != null) { result.AddRange(BitConverter.GetBytes((uint)(model.Position.Count))); } else { result.AddRange(BitConverter.GetBytes((uint)0)); } if (model.Rotation != null) { result.AddRange(BitConverter.GetBytes((uint)(model.Rotation.Count))); } else { result.AddRange(BitConverter.GetBytes((uint)0)); } } else { result.AddRange(BitConverter.GetBytes((uint)0)); result.AddRange(BitConverter.GetBytes((uint)0)); result.AddRange(BitConverter.GetBytes((uint)0)); result.AddRange(BitConverter.GetBytes((uint)0)); } } result.InsertRange(mdata_section, BitConverter.GetBytes((uint)3)); result.InsertRange(mdata_section + 4, BitConverter.GetBytes((uint)(16 * motion.ModelParts + 8))); result.InsertRange(mdata_section + 8, BitConverter.GetBytes((uint)motion.ModelParts << 2)); result.InsertRange(mdata_section + 12, BitConverter.GetBytes((uint)2)); // Write motion header result.AddRange(BitConverter.GetBytes((uint)4)); result.AddRange(BitConverter.GetBytes((uint)12)); result.AddRange(BitConverter.GetBytes(mdata_section + 8)); result.AddRange(BitConverter.GetBytes(motion.Frames)); // It's MDATA2 so flags are hardcoded AnimFlags flags = 0; flags |= AnimFlags.Position; flags |= AnimFlags.Rotation; result.AddRange(BitConverter.GetBytes((short)flags)); result.AddRange(BitConverter.GetBytes((short)motion.InterpolationMode)); return(result.ToArray()); }
private void UpdateWeightedModels() { if (scenenum > 0) { for (int i = 0; i < @event.Scenes[scenenum].Entities.Count; i++) { if (@event.Scenes[scenenum].Entities[i].Model != null) { if (@event.Scenes[scenenum].Entities[i].Model.HasWeight) { if (animframe == -1 || @event.Scenes[scenenum].Entities[i].Motion == null) { @event.Scenes[scenenum].Entities[i].Model.UpdateWeightedModel(new MatrixStack(), meshes[scenenum][i]); } else { MatrixStack m = new MatrixStack(); m.NJTranslate([email protected][scenenum].Entities[i].Motion.Models[0].GetPosition(animframe)); @event.Scenes[scenenum].Entities[i].Model.UpdateWeightedModelAnimated(m, @event.Scenes[scenenum].Entities[i].Motion, animframe, meshes[scenenum][i]); } } else if (@event.Scenes[scenenum].Entities[i].ShapeMotion != null) { if (animframe == -1) { @event.Scenes[scenenum].Entities[i].Model.ProcessVertexData(); } else { @event.Scenes[scenenum].Entities[i].Model.ProcessShapeMotionVertexData(@event.Scenes[scenenum].Entities[i].ShapeMotion, animframe); } NJS_OBJECT[] models = @event.Scenes[scenenum].Entities[i].Model.GetObjects(); for (int j = 0; j < models.Length; j++) { if (models[j].Attach != null) { try { meshes[scenenum][i][j] = models[j].Attach.CreateD3DMesh(); } catch { } } } } } } if (@event.Scenes[scenenum].Big?.Model != null) { if (@event.Scenes[scenenum].Big.Model.HasWeight) { if (animframe == -1) { @event.Scenes[scenenum].Big.Model.UpdateWeightedModel(new MatrixStack(), bigmeshes[scenenum]); } else { int an = 0; int fr = animframe; while (an < @event.Scenes[scenenum].Big.Motions.Count && @event.Scenes[scenenum].Big.Motions[an].a.Frames < fr) { fr -= @event.Scenes[scenenum].Big.Motions[an].a.Frames; an++; } if (an < @event.Scenes[scenenum].Big.Motions.Count) { @event.Scenes[scenenum].Big.Model.UpdateWeightedModelAnimated(new MatrixStack(), @event.Scenes[scenenum].Big.Motions[an].a, fr, bigmeshes[scenenum]); } } } } if (eventcamera && animframe != -1 && @event.Scenes[scenenum].CameraMotions != null) { cam.mode = 2; int an = 0; int fr = animframe; while (@event.Scenes[scenenum].CameraMotions[an].Frames < fr) { fr -= @event.Scenes[scenenum].CameraMotions[an].Frames; an++; } AnimModelData data = @event.Scenes[scenenum].CameraMotions[an].Models[0]; cam.Position = data.GetPosition(fr).ToVector3(); Vector3 dir; if (data.Vector.Count > 0) { dir = data.GetVector(fr).ToVector3(); } else { dir = Vector3.Normalize(cam.Position - data.GetTarget(fr).ToVector3()); } cam.Direction = dir; cam.Roll = data.GetRoll(fr); cam.FOV = SonicRetro.SAModel.Direct3D.Extensions.BAMSToRad(@event.Scenes[scenenum].CameraMotions[an].Models[0].GetAngle(fr)); } else { cam.mode = 0; cam.FOV = (float)(Math.PI / 4); if (animframe != -1 && @event.Scenes[scenenum].CameraMotions != null) { int an = 0; int fr = animframe; while (@event.Scenes[scenenum].CameraMotions[an].Frames < fr) { fr -= @event.Scenes[scenenum].CameraMotions[an].Frames; an++; } AnimModelData data = @event.Scenes[scenenum].CameraMotions[an].Models[0]; Vector3 pos = data.GetPosition(fr).ToVector3(); Vector3 dir; if (data.Vector.Count > 0) { dir = data.GetVector(fr).ToVector3(); } else { dir = Vector3.Normalize(pos - data.GetTarget(fr).ToVector3()); } int roll = data.GetRoll(fr); float bams_sin = SonicRetro.SAModel.Direct3D.Extensions.NJSin(roll); float bams_cos = SonicRetro.SAModel.Direct3D.Extensions.NJCos(-roll); float thing = dir.X * dir.X + dir.Z * dir.Z; double sqrt = Math.Sqrt(thing); float v3 = dir.Y * dir.Y + thing; double v4 = 1.0 / Math.Sqrt(v3); double sqrt__ = sqrt * v4; double sqrt___ = v4 * dir.Y; double v7, v8; if (thing <= 0.000001) { v7 = 1.0; v8 = 0.0; } else { double v5 = 1.0 / Math.Sqrt(thing); double v6 = v5; v7 = v5 * dir.Z; v8 = -(v6 * dir.X); } double v9 = sqrt___ * v8; cammatrix.M14 = 0; cammatrix.M23 = (float)sqrt___; cammatrix.M24 = 0; cammatrix.M34 = 0; cammatrix.M11 = (float)(v7 * bams_cos - v9 * bams_sin); cammatrix.M12 = (float)(v9 * bams_cos + v7 * bams_sin); cammatrix.M13 = -(float)(sqrt__ * v8); cammatrix.M21 = -(float)(sqrt__ * bams_sin); cammatrix.M22 = (float)(sqrt__ * bams_cos); double v10 = v7 * sqrt___; cammatrix.M31 = (float)(bams_sin * v10 + v8 * bams_cos); cammatrix.M32 = (float)(v8 * bams_sin - v10 * bams_cos); cammatrix.M33 = (float)(v7 * sqrt__); cammatrix.M41 = -(cammatrix.M31 * pos.Z) - cammatrix.M11 * pos.X - cammatrix.M21 * pos.Y; cammatrix.M42 = -(cammatrix.M32 * pos.Z) - cammatrix.M12 * pos.X - cammatrix.M22 * pos.Y; float v12 = -(cammatrix.M33 * pos.Z) - cammatrix.M13 * pos.X; double v13 = sqrt___ * pos.Y; cammatrix.M44 = 1; cammatrix.M43 = (float)(v12 - v13); cammatrix.Invert(); } } } }
static void Main(string[] args) { string path; if (args.Length > 0) { path = args[0]; } else { Console.Write("Path: "); path = Console.ReadLine().Trim('"'); } path = Path.GetFullPath(path); if (path.EndsWith(".ini")) { path = Path.GetDirectoryName(path); } string mtnfn = new DirectoryInfo(path).Name; MTNInfo info = IniSerializer.Deserialize <MTNInfo>(Path.Combine(path, mtnfn + ".ini")); string fext = null; switch (info.ModelFormat) { case ModelFormat.Basic: case ModelFormat.BasicDX: fext = "*.sa1mdl"; break; case ModelFormat.Chunk: fext = "*.sa2mdl"; break; case ModelFormat.GC: fext = "*.sa2bmdl"; break; } SortedDictionary <int, NJS_OBJECT> mdls = new SortedDictionary <int, NJS_OBJECT>(); foreach (string fn in Directory.EnumerateFiles(path, fext)) { if (int.TryParse(Path.GetFileNameWithoutExtension(fn), out int i) && i >= 0 && i < info.Frames) { mdls[i] = new ModelFile(fn).Model; } } NJS_OBJECT first = mdls.First().Value; int nodecnt = first.CountMorph(); int[] vcnt = new int[nodecnt]; ushort[][] polys = new ushort[nodecnt][]; NJS_OBJECT[] nodes = first.GetObjects().Where(a => a.Morph).ToArray(); for (int i = 0; i < nodecnt; i++) { switch (nodes[i].Attach) { case BasicAttach batt: vcnt[i] = batt.Vertex.Length; List <ushort> plist = new List <ushort>(); foreach (NJS_MESHSET mesh in batt.Mesh) { foreach (Poly p in mesh.Poly) { plist.AddRange(p.Indexes); } } polys[i] = plist.ToArray(); break; case ChunkAttach catt: if (catt.Vertex != null) { vcnt[i] = catt.Vertex.Sum(a => a.VertexCount); } plist = new List <ushort>(); foreach (PolyChunkStrip pcs in catt.Poly.OfType <PolyChunkStrip>()) { foreach (PolyChunkStrip.Strip s in pcs.Strips) { plist.AddRange(s.Indexes); } } polys[i] = plist.ToArray(); break; } } NJS_MOTION mtn = new NJS_MOTION() { Name = info.Name, Frames = info.Frames, InterpolationMode = info.InterpolationMode, ModelParts = nodecnt }; foreach ((int frame, NJS_OBJECT mdl) in mdls) { NJS_OBJECT[] nodes2 = mdl.GetObjects().Where(a => a.Morph).ToArray(); if (nodes2.Length != nodecnt) { Console.WriteLine("Warning: Model {0} skeleton does not match first file!", frame); if (nodes2.Length > mtn.ModelParts) { mtn.ModelParts = nodes2.Length; } } for (int i = 0; i < nodes2.Length; i++) { switch (nodes2[i].Attach) { case BasicAttach batt: if (batt.Vertex.Length != vcnt[i]) { Console.WriteLine("Warning: Model {0} node {1} vertex count does not match first file!", frame, i); } AnimModelData amd; if (!mtn.Models.TryGetValue(i, out amd)) { amd = new AnimModelData() { VertexName = mtn.MdataName + "_vtx_" + i, NormalName = mtn.MdataName + "_nor_" + i }; mtn.Models[i] = amd; } amd.Vertex[frame] = batt.Vertex; if (batt.Normal != null) { amd.Normal[frame] = batt.Normal; } List <ushort> plist = new List <ushort>(); foreach (NJS_MESHSET mesh in batt.Mesh) { foreach (Poly p in mesh.Poly) { plist.AddRange(p.Indexes); } } if (!polys[i].SequenceEqual(plist)) { Console.WriteLine("Warning: Model {0} node {1} poly data does not match first file!", frame, i); } break; case ChunkAttach catt: if (catt.Vertex != null) { if (catt.Vertex.Sum(a => a.VertexCount) != vcnt[i]) { Console.WriteLine("Warning: Model {0} node {1} vertex count does not match first file!", frame, i); } if (!mtn.Models.TryGetValue(i, out amd)) { amd = new AnimModelData() { VertexName = mtn.MdataName + "_vtx_" + i, NormalName = mtn.MdataName + "_nor_" + i }; mtn.Models[i] = amd; } amd.Vertex[frame] = catt.Vertex.SelectMany(a => a.Vertices).ToArray(); if (catt.Vertex.All(a => a.Normals != null)) { amd.Normal[frame] = catt.Vertex.SelectMany(a => a.Normals).ToArray(); } } plist = new List <ushort>(); foreach (PolyChunkStrip pcs in catt.Poly.OfType <PolyChunkStrip>()) { foreach (PolyChunkStrip.Strip s in pcs.Strips) { plist.AddRange(s.Indexes); } } polys[i] = plist.ToArray(); break; } } } foreach ((int _, AnimModelData amd) in mtn.Models) { amd.VertexItemName = Enumerable.Range(0, amd.Vertex.Count).Select(a => amd.VertexName + "_" + a).ToArray(); if (amd.Normal.Count > 0) { amd.NormalItemName = Enumerable.Range(0, amd.Normal.Count).Select(a => amd.NormalName + "_" + a).ToArray(); } } mtn.Save(Path.Combine(path, $"{mtnfn}.saanim")); }
public override void ProcessShapeMotionVertexData(NJS_MOTION motion, float frame, int animindex) { if (!motion.Models.ContainsKey(animindex)) { ProcessVertexData(); return; } Vertex[] vertdata = Vertex; Vertex[] normdata = Normal; AnimModelData data = motion.Models[animindex]; if (data.Vertex.Count > 0) { vertdata = data.GetVertex(frame); } if (data.Normal.Count > 0) { normdata = data.GetNormal(frame); } List <MeshInfo> result = new List <MeshInfo>(); foreach (NJS_MESHSET mesh in Mesh) { bool hasVColor = mesh.VColor != null; bool hasUV = mesh.UV != null; List <Poly> polys = new List <Poly>(); List <VertexData> verts = new List <VertexData>(); int currentstriptotal = 0; foreach (Poly poly in mesh.Poly) { Poly newpoly = null; switch (mesh.PolyType) { case Basic_PolyType.Triangles: newpoly = new Triangle(); break; case Basic_PolyType.Quads: newpoly = new Quad(); break; case Basic_PolyType.NPoly: case Basic_PolyType.Strips: newpoly = new Strip(poly.Indexes.Length, ((Strip)poly).Reversed); break; } for (int i = 0; i < poly.Indexes.Length; i++) { newpoly.Indexes[i] = (ushort)verts.Count; verts.Add(new VertexData( vertdata[poly.Indexes[i]], normdata[poly.Indexes[i]], hasVColor ? (Color?)mesh.VColor[currentstriptotal] : null, hasUV ? mesh.UV[currentstriptotal++] : null)); } polys.Add(newpoly); } NJS_MATERIAL mat = null; if (Material != null && mesh.MaterialID < Material.Count) { mat = Material[mesh.MaterialID]; } result.Add(new MeshInfo(mat, polys.ToArray(), verts.ToArray(), hasUV, hasVColor)); } MeshInfo = result.ToArray(); }