static System.Numerics.Quaternion ConvertQuat(Assimp.Quaternion Q) { NumMatrix4x4.Decompose(ConvertMatrix(new AssMatrix4x4(Q.GetMatrix())), out Vector3 _S, out System.Numerics.Quaternion Quat, out Vector3 _T); return(Quat); // return new System.Numerics.Quaternion(Q.X, Q.Y, Q.Z, Q.W); }
protected override void Initialize() { AddExportHandler <ObjectSet>(filePath => { if (filePath.EndsWith(".fbx", StringComparison.OrdinalIgnoreCase)) { Data.TryFixParentBoneInfos(SourceConfiguration?.BoneDatabase); FbxExporter.ExportToFile(Data, filePath); } else { var configuration = ConfigurationList.Instance.CurrentConfiguration; var objectDatabase = configuration?.ObjectDatabase; var textureDatabase = configuration?.TextureDatabase; var boneDatabase = configuration?.BoneDatabase; Data.Save(filePath, objectDatabase, textureDatabase, boneDatabase); } }); AddExportHandler <Scene>(filePath => { Data.TryFixParentBoneInfos(SourceConfiguration?.BoneDatabase); AssimpExporter.ExportToFile(Data, filePath); }); AddReplaceHandler <ObjectSet>(filePath => { var configuration = ConfigurationList.Instance.CurrentConfiguration; var objectDatabase = configuration?.ObjectDatabase; var textureDatabase = configuration?.TextureDatabase; var objectSet = new ObjectSet(); objectSet.Load(filePath, objectDatabase, textureDatabase); return(objectSet); }); AddReplaceHandler <Scene>(filePath => { if (Data.Objects.Count > 1) { return(AssimpImporter.ImportFromFile(filePath)); } return(AssimpImporter.ImportFromFileWithSingleObject(filePath)); }); AddCustomHandler("Copy object set info to clipboard", () => { uint objectSetId = 39; uint objectId = 0xFFFFFFFF; var objectDatabase = ConfigurationList.Instance.CurrentConfiguration?.ObjectDatabase; if (objectDatabase != null && objectDatabase.ObjectSets.Count > 0) { objectSetId = objectDatabase.ObjectSets.Max(x => x.Id) + 1; objectId = objectDatabase.ObjectSets.SelectMany(x => x.Objects).Max(x => x.Id) + 1; } else { using (var inputDialog = new InputDialog { WindowTitle = "Enter base id for objects", Input = Math.Max(0, Data.Objects.Max(x => x.Id) + 1).ToString() }) { while (inputDialog.ShowDialog() == DialogResult.OK) { bool result = uint.TryParse(inputDialog.Input, out objectId); if (!result || objectId == 0xFFFFFFFF) { MessageBox.Show("Please enter a correct id number.", Program.Name, MessageBoxButtons.OK, MessageBoxIcon.Error); } else { break; } } } } if (objectId == 0xFFFFFFFF) { return; } string baseName = Path.ChangeExtension(Name, null); if (Data.Format.IsClassic() && baseName.EndsWith("_obj", StringComparison.OrdinalIgnoreCase)) { baseName = baseName.Substring(0, baseName.Length - 4); } var objectSetInfo = new ObjectSetInfo { Name = baseName.ToUpperInvariant(), Id = objectSetId, FileName = Name, TextureFileName = baseName + (Data.Format.IsClassic() ? "_tex.bin" : ".txd"), ArchiveFileName = Parent is FarcArchiveNode ? Parent.Name : baseName + ".farc" }; foreach (var obj in Data.Objects) { objectSetInfo.Objects.Add(new ObjectInfo { Id = objectId++, Name = obj.Name.ToUpperInvariant() }); } using (var stringWriter = new StringWriter()) using (var xmlWriter = XmlWriter.Create(stringWriter, new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true })) { sObjectSetInfoSerializer.Serialize(xmlWriter, objectSetInfo, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty })); Clipboard.SetText(stringWriter.ToString()); } }); AddCustomHandlerSeparator(); AddDirtyCustomHandler("Combine all objects into one", () => { if (Data.Objects.Count <= 1) { return(false); } var combinedObject = new Object { Name = "Combined object" }; var indexMap = new Dictionary <int, int>(); foreach (var obj in Data.Objects) { if (obj.Skin != null) { if (combinedObject.Skin == null) { combinedObject.Skin = new Skin(); combinedObject.Skin.Bones.AddRange(obj.Skin.Bones); } else { for (int i = 0; i < obj.Skin.Bones.Count; i++) { var bone = obj.Skin.Bones[i]; int boneIndex = combinedObject.Skin.Bones.FindIndex( x => x.Name.Equals(bone.Name, StringComparison.OrdinalIgnoreCase)); if (boneIndex == -1) { indexMap[i] = combinedObject.Skin.Bones.Count; combinedObject.Skin.Bones.Add(bone); } else { indexMap[i] = boneIndex; } } foreach (var subMesh in obj.Meshes.SelectMany(x => x.SubMeshes)) { if (subMesh.BoneIndices?.Length >= 1) { for (int i = 0; i < subMesh.BoneIndices.Length; i++) { subMesh.BoneIndices[i] = ( ushort )indexMap[subMesh.BoneIndices[i]]; } } } } combinedObject.Skin.Blocks.AddRange(obj.Skin.Blocks); } foreach (var subMesh in obj.Meshes.SelectMany(x => x.SubMeshes)) { subMesh.MaterialIndex += ( uint )combinedObject.Materials.Count; } combinedObject.Meshes.AddRange(obj.Meshes); combinedObject.Materials.AddRange(obj.Materials); } Data.Objects.Clear(); Data.Objects.Add(combinedObject); return(true); }, Keys.None, CustomHandlerFlags.Repopulate | CustomHandlerFlags.ClearMementos); AddCustomHandlerSeparator(); AddDirtyCustomHandler("Convert all bones to osage", () => { foreach (var obj in Data.Objects) { var movingBone = obj.Skin?.Bones.FirstOrDefault(x => x.Name == "kl_mune_b_wj"); if (movingBone == null) { continue; } var nameMap = new Dictionary <string, string>(); var stringBuilder = new StringBuilder(); foreach (var bone in obj.Skin.Bones) { // Ignore bones if they are already OSG or EXP. // Also ignore the moving bone and its parents. // Ignore j_kao_wj for now because it f***s miku's headphones up if (bone.Name == "j_kao_wj") { continue; } var boneToCompare = movingBone; do { if (boneToCompare == bone) { break; } boneToCompare = boneToCompare.Parent; } while (boneToCompare != null); if (boneToCompare == bone) { continue; } if (obj.Skin.Blocks.Any(x => { switch (x) { case OsageBlock osgBlock: return(osgBlock.Nodes.Any(y => y.Name == bone.Name)); case ExpressionBlock expBlock: return(expBlock.Name == bone.Name); default: return(false); } })) { continue; } if (bone.Parent == null) { bone.Parent = movingBone; } Matrix4x4.Invert(bone.InverseBindPoseMatrix, out var bindPoseMatrix); var matrix = Matrix4x4.Multiply(bindPoseMatrix, bone.Parent.InverseBindPoseMatrix); Matrix4x4.Decompose(matrix, out var scale, out var rotation, out var translation); rotation = Quaternion.Normalize(rotation); string newName = bone.Name; if (newName.EndsWith("_wj", StringComparison.OrdinalIgnoreCase)) { newName = newName.Remove(newName.Length - 3); } newName += "_ragdoll"; nameMap.Add(bone.Name, newName); bone.Name = newName; string baseName = newName; var osageBlock = new OsageBlock { ExternalName = $"c_{baseName}_osg", Name = $"e_{baseName}", ParentName = bone.Parent.Name, Position = translation, Rotation = rotation.ToEulerAngles(), Scale = scale }; osageBlock.Nodes.Add(new OsageNode { Name = bone.Name, Length = 0.08f }); obj.Skin.Blocks.Add(osageBlock); stringBuilder.AppendFormat( "{0}.node.0.coli_r=0.030000\r\n" + "{0}.node.0.hinge_ymax=179.000000\r\n" + "{0}.node.0.hinge_ymin=-179.000000\r\n" + "{0}.node.0.hinge_zmax=179.000000\r\n" + "{0}.node.0.hinge_zmin=-179.000000\r\n" + "{0}.node.0.inertial_cancel=1.000000\r\n" + "{0}.node.0.weight=3.000000\r\n" + "{0}.node.length=1\r\n" + "{0}.root.force=0.010000\r\n" + "{0}.root.force_gain=0.300000\r\n" + "{0}.root.friction=1.000000\r\n" + "{0}.root.init_rot_y=0.000000\r\n" + "{0}.root.init_rot_z=0.000000\r\n" + "{0}.root.rot_y=0.000000\r\n" + "{0}.root.rot_z=0.000000\r\n" + "{0}.root.stiffness=0.100000\r\n" + "{0}.root.wind_afc=0.500000\r\n", osageBlock.ExternalName); }