public static void ImportDS1CollisionHKX(HKX hkx, string assetName) { // Setup a game object asset GameObject root = new GameObject(Path.GetFileNameWithoutExtension(assetName)); if (!AssetDatabase.IsValidFolder(assetName)) { AssetDatabase.CreateFolder(Path.GetDirectoryName(assetName + ".blah"), Path.GetFileNameWithoutExtension(assetName + ".blah")); } int index = 0; foreach (var col in hkx.DataSection.Objects) { if (col is HKX.HKPStorageExtendedMeshShapeMeshSubpartStorage) { var verts = new List <Vector3>(); var normals = new List <Vector3>(); var indices = new List <int>(); var coldata = (HKX.HKPStorageExtendedMeshShapeMeshSubpartStorage)col; for (int i = 0; i < coldata.Indices16.Size / 4; i++) { var vert0 = coldata.Vertices.GetArrayData().Elements[coldata.Indices16.GetArrayData().Elements[i * 4].data]; var vert1 = coldata.Vertices.GetArrayData().Elements[coldata.Indices16.GetArrayData().Elements[i * 4 + 1].data]; var vert2 = coldata.Vertices.GetArrayData().Elements[coldata.Indices16.GetArrayData().Elements[i * 4 + 2].data]; verts.Add(new Vector3(vert0.Vector.X, vert0.Vector.Y, vert0.Vector.Z)); verts.Add(new Vector3(vert1.Vector.X, vert1.Vector.Y, vert1.Vector.Z)); verts.Add(new Vector3(vert2.Vector.X, vert2.Vector.Y, vert2.Vector.Z)); indices.Add(i * 3); indices.Add(i * 3 + 1); indices.Add(i * 3 + 2); } var mesh = new Mesh(); mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; mesh.subMeshCount = 1; mesh.SetVertices(verts); mesh.SetTriangles(indices.ToArray(), 0, true); mesh.RecalculateNormals(); mesh.RecalculateBounds(); AssetDatabase.CreateAsset(mesh, assetName + "/" + Path.GetFileNameWithoutExtension(assetName) + "_" + index + ".mesh"); // Setup a game object asset GameObject obj = new GameObject(Path.GetFileNameWithoutExtension(assetName) + $@"_{index}"); obj.AddComponent <MeshFilter>(); obj.AddComponent <MeshRenderer>(); obj.GetComponent <MeshFilter>().mesh = mesh; obj.GetComponent <MeshRenderer>().material = AssetDatabase.LoadAssetAtPath <Material>("Assets/dstools/Materials/CollisionMeshMaterial.mat"); obj.transform.parent = root.transform; index++; } } PrefabUtility.SaveAsPrefabAsset(root, assetName + ".prefab"); Object.DestroyImmediate(root); }
public override void Read(HKX hkx, HKXSection section, BinaryReaderEx br, HKXVariation variation) { SectionOffset = (uint)br.Position; AssertPointer(hkx, br); AssertPointer(hkx, br); AssertPointer(hkx, br); AssertPointer(hkx, br); TransformTrackToBoneIndices = new HKArray <HKShort>(hkx, section, this, br, variation); FloatTrackToFloatSlotIndices = new HKArray <HKShort>(hkx, section, this, br, variation); if (variation != HKXVariation.HKXDS1) { //PartitionIndices = new HKArray<HKShort>(hkx, section, this, br, variation); PartitionIndices = new HKArray <HKShort>(hkx, section, this, br, variation); BlendHint = br.ReadEnum32 <AnimationBlendHint>(); } else { BlendHint = br.ReadEnum32 <AnimationBlendHint>(); OriginalSkeletonName = br.ReadShiftJIS(); br.Pad(16); } br.Pad(16); DataSize = (uint)br.Position - SectionOffset; ResolveDestinations(hkx, section); }
bool IResource._Load(string file, AccessLevel al, GameType type) { if (type == GameType.Bloodborne) { Hkx = HKX.Read(file, HKX.HKXVariation.HKXBloodBorne); } else if (type == GameType.DarkSoulsIII) { DCX.Type t; var decomp = DCX.Decompress(file, out t); var br = new BinaryReaderEx(false, decomp); var des = new HKX2.PackFileDeserializer(); Hkx2 = (hkRootLevelContainer)des.Deserialize(br); } else { Hkx = HKX.Read(file); } if (type == GameType.DarkSoulsIISOTFS || type == GameType.DarkSoulsIII || type == GameType.Bloodborne) { FrontFace = FrontFace.Clockwise; } else { FrontFace = FrontFace.CounterClockwise; } if (type == GameType.DarkSoulsIII) { return(LoadInternalNew(al)); } return(LoadInternal(al)); }
public override void Read(HKX hkx, HKXSection section, HKXObject source, BinaryReaderEx br, HKXVariation variation) { Position = new HKVector4(); Position.Read(hkx, section, source, br, variation); Rotation = new HKVector4(); Rotation.Read(hkx, section, source, br, variation); Scale = new HKVector4(); Scale.Read(hkx, section, source, br, variation); }
public override void Read(HKX hkx, HKXSection section, HKXObject source, BinaryReaderEx br, HKXVariation variation) { //AssertPointer(hkx, br); //br.ReadUInt64s(1); // blah Name = new HKCString(hkx, section, source, br, variation); LockTranslation = br.ReadInt32(); if (variation != HKXVariation.HKXDS1) { br.ReadInt32(); // Padding? } }
public void Write(HKX hkx, HKXSection section, BinaryWriterEx bw, uint sectionBaseOffset, HKX.HKXVariation variation) { foreach (var cls in ClassNames) { cls.Write(bw); } while ((bw.Position % 16) != 0) { // Write padding bytes to 16 byte align bw.WriteByte(0xFF); } }
private static Dictionary <string, HavokBoneMeme> LoadBonesFromHKX2010(string hkxName) { var hkx = HKX.Read(hkxName, HKX.HKXVariation.HKXDS1, isDS1RAnimHotfix: false); var result = new Dictionary <string, HavokBoneMeme>(); foreach (var obj in hkx.DataSection.Objects) { if (obj is HKX.HKASkeleton skelington) { List <string> boneNames = new List <string>(); foreach (var bone in skelington.Bones.GetArrayData().Elements) { var n = bone.Name.GetString(); result.Add(n, new HavokBoneMeme()); boneNames.Add(n); } var skelingtonTransf = skelington.Transforms.GetArrayData().Elements; for (int i = 0; i < skelingtonTransf.Count; i++) { result[boneNames[i]].ReferenceTransform.Translation = skelingtonTransf[i].Position.Vector.ToVector3(); result[boneNames[i]].ReferenceTransform.Scale = skelingtonTransf[i].Scale.Vector.ToVector3(); result[boneNames[i]].ReferenceTransform.Rotation = skelingtonTransf[i].Rotation.Vector.ToQuat(); } var skelingtonParentIndices = skelington.ParentIndices.GetArrayData().Elements; for (int i = 0; i < skelingtonParentIndices.Count; i++) { var parentIndex = skelingtonParentIndices[i].data; result[boneNames[i]].Parent = parentIndex >= 0 ? boneNames[parentIndex] : null; } foreach (var kvp in result) { if (kvp.Value.Parent != null) { if (!result[kvp.Value.Parent].Children.Contains(kvp.Key)) { result[kvp.Value.Parent].Children.Add(kvp.Key); } } } } } return(result); }
private static ModelDataType GetModelDataType(byte[] fileContents) { if (FLVER2.Is(fileContents)) { return(ModelDataType.Flver); } if (HKX.Is(fileContents)) { return(ModelDataType.Hkx); } return(ModelDataType.Unk); }
private bool LoadInternalNew(AccessLevel al) { if (al == AccessLevel.AccessFull || al == AccessLevel.AccessGPUOptimizedOnly) { Bounds = new BoundingBox(); var submeshes = new List <CollisionSubmesh>(); bool first = true; if (Hkx2.m_namedVariants.Count == 0) { // Yes this happens for some cols wtf From??? return(false); } var physicsscene = (hknpPhysicsSceneData)Hkx2.m_namedVariants[0].m_variant; foreach (var bodyInfo in physicsscene.m_systemDatas[0].m_bodyCinfos) { var ncol = (HKX2.fsnpCustomParamCompressedMeshShape)bodyInfo.m_shape; try { var mesh = new CollisionSubmesh(); ProcessMesh(ncol, bodyInfo, mesh); if (first) { Bounds = mesh.Bounds; first = false; } else { Bounds = BoundingBox.Combine(Bounds, mesh.Bounds); } submeshes.Add(mesh); } catch (Exception e) { // Debug failing cases later } } GPUMeshes = submeshes.ToArray(); } if (al == AccessLevel.AccessGPUOptimizedOnly) { Hkx = null; } return(true); }
public override void Read(HKX hkx, HKXSection section, BinaryReaderEx br, HKXVariation variation) { SectionOffset = (uint)br.Position; AssertPointer(hkx, br); if (variation == HKXVariation.HKXBloodBorne) { br.AssertInt32(0); } else { AssertPointer(hkx, br); } AnimationType = br.ReadEnum32 <AnimationType>(); Duration = br.ReadSingle(); TransformTrackCount = br.ReadInt32(); FloatTrackCount = br.ReadInt32(); if (variation == HKXVariation.HKXBloodBorne) { br.Pad(16); } if (variation == HKXVariation.HKXDS1) { br.ReadInt64s(2); // Annotations } else { // Literally guessing here br.ReadInt64s(3); // Annotations //br.ReadUInt32(); // padding? } Transforms = new HKArray <Transform>(hkx, section, this, br, variation); Floats = new HKArray <HKFloat>(hkx, section, this, br, variation); DataSize = (uint)br.Position - SectionOffset; ResolveDestinations(hkx, section); }
bool IResource._Load(string file, AccessLevel al, GameType type) { if (type == GameType.Bloodborne) { Hkx = HKX.Read(file, HKX.HKXVariation.HKXBloodBorne); } else { Hkx = HKX.Read(file); } if (type == GameType.DarkSoulsIISOTFS || type == GameType.DarkSoulsIII || type == GameType.Bloodborne) { FrontFace = FrontFace.Clockwise; } else { FrontFace = FrontFace.CounterClockwise; } return(LoadInternal(al)); }
public override void Read(HKX hkx, HKXSection section, BinaryReaderEx br, HKXVariation variation) { SectionOffset = (uint)br.Position; AssertPointer(hkx, br); AssertPointer(hkx, br); //br.ReadUInt64s(1); // Name Name = new HKCString(hkx, section, this, br, variation); ParentIndices = new HKArray <HKShort>(hkx, section, this, br, variation); Bones = new HKArray <Bone>(hkx, section, this, br, variation); Transforms = new HKArray <Transform>(hkx, section, this, br, variation); ReferenceFloats = new HKArray <HKFloat>(hkx, section, this, br, variation); br.ReadUInt64s(2); // unused array br.ReadUInt64s(2); // unused array br.ReadUInt64s(2); // unused array br.ReadUInt64s(2); // unused array br.ReadUInt64s(1); // padding DataSize = (uint)br.Position - SectionOffset; ResolveDestinations(hkx, section); }
public Model(HKX hkx) { Type = ModelType.ModelTypeCollision; Submeshes = new List <FlverSubmeshRenderer>(); var subBoundsPoints = new List <Vector3>(); foreach (var col in hkx.DataSection.Objects) { if (col is HKX.FSNPCustomParamCompressedMeshShape) { var smm = new FlverSubmeshRenderer(this, hkx, (HKX.FSNPCustomParamCompressedMeshShape)col); Submeshes.Add(smm); subBoundsPoints.Add(smm.Bounds.Min); subBoundsPoints.Add(smm.Bounds.Max); } if (col is HKX.HKPStorageExtendedMeshShapeMeshSubpartStorage) { var smm = new FlverSubmeshRenderer(this, hkx, (HKX.HKPStorageExtendedMeshShapeMeshSubpartStorage)col); Submeshes.Add(smm); subBoundsPoints.Add(smm.Bounds.Min); subBoundsPoints.Add(smm.Bounds.Max); } } if (Submeshes.Count == 0) { Bounds = new BoundingBox(); IsVisible = false; } else { Bounds = BoundingBox.CreateFromPoints(subBoundsPoints); } }
public override void Read(HKX hkx, HKXSection section, BinaryReaderEx br, HKXVariation variation) { SectionOffset = (uint)br.Position; AssertPointer(hkx, br); AssertPointer(hkx, br); if (variation != HKXVariation.HKXBloodBorne) { AssertPointer(hkx, br); AssertPointer(hkx, br); } Up.X = br.ReadSingle(); Up.Y = br.ReadSingle(); Up.Z = br.ReadSingle(); Up.W = br.ReadSingle(); Forward.X = br.ReadSingle(); Forward.Y = br.ReadSingle(); Forward.Z = br.ReadSingle(); Forward.W = br.ReadSingle(); Duration = br.ReadSingle(); if (variation != HKXVariation.HKXDS1) { br.AssertInt32(0); // probably padding } ReferenceFrameSamples = new HKArray <HKVector4>(hkx, section, this, br, variation); br.Pad(16); // probably DataSize = (uint)br.Position - SectionOffset; ResolveDestinations(hkx, section); }
private bool LoadInternal(AccessLevel al) { if (al == AccessLevel.AccessFull || al == AccessLevel.AccessGPUOptimizedOnly) { Bounds = new BoundingBox(); var submeshes = new List <CollisionSubmesh>(); bool first = true; foreach (var obj in Hkx.DataSection.Objects) { if (obj is HKX.HKPStorageExtendedMeshShapeMeshSubpartStorage col) { var mesh = new CollisionSubmesh(); ProcessMesh(col, mesh); if (first) { Bounds = mesh.Bounds; first = false; } else { Bounds = BoundingBox.Combine(Bounds, mesh.Bounds); } submeshes.Add(mesh); } if (obj is HKX.FSNPCustomParamCompressedMeshShape ncol) { // Find a body data for this HKX.HKNPBodyCInfo bodyInfo = null; foreach (var scene in Hkx.DataSection.Objects) { if (scene is HKX.HKNPPhysicsSystemData) { var sys = (HKX.HKNPPhysicsSystemData)scene; foreach (HKX.HKNPBodyCInfo info in sys.Bodies.GetArrayData().Elements) { if (info.ShapeReference.DestObject == ncol) { bodyInfo = info; break; } } break; } try { var mesh = new CollisionSubmesh(); ProcessMesh(ncol, bodyInfo, mesh); if (first) { Bounds = mesh.Bounds; first = false; } else { Bounds = BoundingBox.Combine(Bounds, mesh.Bounds); } submeshes.Add(mesh); } catch (Exception e) { // Debug failing cases later } } } //Bounds = BoundingBox.CreateMerged(Bounds, GPUMeshes[i].Bounds); } GPUMeshes = submeshes.ToArray(); } if (al == AccessLevel.AccessGPUOptimizedOnly) { Hkx = null; } return(true); }
public static void ImportDS3CollisionHKX(HKX hkx, string assetName) { var verts = new List <Vector3>(); var normals = new List <Vector3>(); var indices = new List <int>(); foreach (var col in hkx.DataSection.Objects) { if (col is HKX.FSNPCustomParamCompressedMeshShape) { // Find a body data for this HKX.HKNPBodyCInfo bodyInfo = null; foreach (var scene in hkx.DataSection.Objects) { if (scene is HKX.HKNPPhysicsSystemData) { var sys = (HKX.HKNPPhysicsSystemData)scene; foreach (HKX.HKNPBodyCInfo info in sys.Bodies.GetArrayData().Elements) { if (info.ShapeReference.DestObject == col) { bodyInfo = info; break; } } break; } } var meshdata = (HKX.FSNPCustomParamCompressedMeshShape)col; var coldata = meshdata.GetMeshShapeData(); foreach (var chunk in coldata.Chunks.GetArrayData().Elements) { for (int i = 0; i < chunk.ByteIndicesLength; i++) { var tri = coldata.MeshIndices.GetArrayData().Elements[i + chunk.ByteIndicesIndex]; //if (tri.Idx2 == tri.Idx3 && tri.Idx1 != tri.Idx2) //{ if (tri.Idx0 < chunk.VertexIndicesLength) { ushort index = (ushort)((uint)tri.Idx0 + chunk.SmallVerticesBase); indices.Add(verts.Count); var vert = coldata.SmallVertices.GetArrayData().Elements[index].Decompress(chunk.SmallVertexScale, chunk.SmallVertexOffset); verts.Add(TransformVert(vert, bodyInfo)); } else { ushort index = (ushort)(coldata.VertexIndices.GetArrayData().Elements[tri.Idx0 + chunk.VertexIndicesIndex - chunk.VertexIndicesLength].data); indices.Add(verts.Count); var vert = coldata.LargeVertices.GetArrayData().Elements[index].Decompress(coldata.BoundingBoxMin, coldata.BoundingBoxMax); verts.Add(TransformVert(vert, bodyInfo)); } if (tri.Idx1 < chunk.VertexIndicesLength) { ushort index = (ushort)((uint)tri.Idx1 + chunk.SmallVerticesBase); indices.Add(verts.Count); var vert = coldata.SmallVertices.GetArrayData().Elements[index].Decompress(chunk.SmallVertexScale, chunk.SmallVertexOffset); verts.Add(TransformVert(vert, bodyInfo)); } else { ushort index = (ushort)(coldata.VertexIndices.GetArrayData().Elements[tri.Idx1 + chunk.VertexIndicesIndex - chunk.VertexIndicesLength].data); indices.Add(verts.Count); var vert = coldata.LargeVertices.GetArrayData().Elements[index].Decompress(coldata.BoundingBoxMin, coldata.BoundingBoxMax); verts.Add(TransformVert(vert, bodyInfo)); } if (tri.Idx2 < chunk.VertexIndicesLength) { ushort index = (ushort)((uint)tri.Idx2 + chunk.SmallVerticesBase); indices.Add(verts.Count); var vert = coldata.SmallVertices.GetArrayData().Elements[index].Decompress(chunk.SmallVertexScale, chunk.SmallVertexOffset); verts.Add(TransformVert(vert, bodyInfo)); } else { ushort index = (ushort)(coldata.VertexIndices.GetArrayData().Elements[tri.Idx2 + chunk.VertexIndicesIndex - chunk.VertexIndicesLength].data); indices.Add(verts.Count); var vert = coldata.LargeVertices.GetArrayData().Elements[index].Decompress(coldata.BoundingBoxMin, coldata.BoundingBoxMax); verts.Add(TransformVert(vert, bodyInfo)); } if (tri.Idx2 != tri.Idx3) { verts.Add(verts[verts.Count - 2]); verts.Add(verts[verts.Count - 1]); indices.Add(indices[indices.Count - 2]); indices.Add(indices[indices.Count - 1]); if (tri.Idx3 < chunk.VertexIndicesLength) { ushort index = (ushort)((uint)tri.Idx3 + chunk.SmallVerticesBase); indices.Add(verts.Count); var vert = coldata.SmallVertices.GetArrayData().Elements[index].Decompress(chunk.SmallVertexScale, chunk.SmallVertexOffset); verts.Add(TransformVert(vert, bodyInfo)); } else { ushort index = (ushort)(coldata.VertexIndices.GetArrayData().Elements[tri.Idx3 + chunk.VertexIndicesIndex - chunk.VertexIndicesLength].data); indices.Add(verts.Count); var vert = coldata.LargeVertices.GetArrayData().Elements[index].Decompress(coldata.BoundingBoxMin, coldata.BoundingBoxMax); verts.Add(TransformVert(vert, bodyInfo)); } } //} } } } } if (indices.Count == 0) { return; } var mesh = new Mesh(); mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; mesh.subMeshCount = 1; mesh.SetVertices(verts); mesh.SetTriangles(indices.ToArray(), 0, true); mesh.RecalculateNormals(); mesh.RecalculateBounds(); AssetDatabase.CreateAsset(mesh, assetName + ".mesh"); // Setup a game object asset GameObject obj = new GameObject(Path.GetFileNameWithoutExtension(assetName)); obj.AddComponent <MeshFilter>(); obj.AddComponent <MeshRenderer>(); obj.GetComponent <MeshFilter>().mesh = mesh; obj.GetComponent <MeshRenderer>().material = AssetDatabase.LoadAssetAtPath <Material>("Assets/dstools/Materials/CollisionMeshMaterial.mat"); PrefabUtility.SaveAsPrefabAsset(obj, assetName + ".prefab"); Object.DestroyImmediate(obj); }
public override void Write(HKX hkx, HKXSection section, BinaryWriterEx bw, uint sectionBaseOffset, HKXVariation variation) { throw new NotImplementedException(); }
public static void ImportNavimeshHKX(HKX hkx, string assetName) { NavMesh nm = ScriptableObject.CreateInstance <NavMesh>(); List <NavMesh.Edge> edges = new List <NavMesh.Edge>(); List <NavMesh.Face> faces = new List <NavMesh.Face>(); List <Vector3> vertices = new List <Vector3>(); List <NavMesh.CostGraphNode> cgnodes = new List <NavMesh.CostGraphNode>(); List <NavMesh.CostGraphEdge> cgedges = new List <NavMesh.CostGraphEdge>(); foreach (var cl in hkx.DataSection.Objects) { if (cl is HKX.HKAINavMesh) { var mesh = (HKX.HKAINavMesh)cl; foreach (var f in mesh.Faces.GetArrayData().Elements) { var face = new NavMesh.Face(); face.StartEdgeIndex = f.StartEdgeIndex; face.EdgeCount = f.NumEdges; faces.Add(face); } foreach (var e in mesh.Edges.GetArrayData().Elements) { var edge = new NavMesh.Edge(); edge.A = e.A; edge.B = e.B; edges.Add(edge); } foreach (var v in mesh.Vertices.GetArrayData().Elements) { vertices.Add(new Vector3(v.Vector.X, v.Vector.Y, v.Vector.Z)); } nm.Faces = faces.ToArray(); nm.Edges = edges.ToArray(); nm.Vertices = vertices.ToArray(); continue; } else if (cl is HKX.HKCDStaticAABBTreeStorage) { var tree = (HKX.HKCDStaticAABBTreeStorage)cl; var nodes = tree.CompressedTree.GetArrayData().Elements; var unpacked = new NavMesh.AABBTreeNode[nodes.Count]; for (uint i = 0; i < unpacked.Length; i++) { unpacked[i] = new NavMesh.AABBTreeNode(); unpacked[i].id = i; } unpacked[0].Min = new Vector3(tree.AABBMin.X, tree.AABBMin.Y, tree.AABBMin.Z); unpacked[0].Max = new Vector3(tree.AABBMax.X, tree.AABBMax.Y, tree.AABBMax.Z); // Propogate the decompression down the tree for (uint i = 0; i < unpacked.Length; i++) { var cnode = nodes[(int)i]; if ((cnode.IDX0 & 0x80) > 0) { uint left = i + 1; uint right = i + ((((uint)cnode.IDX0 & 0x7F) << 8) | (uint)cnode.IDX1) * 2; unpacked[(int)left].Min = nodes[(int)left].DecompressMin(unpacked[i].Min, unpacked[i].Max); unpacked[(int)left].Max = nodes[(int)left].DecompressMax(unpacked[i].Min, unpacked[i].Max); unpacked[(int)right].Min = nodes[(int)right].DecompressMin(unpacked[i].Min, unpacked[i].Max); unpacked[(int)right].Max = nodes[(int)right].DecompressMax(unpacked[i].Min, unpacked[i].Max); unpacked[i].Left = (int)left; unpacked[i].Right = (int)right; unpacked[i].IsTerminal = false; } else { unpacked[i].IsTerminal = true; unpacked[i].Left = -1; unpacked[i].Right = -1; unpacked[i].Index = (((uint)cnode.IDX0 & 0x7F) << 8) | (uint)cnode.IDX1; } } nm.AABBTree = unpacked; } else if (cl is HKX.HKAIDirectedGraphExplicitCost) { var graph = (HKX.HKAIDirectedGraphExplicitCost)cl; int i = 0; foreach (var n in graph.Nodes.GetArrayData().Elements) { var node = new NavMesh.CostGraphNode(); node.EdgeCount = n.NumEdges; node.StartEdgeIndex = n.StartEdgeIndex; var pos = graph.Positions.GetArrayData().Elements[i].Vector; node.Position = new Vector3(pos.X, pos.Y, pos.Z); cgnodes.Add(node); i++; } foreach (var e in graph.Edges.GetArrayData().Elements) { var edge = new NavMesh.CostGraphEdge(); edge.Flags = e.Flags; edge.TargetNode = e.TargetNode; var floatbytes = BitConverter.GetBytes(((uint)e.Cost) << 16); edge.Cost = BitConverter.ToSingle(floatbytes, 0); cgedges.Add(edge); } nm.CostGraphEdges = cgedges.ToArray(); nm.CostGraphNodes = cgnodes.ToArray(); } } AssetDatabase.CreateAsset(nm, assetName + ".asset"); }
public override void Read(HKX hkx, HKXSection section, BinaryReaderEx br, HKXVariation variation) { SectionOffset = (uint)br.Position; AssertPointer(hkx, br); if (variation == HKXVariation.HKXBloodBorne) { br.AssertInt32(0); } else { AssertPointer(hkx, br); } AnimationType = br.ReadEnum32 <AnimationType>(); Duration = br.ReadSingle(); TransformTrackCount = br.ReadInt32(); FloatTrackCount = br.ReadInt32(); if (variation == HKXVariation.HKXBloodBorne) { br.Pad(16); } if (variation == HKXVariation.HKXDS1) { br.ReadInt64s(2); // Annotations FrameCount = br.ReadInt32(); BlockCount = br.ReadInt32(); FramesPerBlock = br.ReadInt32(); MaskAndQuantization = br.ReadUInt32(); BlockDuration = br.ReadSingle(); InverseBlockDuration = br.ReadSingle(); FrameDuration = br.ReadSingle(); } else { br.ReadInt64s(3); // Annotations FrameCount = br.ReadInt32(); BlockCount = br.ReadInt32(); FramesPerBlock = br.ReadInt32(); MaskAndQuantization = br.ReadUInt32(); BlockDuration = br.ReadSingle(); InverseBlockDuration = br.ReadSingle(); FrameDuration = br.ReadSingle(); br.ReadUInt32(); // padding? } BlockOffsets = new HKArray <HKUInt>(hkx, section, this, br, variation); FloatBlockOffsets = new HKArray <HKUInt>(hkx, section, this, br, variation); TransformBlockOffsets = new HKArray <HKUInt>(hkx, section, this, br, variation); FloatOffsets = new HKArray <HKUInt>(hkx, section, this, br, variation); Data = new HKArray <HKByte>(hkx, section, this, br, variation); Endian = br.ReadInt32(); DataSize = (uint)br.Position - SectionOffset; ResolveDestinations(hkx, section); }
private byte[] ImportAnimToHKX() { var importedAnim = ImportAnim(); byte[] finalHkxDataToImport = null; var compressed2010Hkx = importedAnim.WriteToSplineCompressedHKX2010Bytes(ImportConfig.RotationQuantizationType, ImportConfig.RotationTolerance); if (GameDataManager.GameType == SoulsAssetPipeline.SoulsGames.DS1R) { finalHkxDataToImport = HavokDowngrade.UpgradeHkx2010to2015(compressed2010Hkx); } else if (GameDataManager.GameType == SoulsAssetPipeline.SoulsGames.DS1) { finalHkxDataToImport = compressed2010Hkx; } else if (GameDataManager.GameType == SoulsAssetPipeline.SoulsGames.DS3) { HKX.HKAAnimationBinding hk_binding = null; HKX.HKASplineCompressedAnimation hk_anim = null; HKX.HKASkeleton hk_skeleton = null; HKX.HKADefaultAnimatedReferenceFrame hk_refFrame = null; var hkx = HKX.Read(compressed2010Hkx); foreach (var o in hkx.DataSection.Objects) { if (o is HKX.HKASkeleton asSkeleton) { hk_skeleton = asSkeleton; } else if (o is HKX.HKAAnimationBinding asBinding) { hk_binding = asBinding; } else if (o is HKX.HKASplineCompressedAnimation asAnim) { hk_anim = asAnim; } else if (o is HKX.HKADefaultAnimatedReferenceFrame asRefFrame) { hk_refFrame = asRefFrame; } } var root = new HKX2.hkRootLevelContainer(); var animBinding = new HKX2.hkaAnimationBinding(); var anim = new HKX2.hkaSplineCompressedAnimation(); animBinding.m_animation = anim; animBinding.m_originalSkeletonName = hk_binding.OriginalSkeletonName; animBinding.m_transformTrackToBoneIndices = hk_binding.TransformTrackToBoneIndices.GetArrayData().Elements.Select(x => x.data).ToList(); animBinding.m_floatTrackToFloatSlotIndices = new List <short>(); animBinding.m_partitionIndices = new List <short>(); animBinding.m_blendHint = (HKX2.BlendHint)(int) hk_binding.BlendHint; anim.m_blockDuration = hk_anim.BlockDuration; anim.m_blockInverseDuration = hk_anim.InverseBlockDuration; anim.m_data = hk_anim.Data.GetArrayData().Elements.Select(x => x.data).ToList(); anim.m_duration = hk_anim.Duration; anim.m_endian = hk_anim.Endian; if (hk_refFrame != null) { var rootMotion = new HKX2.hkaDefaultAnimatedReferenceFrame(); rootMotion.m_duration = hk_refFrame.Duration; rootMotion.m_forward = hk_refFrame.Forward; rootMotion.m_referenceFrameSamples = new List <System.Numerics.Vector4>(); foreach (var rf in hk_refFrame.ReferenceFrameSamples.GetArrayData().Elements) { rootMotion.m_referenceFrameSamples.Add(rf.Vector); } rootMotion.m_up = hk_refFrame.Up; anim.m_extractedMotion = rootMotion; } anim.m_frameDuration = hk_anim.FrameDuration; anim.m_maskAndQuantizationSize = (int)hk_anim.MaskAndQuantization; anim.m_maxFramesPerBlock = hk_anim.FramesPerBlock; anim.m_numberOfFloatTracks = hk_anim.FloatTrackCount; anim.m_numberOfTransformTracks = hk_anim.TransformTrackCount; anim.m_numBlocks = hk_anim.BlockCount; anim.m_numFrames = hk_anim.FrameCount; anim.m_floatBlockOffsets = hk_anim.FloatBlockOffsets.GetArrayData().Elements.Select(b => b.data).ToList(); anim.m_type = HKX2.AnimationType.HK_SPLINE_COMPRESSED_ANIMATION; anim.m_blockOffsets = hk_anim.BlockOffsets.GetArrayData().Elements.Select(b => b.data).ToList(); anim.m_floatOffsets = new List <uint>(); anim.m_transformOffsets = new List <uint>(); //TODO: IMPLEMENT ANNOTATION TRACK READ IN LEGACY HKX TO TRANSFER IT TO HKX2 //anim.m_annotationTracks = new List<HKX2.hkaAnnotationTrack>(); //for (int i = 0; i < hk_anim.TransformTrackCount; i++) //{ // var boneIndex = animBinding.m_transformTrackToBoneIndices[i]; // string boneName = boneNames[boneIndex]; // anim.m_annotationTracks.Add(new HKX2.hkaAnnotationTrack() // { // m_trackName = boneName, // m_annotations = new List<HKX2.hkaAnnotationTrackAnnotation>(), // }); //} var animContainer = new HKX2.hkaAnimationContainer(); animContainer.m_attachments = new List <HKX2.hkaBoneAttachment>(); animContainer.m_skins = new List <HKX2.hkaMeshBinding>(); animContainer.m_skeletons = new List <HKX2.hkaSkeleton>(); animContainer.m_animations = new List <HKX2.hkaAnimation>(); animContainer.m_animations.Add(anim); animContainer.m_bindings = new List <HKX2.hkaAnimationBinding>(); animContainer.m_bindings.Add(animBinding); root.m_namedVariants = new List <HKX2.hkRootLevelContainerNamedVariant>(); root.m_namedVariants.Add(new HKX2.hkRootLevelContainerNamedVariant() { m_className = "hkaAnimationContainer", m_name = "Merged Animation Container", m_variant = animContainer }); using (MemoryStream s2 = new MemoryStream()) { BinaryWriterEx bw = new BinaryWriterEx(false, s2); var s = new HKX2.PackFileSerializer(); s.Serialize(root, bw); finalHkxDataToImport = s2.ToArray(); } } return(finalHkxDataToImport); }
public FlverSubmeshRenderer(Model parent, HKX colhkx, HKX.HKPStorageExtendedMeshShapeMeshSubpartStorage meshdata) { Parent = parent; var vertices = new VertexPositionColorNormalTangentTexture[(meshdata.Indices16.Size / 4) * 3]; //for (int i = 0; i < meshdata.Vertices.Size; i++) //{ // var vert = meshdata.Vertices.GetArrayData().Elements[i]; // vertices[i] = new VertexPositionColorNormalTangentTexture(); // vertices[i].Position = new Vector3(vert.Vector.X, vert.Vector.Y, vert.Vector.Z); //} MeshFacesets = new List <FlverSubmeshRendererFaceSet>(); List <ushort> indices = new List <ushort>(); int j = 0; for (var index = 0; index < meshdata.Indices16.Size / 4; index++) { var idx = meshdata.Indices16.GetArrayData().Elements; var vtxs = meshdata.Vertices.GetArrayData().Elements; var vert1 = vtxs[idx[index * 4].data].Vector; var vert2 = vtxs[idx[index * 4 + 1].data].Vector; var vert3 = vtxs[idx[index * 4 + 2].data].Vector; vertices[index * 3].Position = new Vector3(vert1.X, vert1.Y, vert1.Z); vertices[index * 3 + 1].Position = new Vector3(vert2.X, vert2.Y, vert2.Z); vertices[index * 3 + 2].Position = new Vector3(vert3.X, vert3.Y, vert3.Z); Vector3 a = new Vector3(vert2.X - vert1.X, vert2.Y - vert1.Y, vert2.Z - vert1.Z); Vector3 b = new Vector3(vert3.X - vert1.X, vert3.Y - vert1.Y, vert3.Z - vert1.Z); Vector3 normal = Vector3.Cross(a, b); normal.Normalize(); vertices[index * 3].Normal = normal; vertices[index * 3 + 1].Normal = normal; vertices[index * 3 + 2].Normal = normal; a.Normalize(); vertices[index * 3].Tangent = a; vertices[index * 3 + 1].Tangent = a; vertices[index * 3 + 2].Tangent = a; vertices[index * 3].Binormal = Vector3.Cross(normal, a); vertices[index * 3 + 1].Binormal = Vector3.Cross(normal, a); vertices[index * 3 + 2].Binormal = Vector3.Cross(normal, a); indices.Add((ushort)(index * 3)); indices.Add((ushort)(index * 3 + 1)); indices.Add((ushort)(index * 3 + 2)); } if (indices.Count > 0) { var newFaceSet = new FlverSubmeshRendererFaceSet() { BackfaceCulling = false, IsTriangleStrip = false, IndexBuffer = new IndexBuffer( GFX.Device, IndexElementSize.SixteenBits, indices.Count, BufferUsage.WriteOnly), IndexCount = indices.Count, }; newFaceSet.IndexBuffer.SetData(indices.Select(x => (ushort)x).ToArray()); MeshFacesets.Add(newFaceSet); } else { vertices = new VertexPositionColorNormalTangentTexture[meshdata.Vertices.Size]; for (int i = 0; i < meshdata.Vertices.Size; i++) { var vert = meshdata.Vertices.GetArrayData().Elements[i]; vertices[i] = new VertexPositionColorNormalTangentTexture(); vertices[i].Position = new Vector3(vert.Vector.X, vert.Vector.Y, vert.Vector.Z); } } Bounds = BoundingBox.CreateFromPoints(vertices.Select(x => x.Position)); VertBuffer = new VertexBuffer(GFX.Device, typeof(VertexPositionColorNormalTangentTexture), vertices.Length, BufferUsage.WriteOnly); VertBuffer.SetData(vertices); VertBufferBinding = new VertexBufferBinding(VertBuffer, 0, 0); }
// Used for collision rendering public FlverSubmeshRenderer(Model parent, HKX colhkx, HKX.FSNPCustomParamCompressedMeshShape meshdata) { Parent = parent; var coldata = meshdata.GetMeshShapeData(); var tree = coldata.getMeshBVH(); var box = new DbgPrimWireBox(Transform.Default, Vector3.One, Color.Cyan); if (tree != null) { //DebugBVHDraw(tree, box); } var vertices = new VertexPositionColorNormalTangentTexture[coldata.SmallVertices.Size + coldata.LargeVertices.Size]; /*for (int i = 0; i < coldata.SmallVertices.Size; i++) * { * var vert = coldata.SmallVertices.GetArrayData().Elements[i].Decompress(coldata.BoundingBoxMin, coldata.BoundingBoxMax); * vertices[i] = new VertexPositionColorNormalTangentTexture(); * vertices[i].Position = new Vector3(vert.X, vert.Y, vert.Z); * }*/ var largebase = coldata.SmallVertices.Size; for (int i = 0; i < coldata.LargeVertices.Size; i++) { var vert = coldata.LargeVertices.GetArrayData().Elements[i].Decompress(coldata.BoundingBoxMin, coldata.BoundingBoxMax); vertices[i + largebase] = new VertexPositionColorNormalTangentTexture(); vertices[i + largebase].Position = new Vector3(vert.X, vert.Y, vert.Z); } MeshFacesets = new List <FlverSubmeshRendererFaceSet>(); int ch = 0; foreach (var chunk in coldata.Chunks.GetArrayData().Elements) { /*if (ch != 1) * { * ch++; * continue; * } * ch++;*/ /*var tree2 = chunk.getChunkBVH(); * if (tree2 != null) * { * DebugBVHDraw(tree2, box); * }*/ List <ushort> indices = new List <ushort>(); for (int i = 0; i < chunk.ByteIndicesLength; i++) { var tri = coldata.MeshIndices.GetArrayData().Elements[i + chunk.ByteIndicesIndex]; if (tri.Idx2 == tri.Idx3 && tri.Idx1 != tri.Idx2) { if (tri.Idx0 < chunk.VertexIndicesLength) { ushort index = (ushort)((uint)tri.Idx0 + chunk.SmallVerticesBase); indices.Add(index); var vert = coldata.SmallVertices.GetArrayData().Elements[index].Decompress(chunk.SmallVertexScale, chunk.SmallVertexOffset); vertices[index] = new VertexPositionColorNormalTangentTexture(); vertices[index].Position = new Vector3(vert.X, vert.Y, vert.Z); } else { indices.Add((ushort)(coldata.VertexIndices.GetArrayData().Elements[tri.Idx0 + chunk.VertexIndicesIndex - chunk.VertexIndicesLength].data + largebase)); } if (tri.Idx1 < chunk.VertexIndicesLength) { ushort index = (ushort)((uint)tri.Idx1 + chunk.SmallVerticesBase); indices.Add(index); var vert = coldata.SmallVertices.GetArrayData().Elements[index].Decompress(chunk.SmallVertexScale, chunk.SmallVertexOffset); vertices[index] = new VertexPositionColorNormalTangentTexture(); vertices[index].Position = new Vector3(vert.X, vert.Y, vert.Z); } else { indices.Add((ushort)(coldata.VertexIndices.GetArrayData().Elements[tri.Idx1 + chunk.VertexIndicesIndex - chunk.VertexIndicesLength].data + largebase)); } if (tri.Idx2 < chunk.VertexIndicesLength) { ushort index = (ushort)((uint)tri.Idx2 + chunk.SmallVerticesBase); indices.Add(index); var vert = coldata.SmallVertices.GetArrayData().Elements[index].Decompress(chunk.SmallVertexScale, chunk.SmallVertexOffset); vertices[index] = new VertexPositionColorNormalTangentTexture(); vertices[index].Position = new Vector3(vert.X, vert.Y, vert.Z); } else { indices.Add((ushort)(coldata.VertexIndices.GetArrayData().Elements[tri.Idx2 + chunk.VertexIndicesIndex - chunk.VertexIndicesLength].data + largebase)); } } } if (indices.Count > 0) { var newFaceSet = new FlverSubmeshRendererFaceSet() { BackfaceCulling = false, IsTriangleStrip = false, IndexBuffer = new IndexBuffer( GFX.Device, IndexElementSize.SixteenBits, indices.Count, BufferUsage.WriteOnly), IndexCount = indices.Count, }; newFaceSet.IndexBuffer.SetData(indices.Select(x => (ushort)x).ToArray()); MeshFacesets.Add(newFaceSet); } } Bounds = BoundingBox.CreateFromPoints(vertices.Select(x => x.Position)); VertBuffer = new VertexBuffer(GFX.Device, typeof(VertexPositionColorNormalTangentTexture), vertices.Length, BufferUsage.WriteOnly); VertBuffer.SetData(vertices); VertBufferBinding = new VertexBufferBinding(VertBuffer, 0, 0); }
private static void LoadRemoHKX(byte[] hkxBytes, string animName) { Scene.DisableModelDrawing(); Scene.DisableModelDrawing2(); HKX.HKAAnimationBinding hk_binding = null; HKX.HKASplineCompressedAnimation hk_anim = null; HKX.HKASkeleton hk_skeleton = null; if (remoCutsLoaded.ContainsKey(animName)) { hk_binding = remoCutsLoaded[animName].hk_binding; hk_anim = remoCutsLoaded[animName].hk_anim; hk_skeleton = remoCutsLoaded[animName].hk_skeleton; } else { var hkx = HKX.Read(hkxBytes, HKX.HKXVariation.HKXDS1, false); foreach (var o in hkx.DataSection.Objects) { if (o is HKX.HKASkeleton asSkeleton) { hk_skeleton = asSkeleton; } else if (o is HKX.HKAAnimationBinding asBinding) { hk_binding = asBinding; } else if (o is HKX.HKASplineCompressedAnimation asAnim) { hk_anim = asAnim; } } remoCutsLoaded.Add(animName, new RemoCutCache() { hk_binding = hk_binding, hk_anim = hk_anim, hk_skeleton = hk_skeleton, }); } var animContainer = new NewAnimationContainer(); AnimContainer = animContainer; animContainer.ClearAnimations(); animContainer.Skeleton.LoadHKXSkeleton(hk_skeleton); var testIdleAnimThing = new NewHavokAnimation_SplineCompressed(animName, animContainer.Skeleton, null, hk_binding, hk_anim, animContainer); animContainer.AddNewAnimation(animName, testIdleAnimThing); animContainer.CurrentAnimationName = animName; var modelNames = animContainer.Skeleton.TopLevelHkxBoneIndices.Select(b => animContainer.Skeleton.HkxSkeleton[b].Name).ToList(); CurrentCutHits.Clear(); CurrentCutOtherBlocks.Clear(); lock (Scene._lock_ModelLoad_Draw) { Scene.Models.Clear(); } foreach (var name in modelNames) { Model mdl = null; if (!remoModelDict.ContainsKey(name)) { PauseStreamBGM(); if (name.StartsWith("c")) { string shortName = name.Substring(0, 5); mdl = GameDataManager.LoadCharacter(shortName); FmodManager.LoadInterrootFEV(shortName); if (mdl.IS_PLAYER) { ViewportInteractor.InitializeCharacterModel(mdl, isRemo: true); } } else if (name.StartsWith("o")) { string shortName = name.Substring(0, 5); mdl = GameDataManager.LoadObject(shortName); FmodManager.LoadInterrootFEV(shortName); } else if (name.StartsWith("m")) { mdl = GameDataManager.LoadMapPiece(AreaInt, BlockInt, 0, 0, int.Parse(name.Substring(1, 4))); } else if (name.StartsWith("A")) { int a = int.Parse(name.Substring(1, 2)); int b = int.Parse(name.Substring(4, 2)); if (b != BlockInt && !CurrentCutOtherBlocks.Contains(b)) { CurrentCutOtherBlocks.Add(b); } mdl = GameDataManager.LoadMapPiece(a, b, 0, 0, int.Parse(name.Substring(8, 4))); } else if (name.StartsWith("d")) { // TODO // Dummy entity e.g. 'd0000_0000'. Apparently just acts as a single DummyPoly? mdl = GameDataManager.LoadCharacter("c1000"); mdl.RemoDummyTransformPrim = new DebugPrimitives.DbgPrimWireArrow(name, new Transform(Microsoft.Xna.Framework.Matrix.CreateScale(0.25f) * mdl.CurrentTransform.WorldMatrix), Microsoft.Xna.Framework.Color.Lime) { Category = DebugPrimitives.DbgPrimCategory.AlwaysDraw }; mdl.RemoDummyTransformTextPrint = new StatusPrinter(null, Microsoft.Xna.Framework.Color.Lime); mdl.RemoDummyTransformTextPrint.AppendLine(name); mdl.IS_REMO_DUMMY = true; } else if (name.StartsWith("h")) { // Collision. CurrentCutHits.Add(name); } else { throw new NotImplementedException($"Cannot tell what object type '{name}' is in remo HKX"); } if (mdl != null) { mdl.Name = name; remoModelDict.Add(name, mdl); } } else { mdl = remoModelDict[name]; } if (mdl != null) { mdl.AnimContainer = animContainer; mdl.IsRemoModel = true; mdl.Name = name; mdl.SkeletonFlver.RevertToReferencePose(); mdl.SkeletonFlver.MapToSkeleton(animContainer.Skeleton, isRemo: true); mdl.UpdateSkeleton(); lock (Scene._lock_ModelLoad_Draw) { Scene.Models.Add(mdl); } } } var msbName = GameDataManager.GetInterrootPath($@"map\MapStudio\m{AreaInt:D2}_{BlockInt:D2}_00_00.msb"); var msb = MSB1.Read(msbName); Vector3 mapOffset = msb.Events.MapOffsets.FirstOrDefault()?.Position.ToXna() ?? Vector3.Zero; uint dg1 = 0, dg2 = 0, dg3 = 0, dg4 = 0; foreach (var hitName in CurrentCutHits) { var hit = msb.Parts.Collisions.FirstOrDefault(h => h.Name == hitName); dg1 |= hit.DrawGroups[0]; dg2 |= hit.DrawGroups[1]; dg3 |= hit.DrawGroups[2]; dg4 |= hit.DrawGroups[3]; } bool IsThingVisible(uint[] drawGroups) { return(((drawGroups[0] & dg1) == dg1) && ((drawGroups[1] & dg2) == dg2) && ((drawGroups[2] & dg3) == dg3) && ((drawGroups[3] & dg4) == dg4)); } foreach (var mapPiece in msb.Parts.MapPieces) { var thisEntityName = CurrentCutOtherBlocks.Count > 0 ? $"A{AreaInt:D2}B{BlockInt:D2}_{mapPiece.Name}" : mapPiece.Name; if (IsThingVisible(mapPiece.DrawGroups)) { Model mdl = null; if (remoModelDict.ContainsKey(thisEntityName)) { mdl = remoModelDict[thisEntityName]; mdl.AnimContainer = animContainer; mdl.IsRemoModel = true; mdl.SkeletonFlver.RevertToReferencePose(); mdl.SkeletonFlver.MapToSkeleton(animContainer.Skeleton, isRemo: true); mdl.UpdateSkeleton(); lock (Scene._lock_ModelLoad_Draw) { Scene.Models.Add(mdl); } continue; } mdl = GameDataManager.LoadMapPiece(AreaInt, BlockInt, 0, 0, int.Parse(mapPiece.ModelName.Substring(1, 4))); mdl.AnimContainer = animContainer; mdl.IsRemoModel = true; mdl.Name = thisEntityName; mdl.SkeletonFlver.RevertToReferencePose(); mdl.StartTransform.Position = mapPiece.Position.ToXna() - mapOffset; mdl.StartTransform.Rotation = Utils.EulerToQuaternion((mapPiece.Rotation * (SapMath.Pi / 180f)).ToXna()); mdl.StartTransform.Scale = mapPiece.Scale.ToXna(); mdl.CurrentTransform = mdl.StartTransform; mdl.IS_REMO_NOTSKINNED = true; mdl.SkeletonFlver.MapToSkeleton(animContainer.Skeleton, isRemo: true); mdl.UpdateSkeleton(); lock (Scene._lock_ModelLoad_Draw) { Scene.Models.Add(mdl); } remoModelDict.Add(thisEntityName, mdl); } } foreach (var mapPiece in msb.Parts.Objects) { var thisEntityName = CurrentCutOtherBlocks.Count > 0 ? $"A{AreaInt:D2}B{BlockInt:D2}_{mapPiece.Name}" : mapPiece.Name; if (IsThingVisible(mapPiece.DrawGroups)) { Model mdl = null; if (remoModelDict.ContainsKey(thisEntityName)) { mdl = remoModelDict[thisEntityName]; mdl.AnimContainer = animContainer; mdl.IsRemoModel = true; mdl.SkeletonFlver.RevertToReferencePose(); mdl.SkeletonFlver.MapToSkeleton(animContainer.Skeleton, isRemo: true); mdl.UpdateSkeleton(); lock (Scene._lock_ModelLoad_Draw) { Scene.Models.Add(mdl); } continue; } mdl = GameDataManager.LoadObject(mapPiece.ModelName); mdl.AnimContainer = animContainer; mdl.IsRemoModel = true; mdl.Name = thisEntityName; mdl.StartTransform.Position = mapPiece.Position.ToXna() - mapOffset; mdl.StartTransform.Rotation = Utils.EulerToQuaternion((mapPiece.Rotation * (SapMath.Pi / 180f)).ToXna()); mdl.StartTransform.Scale = mapPiece.Scale.ToXna(); mdl.CurrentTransform = mdl.StartTransform; mdl.IS_REMO_NOTSKINNED = true; mdl.SkeletonFlver.RevertToReferencePose(); mdl.SkeletonFlver.MapToSkeleton(animContainer.Skeleton, isRemo: true); mdl.UpdateSkeleton(); lock (Scene._lock_ModelLoad_Draw) { Scene.Models.Add(mdl); } remoModelDict.Add(thisEntityName, mdl); } } lock (Scene._lock_ModelLoad_Draw) { Scene.Models = Scene.Models.OrderBy(m => m.IS_PLAYER ? 0 : 1).ToList(); } CurrentCut = animName; animContainer.ScrubRelative(0); List <Model> mdls = null; lock (Scene._lock_ModelLoad_Draw) { mdls = Scene.Models.ToList(); } foreach (var m in mdls) { m.UpdateSkeleton(); } GFX.World.Update(0); Scene.EnableModelDrawing(); Scene.EnableModelDrawing2(); ResumeStreamedBGM(); ViewportInteractor.Graph.MainScreen.REMO_HOTFIX_REQUEST_PLAY_RESUME_NEXT_FRAME = true; ViewportInteractor.Graph.MainScreen.HardReset(); }