private static void PrintSupported() { SBConsole.WriteLine("Supported Commands"); SBConsole.WriteLine("convanim (anim path) (model path) (anim type)"); SBConsole.WriteLine($"\tSupported Anim Types: {string.Join(", ", MainForm.SupportedAnimExportTypes())}"); }
public static void Open(string FileName, SBScene Scene) { ISSBH_File File; if (SSBH.TryParseSSBHFile(FileName, out File)) { if (File is MESH mesh) { if (mesh.VersionMajor != 1 && mesh.VersionMinor != 10) { SBConsole.WriteLine($"Mesh Version {mesh.VersionMajor}.{mesh.VersionMinor} not supported"); return; } if (mesh.UnknownOffset != 0 || mesh.UnknownSize != 0) { SBConsole.WriteLine($"Warning: Unknown Mesh format detected"); } SBUltimateModel model = new SBUltimateModel(); model.Name = mesh.ModelName; model.BoundingSphere = new Vector4(mesh.BoundingSphereX, mesh.BoundingSphereY, mesh.BoundingSphereZ, mesh.BoundingSphereRadius); ((SBSceneSSBH)Scene).Model = model; SSBHVertexAccessor accessor = new SSBHVertexAccessor(mesh); { foreach (var meshObject in mesh.Objects) { SBUltimateMesh sbMesh = new SBUltimateMesh(); foreach (var attr in meshObject.Attributes) { foreach (var atstring in attr.AttributeStrings) { UltimateVertexAttribute at; if (Enum.TryParse(atstring.Name, out at)) { sbMesh.EnableAttribute(at); } } } sbMesh.Name = meshObject.Name; sbMesh.ParentBone = meshObject.ParentBoneName; sbMesh.BoundingSphere = new BoundingSphere(meshObject.BoundingSphereX, meshObject.BoundingSphereY, meshObject.BoundingSphereZ, meshObject.BoundingSphereRadius); sbMesh.AABoundingBox = new AABoundingBox(new Vector3(meshObject.MinBoundingBoxX, meshObject.MinBoundingBoxY, meshObject.MinBoundingBoxZ), new Vector3(meshObject.MaxBoundingBoxX, meshObject.MaxBoundingBoxY, meshObject.MaxBoundingBoxZ)); sbMesh.OrientedBoundingBox = new OrientedBoundingBox(new Vector3(meshObject.OBBCenterX, meshObject.OBBCenterY, meshObject.OBBCenterZ), new Vector3(meshObject.OBBSizeX, meshObject.OBBSizeY, meshObject.OBBSizeZ), new Matrix3(meshObject.M11, meshObject.M12, meshObject.M13, meshObject.M21, meshObject.M22, meshObject.M23, meshObject.M31, meshObject.M32, meshObject.M33)); sbMesh.Indices = new List <uint>(accessor.ReadIndices(0, meshObject.IndexCount, meshObject)); sbMesh.Vertices = CreateVertices(mesh, Scene.Skeleton, meshObject, accessor, sbMesh.Indices.ToArray()); model.Meshes.Add(sbMesh); } } } } }
public static MODL CreateMODLFile(SBUltimateModel model) { MODL modl = new MODL(); modl.ModelEntries = new MODL_Entry[model.Meshes.Count]; Dictionary <string, int> subindex = new Dictionary <string, int>(); int i = 0; foreach (var mesh in model.Meshes) { var entry = new MODL_Entry(); modl.ModelEntries[i++] = entry; if (!subindex.ContainsKey(mesh.Name)) { subindex.Add(mesh.Name, 0); } entry.MeshName = mesh.Name; entry.SubIndex = subindex[mesh.Name]; if (mesh.Material != null) { entry.MaterialName = mesh.Material.Label; } else { SBConsole.WriteLine("Warning: Missing material"); } subindex[mesh.Name]++; SBConsole.WriteLine($"Creating modl entry: {entry.MeshName} {entry.SubIndex} {entry.MaterialName}"); } return(modl); }
/// <summary> /// /// </summary> /// <param name="animPath"></param> /// <param name="modelPath"></param> /// <param name="output"></param> public static void ConvertAnim(string animPath, string modelPath, string output) { var exporter = MainForm.GetExportableAnimationFromExtension(output); if (exporter != null) { SBConsole.WriteLine($"Converting {animPath} to {Path.ChangeExtension(animPath, output)}"); var scene = MainForm.LoadScene(modelPath, null); if (scene == null) { SBConsole.WriteLine("Error Opening Model"); return; } var mod = scene.GetIOModel(); if (Directory.Exists(animPath)) { foreach (var v in Directory.GetFiles(animPath)) { ConvertAnim(v, Path.ChangeExtension(v, output), SBSkeleton.FromIOSkeleton(mod.Models[0].Skeleton), exporter); } } else { ConvertAnim(animPath, Path.ChangeExtension(animPath, output), SBSkeleton.FromIOSkeleton(mod.Models[0].Skeleton), exporter); } } else { SBConsole.WriteLine($"Unsupported Extension: {output}"); } }
private bool HandleAttribute(XmlReader reader, SBBone bone, string attribute, bool required = true) { string value = reader.GetAttribute(attribute); float val; if (value == null) { if (required) { SBConsole.WriteLine("Expected attribute \"" + attribute + "\""); } return(false); } else if (!float.TryParse(value, out val)) { SBConsole.WriteLine("Failed to parse attribute \"" + attribute + "\""); return(false); } if (!WriteValue(bone, attribute, val)) { return(false); } return(true); }
public static void Open(string FileName, SBScene Scene) { ISSBH_File File; if (SSBH.TryParseSSBHFile(FileName, out File)) { if (File is MESH mesh) { if (mesh.VersionMajor != 1 && mesh.VersionMinor != 10) { SBConsole.WriteLine($"Mesh Version {mesh.VersionMajor}.{mesh.VersionMinor} not supported"); return; } if (mesh.UnknownOffset != 0 || mesh.UnknownSize != 0) { SBConsole.WriteLine($"Warning: Unknown Mesh format detected"); } SBUltimateModel model = new SBUltimateModel(); model.Name = mesh.ModelName; model.BoundingSphere = new Vector4(mesh.BoundingSphereX, mesh.BoundingSphereY, mesh.BoundingSphereZ, mesh.BoundingSphereRadius / 2); Vector3 min = new Vector3(mesh.MinBoundingBoxX, mesh.MinBoundingBoxY, mesh.MinBoundingBoxZ); Vector3 max = new Vector3(mesh.MaxBoundingBoxX, mesh.MaxBoundingBoxY, mesh.MaxBoundingBoxZ); model.VolumeCenter = (max + min) / 2; model.VolumeSize = (max - min) / 2; ((SBSceneSSBH)Scene).Model = model; using (SSBHVertexAccessor accessor = new SSBHVertexAccessor(mesh)) { foreach (var meshObject in mesh.Objects) { SBUltimateMesh <UltimateVertex> sbMesh = new SBUltimateMesh <UltimateVertex>(); foreach (var attr in meshObject.Attributes) { foreach (var atstring in attr.AttributeStrings) { MESHAttribute at; if (Enum.TryParse <MESHAttribute>(atstring.Name, out at)) { //SBConsole.WriteLine("\tLoaded:" + at.ToString()); sbMesh.ExportAttributes.Add(at); } } } sbMesh.Name = meshObject.Name; sbMesh.BoundingSphere = new Vector4(meshObject.BoundingSphereX, meshObject.BoundingSphereY, meshObject.BoundingSphereZ, meshObject.BoundingSphereRadius); sbMesh.ParentBone = meshObject.ParentBoneName; sbMesh.Indices = new List <uint>(accessor.ReadIndices(0, meshObject.IndexCount, meshObject)); sbMesh.Vertices = CreateVertices(mesh, Scene.Skeleton, meshObject, accessor, sbMesh.Indices.ToArray()); model.Meshes.Add(sbMesh); } } } } }
/// <summary> /// Saves the current framebuffer to specified file path /// </summary> public void SaveRender(string FileName) { using (var bitmap = SFGraphics.GLObjects.Framebuffers.Framebuffer.ReadDefaultFramebufferImagePixels(Width, Height, true)) { bitmap.Save(FileName); SBConsole.WriteLine($"Viewport render saved to: {FileName}"); } }
/// <summary> /// converts the rigging to a new skeleton /// </summary> /// <param name="newSkeleton"></param> public void ConvertToSkeleton(SBSkeleton newSkeleton) { if (Skeleton == null || newSkeleton == null) { return; } Dictionary <int, int> oldToNew = new Dictionary <int, int>(); Dictionary <string, int> boneNameToPosition = new Dictionary <string, int>(); for (int i = 0; i < Skeleton.Bones.Length; i++) { var boneName = Skeleton.Bones[i].Name; if (!boneNameToPosition.ContainsKey(boneName)) { boneNameToPosition.Add(boneName, i); } } for (int i = 0; i < newSkeleton.Bones.Length; i++) { if (boneNameToPosition.ContainsKey(newSkeleton.Bones[i].Name)) { oldToNew.Add(boneNameToPosition[newSkeleton.Bones[i].Name], i); } } // Correct Vertex Rigging HashSet <string> mismatchedBones = new HashSet <string>(); foreach (var mesh in Meshes) { for (int i = 0; i < mesh.Vertices.Count; i++) { var vertex = mesh.Vertices[i]; for (int b = 0; b < 4; b++) { if (vertex.BoneWeights[b] > 0 && oldToNew.ContainsKey((int)vertex.BoneIndices[b])) { vertex.BoneIndices[b] = oldToNew[(int)vertex.BoneIndices[b]]; } else { var name = newSkeleton.Bones[(int)vertex.BoneIndices[b]].Name; if (!mismatchedBones.Contains(name)) { SBConsole.WriteLine($"Warning: missmatched bone {name}"); mismatchedBones.Add(name); } } } mesh.Vertices[i] = vertex; } } // accept new skeleton Skeleton = newSkeleton; }
public static void ConvertAnim(string animPath, string output, SBSkeleton skeleton, IExportableAnimation exporter) { var anim = MainForm.LoadAnimation(animPath, skeleton); if (anim == null) { SBConsole.WriteLine("Error Opening Animation"); return; } ConvertAnim(anim, output, skeleton, exporter); }
public static void Open(string FileName, SBScene Scene) { SsbhFile File; if (Ssbh.TryParseSsbhFile(FileName, out File)) { if (File is Mesh mesh) { if (mesh.VersionMajor != 1 && mesh.VersionMinor != 10) { SBConsole.WriteLine($"Mesh Version {mesh.VersionMajor}.{mesh.VersionMinor} not supported"); return; } if (mesh.UnknownOffset != 0 || mesh.UnknownSize != 0) { SBConsole.WriteLine($"Warning: Unknown Mesh format detected"); } SBUltimateModel model = new SBUltimateModel(); model.Name = mesh.ModelName; model.BoundingSphere = new Vector4(mesh.BoundingSphereCenter.X, mesh.BoundingSphereCenter.Y, mesh.BoundingSphereCenter.Z, mesh.BoundingSphereRadius); ((SBSceneSSBH)Scene).Model = model; SsbhVertexAccessor accessor = new SsbhVertexAccessor(mesh); { foreach (var meshObject in mesh.Objects) { SBUltimateMesh sbMesh = new SBUltimateMesh(); sbMesh.EnableAttributes(meshObject); sbMesh.Name = meshObject.Name; sbMesh.ParentBone = meshObject.ParentBoneName; sbMesh.BoundingSphere = new BoundingSphere(meshObject.BoundingSphereCenter.X, meshObject.BoundingSphereCenter.Y, meshObject.BoundingSphereCenter.Z, meshObject.BoundingSphereRadius); sbMesh.AABoundingBox = new AABoundingBox(new Vector3(meshObject.BoundingBoxMin.X, meshObject.BoundingBoxMin.Y, meshObject.BoundingBoxMin.Z), new Vector3(meshObject.BoundingBoxMax.X, meshObject.BoundingBoxMax.Y, meshObject.BoundingBoxMax.Z)); sbMesh.OrientedBoundingBox = new OrientedBoundingBox( meshObject.OrientedBoundingBoxCenter.ToOpenTK(), meshObject.OrientedBoundingBoxSize.ToOpenTK(), meshObject.OrientedBoundingBoxTransform.ToOpenTK()); sbMesh.Indices = new List <uint>(accessor.ReadIndices(0, meshObject.IndexCount, meshObject)); sbMesh.Vertices = CreateVertices(mesh, Scene.Skeleton, meshObject, accessor, sbMesh.Indices.ToArray()); model.Meshes.Add(sbMesh); } } } } }
/// <summary> /// /// </summary> /// <returns></returns> private List <Tuple <double, double> > SimplifyLines(IEnumerable <float> values) { List <Tuple <double, double> > keys = new List <Tuple <double, double> >(); int i = 0; foreach (var val in values) { keys.Add(new Tuple <double, double>(i++, val)); } List <Tuple <double, double> > newkeys = new List <Tuple <double, double> >(); LineSimplification.RamerDouglasPeucker(keys, 0.1, newkeys); SBConsole.WriteLine("Simplified " + keys.Count + " to " + newkeys.Count); return(newkeys); }
private SBBone ReadBone(XmlReader reader, SBBone bone) { SBBone newBone = new SBBone(); newBone.Transform = Matrix4.Identity; if (!reader.ReadToFollowing("SBBone") || reader.NodeType != XmlNodeType.Element || reader.Name != "SBBone") { SBConsole.WriteLine("Expected bone element"); return(null); } string name = reader.GetAttribute("Name"); if (name != bone.Name) { SBConsole.WriteLine("Expected bone definition for " + bone.Name + ", received \"" + name + "\""); return(null); } newBone.Name = bone.Name; if (!HandleAttribute(reader, newBone, "TranslateX") || !HandleAttribute(reader, newBone, "TranslateY") || !HandleAttribute(reader, newBone, "TranslateZ") || !HandleAttribute(reader, newBone, "RotationEulerX") || !HandleAttribute(reader, newBone, "RotationEulerY") || !HandleAttribute(reader, newBone, "RotationEulerZ")) { return(null); } // Optional attributes HandleAttribute(reader, newBone, "ScaleX", false); HandleAttribute(reader, newBone, "ScaleY", false); HandleAttribute(reader, newBone, "ScaleZ", false); foreach (var child in bone.Children) { SBBone newChild = ReadBone(reader, child); if (newChild == null) { return(null); } newBone.AddChild(newChild); } return(newBone); }
private void Read(XmlReader reader, SBSkeleton skeleton) { List <SBBone> roots = new List <SBBone>(); foreach (var bone in skeleton.Roots) { SBBone newBone = ReadBone(reader, bone); if (newBone == null) { SBConsole.WriteLine("Failed to read XML skeleton."); return; } roots.Add(newBone); } UpdateSkeleton(skeleton, roots); }
private bool WriteValue(SBBone bone, string attribute, float value) { if (attribute.Equals("TranslateX")) { bone.X = value; } else if (attribute.Equals("TranslateY")) { bone.Y = value; } else if (attribute.Equals("TranslateZ")) { bone.Z = value; } else if (attribute.Equals("RotationEulerX")) { bone.RX = value; } else if (attribute.Equals("RotationEulerY")) { bone.RY = value; } else if (attribute.Equals("RotationEulerZ")) { bone.RZ = value; } else if (attribute.Equals("ScaleX")) { bone.SX = value; } else if (attribute.Equals("ScaleY")) { bone.SY = value; } else if (attribute.Equals("ScaleZ")) { bone.SZ = value; } else { SBConsole.WriteLine("Internal failure"); return(false); } return(true); }
public override void FromIOModel(IOModel iomodel) { //System.Windows.Forms.MessageBox.Show("Importing Model to DAT not supported"); // use the existing skeleton always iomodel.ConvertToSkeleton((SBSkeleton)Skeleton); // single bound vertices are stored in inverse transform positions iomodel.InvertSingleBinds(); // dobjs to import to var dobjs = GetMeshObjects(); foreach (SBHsdMesh m in GetMeshObjects()) { m.DOBJ.Pobj = null; } var attributeGroup = MakeRiggedAttributes(); var singleAttributeGroup = MakeSingleAttributes(); // get a compressor ready // the compressor will handle making the compressed attribute buffers POBJ_Generator compressor = new POBJ_Generator(); // import the iomeshes into their respective dobjs foreach (var iomesh in iomodel.Meshes) { int dobjId = -1; int.TryParse(iomesh.Name.Replace("DOBJ_", ""), out dobjId); SBConsole.WriteLine(iomesh.Name + " imported:" + (dobjId != -1)); if (dobjId != -1) { var dobj = (SBHsdMesh)dobjs[dobjId]; dobj.ImportPOBJs(iomesh, (SBSkeleton)Skeleton, compressor, dobj.ParentBone == "JOBJ_0" ? attributeGroup : singleAttributeGroup); } } // finalizes and remakes the buffer compressor.SaveChanges(); // refresh everything RefreshRendering(); }
public void AddKey(float frame, T value, InterpolationType type = InterpolationType.Linear, float TanIn = 0, float TanOut = float.MaxValue) { if (_keys.ContainsKey(frame)) { SBConsole.WriteLine($"Warning: Two keys cannot share a frame {frame}"); return; } SBAnimKey <T> key = new SBAnimKey <T>(); key.Frame = frame; key.Value = value; key.InTan = TanIn; key.OutTan = TanOut == float.MaxValue ? TanIn : TanOut; key.InterpolationType = type; _keys.Add(frame, key); }
/// <summary> /// /// </summary> /// <param name="iomodel"></param> public override void FromIOModel(IOScene iomodel) { var model = iomodel.Models[0]; InvertSingleBinds(model); // dobjs to import to var dobjs = GetMeshObjects(); // clear pobjs foreach (SBHsdMesh m in GetMeshObjects()) { m.DOBJ.Pobj = null; } var attributeGroup = MakeRiggedAttributes(); var singleAttributeGroup = MakeSingleAttributes(); // get a compressor ready // the compressor will handle making the compressed attribute buffers POBJ_Generator compressor = new POBJ_Generator(); // import the iomeshes into their respective dobjs foreach (var iomesh in model.Meshes) { int dobjId = -1; int.TryParse(iomesh.Name.Replace("DOBJ_", ""), out dobjId); SBConsole.WriteLine(iomesh.Name + " imported:" + (dobjId != -1)); if (dobjId != -1) { var dobj = (SBHsdMesh)dobjs[dobjId]; dobj.ImportPOBJs(iomesh, (SBSkeleton)Skeleton, compressor, dobj.ParentBone == "JOBJ_0" ? attributeGroup : singleAttributeGroup); } } // finalizes and remakes the buffer compressor.SaveChanges(); // refresh everything RefreshRendering(); }
/// <summary> /// Optimized the vertex index size by removing duplicate vertices /// </summary> public void Optimize() { Indices.Clear(); List <IOVertex> NewVertices = new List <IOVertex>(); Dictionary <IOVertex, uint> vertexToIndex = new Dictionary <IOVertex, uint>(); foreach (var vertex in Vertices) { if (!vertexToIndex.ContainsKey(vertex)) { vertexToIndex.Add(vertex, (uint)NewVertices.Count); NewVertices.Add(vertex); } Indices.Add(vertexToIndex[vertex]); } SBConsole.WriteLine($"Optimized {Name} {Vertices.Count} -> {NewVertices.Count}"); Vertices.Clear(); Vertices.AddRange(NewVertices); }
/// <summary> /// /// </summary> /// <param name="skeleton"></param> /// <returns></returns> public static SBSkeleton FromIOSkeleton(IOSkeleton ioskel) { SBSkeleton skel = new SBSkeleton(); Dictionary <IOBone, SBBone> iotosb = new Dictionary <IOBone, SBBone>(); foreach (var iobone in ioskel.BreathFirstOrder()) { SBConsole.WriteLine(iobone.Name + " " + iobone.Scale + " " + iobone.Rotation + " " + iobone.Translation); SBBone bone = new SBBone() { Name = iobone.Name, SX = iobone.ScaleX, SY = iobone.ScaleY, SZ = iobone.ScaleZ, RX = iobone.RotationEuler.X, RY = iobone.RotationEuler.Y, RZ = iobone.RotationEuler.Z, X = iobone.TranslationX, Y = iobone.TranslationY, Z = iobone.TranslationZ, }; SBConsole.WriteLine(bone.Name + " " + bone.Translation.ToString() + " " + bone.Scale.ToString() + " " + bone.RotationQuaternion.ToString()); iotosb.Add(iobone, bone); if (iobone.Parent == null) { skel.AddRoot(bone); } else { bone.Parent = iotosb[iobone.Parent]; } } return(skel); }
public static void ProcessCommands(string[] args) { switch (args[0]) { case "convanim": if (args.Length < 4) { SBConsole.WriteLine("Invalid argument count, expected 4"); PrintSupported(); } else { ConvertAnim(args[1], args[2], args[3]); } break; default: SBConsole.WriteLine($"Unknown command {args[0]}"); PrintSupported(); break; } }
/// <summary> /// /// </summary> /// <param name="FileName"></param> public override void ExportSceneToFile(string FileName) { if (Model == null) { return; } string name = Path.GetDirectoryName(FileName) + "/" + Path.GetFileNameWithoutExtension(FileName); string simpleName = Path.GetFileNameWithoutExtension(FileName); SBConsole.WriteLine("Creating MODL..."); var modl = MODL_Loader.CreateMODLFile((SBUltimateModel)Model); modl.ModelFileName = simpleName; modl.SkeletonFileName = $"{simpleName}.nusktb"; modl.MeshString = $"{simpleName}.numshb"; modl.UnknownFileName = ""; modl.MaterialFileNames = new MODL_MaterialName[] { new MODL_MaterialName() { MaterialFileName = $"{simpleName}.numatb" } }; SBConsole.WriteLine("Done"); SSBH.TrySaveSSBHFile(FileName, modl); SBConsole.WriteLine($"Creating MESH... {name}.numshb"); var mesh = MESH_Loader.CreateMESH((SBUltimateModel)Model, (SBSkeleton)Skeleton); SBConsole.WriteLine("Done"); SSBH.TrySaveSSBHFile(name + ".numshb", mesh); SBConsole.WriteLine($"Creating SKEL.. {name}.nusktb"); SKEL_Loader.Save(name + ".nusktb", this); SBConsole.WriteLine("Done"); //SBConsole.WriteLine("Creating MATL..."); }
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); }
public IOModel ImportIOModel(string FileName) { IOModel model = new IOModel(); SBSkeleton skeleton = new SBSkeleton(); model.Skeleton = skeleton; var test = Fbx.FbxIO.ReadBinary(FileName); if (test.Version != Fbx.FbxVersion.v7_4) { throw new NotSupportedException($"Only FBX version 7.4 is currently supported: Imported version = {test.Version}"); } // global settings float Scale = 1; // read global settings var settings = test.GetNodesByName("GlobalSettings"); if (settings.Length != 0) { var prop70 = settings[0].GetNodesByName("Properties70"); if (prop70.Length != 0) { foreach (var property in prop70[0].Nodes) { if (property == null) { continue; } if (property.Properties.Count > 0 && property.Name == "P") { //TODO: this is inaccurate... //if (property.Properties[0].Equals("UnitScaleFactor")) // Scale = (float)(double)property.Properties[4]; } } } } FbxAccessor accessor = new FbxAccessor(FileName); //Bones var limbs = accessor.GetLimbNodes(); SBConsole.WriteLine($"Limb Node Count: {limbs.Length}"); foreach (var limb in limbs) { skeleton.AddRoot(ConvertLimbToSBBone(limb)); } foreach (var root in skeleton.Roots) { root.Scale *= Scale; } // Fast access to bone indices Dictionary <string, int> BoneNameToIndex = new Dictionary <string, int>(); foreach (var b in skeleton.Bones) { BoneNameToIndex.Add(b.Name, skeleton.IndexOfBone(b)); } // Mesh var models = accessor.GetModels(); SBConsole.WriteLine($"Model Node Count: {models.Count}"); int YupAxis = accessor.GetOriginalXAxis(); SBConsole.WriteLine("Yup: " + YupAxis); foreach (var mod in models) { // rotation 90 Matrix4 transform = (ImportSettings.Rotate90 ? Matrix4.CreateRotationX(-90 * DegToRag) : Matrix4.Identity) * GetModelTransform(mod); foreach (var geom in mod.Geometries) { IOMesh mesh = new IOMesh(); mesh.Name = mod.Name; model.Meshes.Add(mesh); // Create Rigging information Vector4[] BoneIndices = new Vector4[geom.Vertices.Length]; Vector4[] BoneWeights = new Vector4[geom.Vertices.Length]; foreach (var deformer in geom.Deformers) { //TODO: this shouldn't happen... if (!BoneNameToIndex.ContainsKey(deformer.Name)) { continue; } int index = BoneNameToIndex[deformer.Name]; for (int i = 0; i < deformer.Indices.Length; i++) { int vertexIndex = deformer.Indices[i]; for (int j = 0; j < 4; j++) { if (BoneWeights[vertexIndex][j] == 0) { BoneIndices[vertexIndex][j] = index; BoneWeights[vertexIndex][j] = (float)deformer.Weights[i]; break; } } } //SBConsole.WriteLine(deformer.Name + " " + deformer.Weights.Length + " " + deformer.Indices.Length + " " + index); } // Explanation: // negative values are used to indicate a stopping point for the face // so every 3rd index needed to be adjusted for (int i = 0; i < geom.Indices.Length; i += 3) { mesh.Indices.Add((uint)i); mesh.Indices.Add((uint)i + 1); mesh.Indices.Add((uint)i + 2); mesh.Vertices.Add(CreateVertex(transform, geom, i, BoneIndices, BoneWeights, Scale)); mesh.Vertices.Add(CreateVertex(transform, geom, i + 1, BoneIndices, BoneWeights, Scale)); mesh.Vertices.Add(CreateVertex(transform, geom, i + 2, BoneIndices, BoneWeights, Scale)); } mesh.HasPositions = true; //SBConsole.WriteLine(geom.Vertices.Length); foreach (var layer in geom.Layers) { switch (layer.Name) { case "LayerElementNormal": case "LayerElementUV": case "LayerElementColor": break; default: SBConsole.WriteLine(layer.Name + " " + layer.ReferenceInformationType + " " + layer.Data.Length + " " + (layer.ReferenceInformationType.Equals("IndexToDirect") ? layer.Indices.Length.ToString() : "")); break; } } mesh.Optimize(); } } return(model); }
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); } }
/// <summary> /// Load an ultimate mesh from file to the given scene /// </summary> /// <param name="FileName"></param> /// <param name="Scene"></param> public static void Open(string FileName, SBScene Scene) { SsbhFile File; if (Ssbh.TryParseSsbhFile(FileName, out File)) { if (File is Matl matl) { if (matl.MajorVersion != 1 && matl.MinorVersion != 6) { SBConsole.WriteLine($"Warning: Mesh Version {matl.MajorVersion}.{matl.MinorVersion} not supported"); } var MaterialProps = typeof(UltimateMaterial).GetProperties(); // use dictionary for faster lookup Dictionary <string, PropertyInfo> NameToProperty = new Dictionary <string, PropertyInfo>(); foreach (var prop in MaterialProps) { var attrName = prop.Name; if (attrName != null) { NameToProperty.Add(attrName, prop); } } foreach (var entry in matl.Entries) { UltimateMaterial material = new UltimateMaterial(); material.Name = entry.ShaderLabel; material.Label = entry.MaterialLabel; Scene.Materials.Add(material); foreach (var attr in entry.Attributes) { if (NameToProperty.ContainsKey(attr.ParamId.ToString())) { var prop = NameToProperty[attr.ParamId.ToString()]; if (prop.PropertyType == typeof(SBMatAttrib <string>)) { material.SetProperty(attr.ParamId.ToString(), attr.DataObject.ToString()); } else if (prop.PropertyType == typeof(SBMatAttrib <Vector4>)) { material.SetProperty(attr.ParamId.ToString(), MATLVectorToGLVector((MatlAttribute.MatlVector4)attr.DataObject)); } else { material.SetProperty(attr.ParamId.ToString(), attr.DataObject); } } else { switch (attr.ParamId) { case MatlEnums.ParamId.RasterizerState0: material.RasterizerState = (MatlAttribute.MatlRasterizerState)attr.DataObject; break; case MatlEnums.ParamId.BlendState0: material.BlendState = (MatlAttribute.MatlBlendState)attr.DataObject; break; default: //SBConsole.WriteLine("Extra Param: " + attr.ParamID.ToString() + " = " + attr.DataObject.ToString()); material.extraParams.Add(attr.ParamId, attr.DataObject); break; } } } } } } }
/// <summary> /// Load an ultimate mesh from file to the given scene /// </summary> /// <param name="FileName"></param> /// <param name="Scene"></param> public static void Open(string FileName, SBScene Scene) { ISSBH_File File; if (SSBH.TryParseSSBHFile(FileName, out File)) { if (File is MATL matl) { if (matl.MajorVersion != 1 && matl.MinorVersion != 6) { SBConsole.WriteLine($"Warning: Mesh Version {matl.MajorVersion}.{matl.MinorVersion} not supported"); } var MaterialProps = typeof(UltimateMaterial).GetProperties(); // use dictionary for faster lookup Dictionary <string, PropertyInfo> NameToProperty = new Dictionary <string, PropertyInfo>(); foreach (var prop in MaterialProps) { var attrName = (MATLLoaderAttributeName)prop.GetCustomAttribute(typeof(MATLLoaderAttributeName)); if (attrName != null) { NameToProperty.Add(attrName.Name, prop); } } foreach (var entry in matl.Entries) { UltimateMaterial material = new UltimateMaterial(); material.Name = entry.MaterialName; material.Label = entry.MaterialLabel; Scene.Materials.Add(material); foreach (var attr in entry.Attributes) { if (NameToProperty.ContainsKey(attr.ParamID.ToString())) { var prop = NameToProperty[attr.ParamID.ToString()]; if (prop.PropertyType == typeof(SBMatAttrib <string>)) { ((SBMatAttrib <string>)prop.GetValue(material)).Value = attr.DataObject.ToString(); //foreach (var surface in Scene.Surfaces) // if (surface.Name == attr.DataObject.ToString()) // prop.SetValue(material, surface); } if (prop.PropertyType == typeof(SBMatAttrib <Vector4>)) { ((SBMatAttrib <Vector4>)prop.GetValue(material)).Value = MATLVectorToGLVector((MatlAttribute.MatlVector4)attr.DataObject); } if (prop.PropertyType == typeof(SBMatAttrib <float>)) { ((SBMatAttrib <float>)prop.GetValue(material)).Value = (float)attr.DataObject; } if (prop.PropertyType == typeof(SBMatAttrib <bool>)) { ((SBMatAttrib <bool>)prop.GetValue(material)).Value = (bool)attr.DataObject; } } else { switch (attr.ParamID) { case MatlEnums.ParamId.RasterizerState0: material.RasterizerState = (MatlAttribute.MatlRasterizerState)attr.DataObject; break; case MatlEnums.ParamId.BlendState0: material.BlendState = (MatlAttribute.MatlBlendState)attr.DataObject; break; default: SBConsole.WriteLine("Extra Param: " + attr.ParamID.ToString() + " = " + attr.DataObject.ToString()); material.extraParams.Add(attr.ParamID, attr.DataObject); break; } } } } } } }
/// <summary> /// Loads the scene from a NUMDLB file /// </summary> /// <param name="FileName"></param> public override void LoadFromFile(string FileName) { string folderPath = Path.GetDirectoryName(FileName); ISSBH_File File; if (!SSBH.TryParseSSBHFile(FileName, out File)) { return; } MODL modl = (MODL)File; string meshPath = ""; string skelPath = ""; string matlPath = ""; foreach (string file in Directory.EnumerateFiles(folderPath)) { // load textures if (file.EndsWith(".nutexb")) { NUTEX_Loader.Open(file, this); } string fileName = Path.GetFileName(file); if (fileName.Equals(modl.SkeletonFileName)) { skelPath = file; } if (fileName.Equals(modl.MeshString)) { meshPath = file; } if (fileName.Equals(modl.MaterialFileNames[0].MaterialFileName)) { matlPath = file; } } // import order Skeleton+Textures->Materials->Mesh // mesh needs to be loaded after skeleton if (skelPath != "") { SBConsole.WriteLine($"Importing skeleton: {Path.GetFileName(skelPath)}"); SKEL_Loader.Open(skelPath, this); } if (matlPath != "") { SBConsole.WriteLine($"Importing materials: {Path.GetFileName(matlPath)}"); MATL_Loader.Open(matlPath, this); } if (meshPath != "") { SBConsole.WriteLine($"Importing mesh: {Path.GetFileName(meshPath)}"); MESH_Loader.Open(meshPath, this); // set materials foreach (var entry in modl.ModelEntries) { UltimateMaterial currentMaterial = null; foreach (UltimateMaterial matentry in Materials) { if (matentry.Label.Equals(entry.MaterialName)) { currentMaterial = matentry; break; } } if (currentMaterial == null) { continue; } int subindex = 0; string prevMesh = ""; if (Model != null) { foreach (var mesh in Model.Meshes) { if (prevMesh.Equals(mesh.Name)) { subindex++; } else { subindex = 0; } prevMesh = mesh.Name; if (subindex == entry.SubIndex && mesh.Name.Equals(entry.MeshName)) { mesh.Material = currentMaterial; break; } } } } } }
public SBAnimation ImportSBAnimation(string FileName, SBSkeleton skeleton, string jvcPath) { var sbAnim = new SBAnimation(); var jointTable = GetJointTable(jvcPath); var anim = new Anim(); using (BinaryReaderExt r = new BinaryReaderExt(new FileStream(FileName, FileMode.Open))) anim.Parse(r); float scale = Settings.FrameScale; sbAnim.FrameCount = (float)(scale * anim.EndTime); foreach (var j in anim.Joints) { if (j.BoneID == -1) { continue; } if (j.BoneID >= jointTable.Length || jointTable[j.BoneID] == -1 || jointTable[j.BoneID] >= skeleton.Bones.Length) { continue; } var bone = skeleton.Bones[jointTable[j.BoneID]]; SBTransformAnimation node = sbAnim.TransformNodes.Find(e => e.Name == bone.Name); if (node == null) { node = new SBTransformAnimation(); node.Name = bone.Name; sbAnim.TransformNodes.Add(node); } //Console.WriteLine(bone.Name); if (j.Flag3 == 0x21) { foreach (var k in j.Keys) { //Console.WriteLine($"\t{k.Time} {k.X} {k.Y} {k.Z} {k.W}"); node.AddKey((float)Math.Ceiling(k.Time * scale), bone.X + k.X, SBTrackType.TranslateX); node.AddKey((float)Math.Ceiling(k.Time * scale), bone.Y + k.Y, SBTrackType.TranslateY); node.AddKey((float)Math.Ceiling(k.Time * scale), bone.Z + k.Z, SBTrackType.TranslateZ); } } else if (j.Flag3 == 0x28) { var eul0 = ToQuat(bone.RotationQuaternion, j.Keys[0].W, new Vector3(j.Keys[0].X, j.Keys[0].Y, j.Keys[0].Z)); node.AddKey(0, eul0.X, SBTrackType.RotateX, InterpolationType.Step); node.AddKey(0, eul0.Y, SBTrackType.RotateY, InterpolationType.Step); node.AddKey(0, eul0.Z, SBTrackType.RotateZ, InterpolationType.Step); node.AddKey(0, eul0.W, SBTrackType.RotateW, InterpolationType.Step); foreach (var k in j.Keys) { var eul = ToQuat(bone.RotationQuaternion, k.W, new Vector3(k.X, k.Y, k.Z)); node.AddKey((float)Math.Ceiling(k.Time * scale), eul.X, SBTrackType.RotateX); node.AddKey((float)Math.Ceiling(k.Time * scale), eul.Y, SBTrackType.RotateY); node.AddKey((float)Math.Ceiling(k.Time * scale), eul.Z, SBTrackType.RotateZ); node.AddKey((float)Math.Ceiling(k.Time * scale), eul.W, SBTrackType.RotateW); } } else { SBConsole.WriteLine("Unknown MOT Track Type " + j.Flag3.ToString("X")); } } sbAnim.ConvertRotationKeysToEuler(); return(sbAnim); }
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); }
private static void Draw(Camera camera, string Text, Matrix4 Transform, Vector3 Color, Vector2 WindowPosition, int Size = 16, bool Blend = false, bool Center = false, bool RelativeToWorld = true) { GL.PushAttrib(AttribMask.AllAttribBits); GL.Enable(EnableCap.Texture2D); if (Blend) { GL.Enable(EnableCap.Blend); GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); } if (characterShape == null) { characterShape = new CharacterShape(); } var shader = ShaderManager.GetShader("Text"); if (shader != null && shader.LinkStatusIsOk) { shader.UseProgram(); shader.SetTexture("fontSheet", DefaultTextures.Instance.renderFont, 0); shader.SetMatrix4x4("transform", ref Transform); shader.SetFloat("letterSize", Size); shader.SetVector2("windowSize", new Vector2(camera.RenderHeight, camera.RenderWidth)); shader.SetVector2("windowPosition", WindowPosition); shader.SetBoolToInt("relativeToWorld", RelativeToWorld); int offset = 0; int LetterSize = Size - (int)(0.45f * Size); if (Center) { offset = -(Text.Length * LetterSize) / 2; } // TODO: drawing each letter individually can be slow // maybe build a mesh for them? foreach (char c in Text.ToCharArray()) { shader.SetFloat("letterIndex", c - ' '); shader.SetVector3("letterColor", Color); shader.SetVector2("letterPosition", new Vector2(offset, 0)); characterShape.Draw(shader, camera); //Drop shadow shader.SetVector3("letterColor", new Vector3(0, 0, 0)); shader.SetVector2("letterPosition", new Vector2(offset + 1, -1)); characterShape.Draw(shader, camera); offset += LetterSize; } } else { SBConsole.WriteLine(shader.GetErrorLog()); } GL.PopAttrib(); }