public Boolean ProcessAssetContextRecursive(H1SkeletalContext.JointNode node, ref H1ReferenceSkeleton refSkeleton, ref Dictionary <String, H1SkeletalContext.JointNode> mapBoneNameToBoneNode) { if (node == null) // successfully reach to the leaf nodes { return(true); } if (!ProcessRefSkeletonFromAssetContext(node, ref refSkeleton)) { return(false); } // add pair for BoneNameToBoneNode mapBoneNameToBoneNode.Add(node.JointName, node); foreach (H1SkeletalContext.JointNode child in node.Children) { if (!ProcessAssetContextRecursive(child, ref refSkeleton, ref mapBoneNameToBoneNode)) { return(false); } } return(true); }
// @TODO - enable these algorithms correctly in the near future (may be not~) // I found extracting skeletal mesh data with bone tree can be problem when there are multiple root bones (like head, pelvis ... etc) // so I disable these methods and change whole algorithms for extracting skeletal mesh data #region Disabled Extracting skeletal mesh data methods private Boolean ExtractSkeletalMeshDataRecursive(Node node, List <Bone> bones, ref H1SkeletalContext.JointNode outNode) { if (node == null) // reach to the leaf node { return(true); } // create joint data ans set it //outNode.JointData = new H1SkeletalContext.Joint(); Bone bone = bones.Find(x => (x.Name == node.Name)); if (bone == null) // it is leaf node { return(false); } //ExtractBone(bone, ref outNode.JointData); // create children foreach (Node child in node.Children) { H1SkeletalContext.JointNode childNode = new H1SkeletalContext.JointNode(); ExtractSkeletalMeshDataRecursive(child, bones, ref childNode); // set parent childNode.Parent = outNode; outNode.Children.Add(childNode); } return(true); }
public Boolean ExtractSkeletalMeshData(Node rootBoneNode, List <Bone> bones, ref H1SkeletalContext newContext) { H1SkeletalContext.JointNode rootNode = new H1SkeletalContext.JointNode(); ExtractSkeletalMeshDataRecursive(rootBoneNode, bones, ref rootNode); newContext.Root = rootNode; return(true); }
public Boolean ProcessRefSkeletonFromAssetContext(H1SkeletalContext.JointNode node, ref H1ReferenceSkeleton refSkeleton) { H1SkeletalContext.Joint jointData = null; // node.JointData; // add mesh bone info H1MeshBoneInfo meshBoneInfo = new H1MeshBoneInfo(); //meshBoneInfo.Name = jointData.JointName; if (refSkeleton.NameToIndexMapIsEmpty()) { // should be root node, so parent index is '0' meshBoneInfo.ParentIndex = 0; } else if (node.Parent == null) { // should be sub-root node, so parent index is '0' (root motion node) meshBoneInfo.ParentIndex = refSkeleton.GetIndexByBoneName("RootMotionNode"); } else //meshBoneInfo.ParentIndex = refSkeleton.GetIndexByBoneName(node.Parent.JointData.JointName); if (meshBoneInfo.ParentIndex == -1) { return(false); } // the new index should be same with RefBoneBases Int32 newBoneInfoIndex = refSkeleton.RefBoneInfoList.Count; Int32 newBoneBaseIndex = refSkeleton.RefBoneBases.Count; if (newBoneBaseIndex != newBoneInfoIndex) { return(false); } refSkeleton.RefBoneInfoList.Add(meshBoneInfo); // add bone base H1Transform boneBase = new H1Transform(); boneBase.Translation = jointData.OffsetTransformation.Translation; boneBase.Scaling = jointData.OffsetTransformation.Scaling; boneBase.Rotation = jointData.OffsetTransformation.Rotation; refSkeleton.RefBoneBases.Add(boneBase); // add NameToIndex refSkeleton.AddNameToIndexPair(meshBoneInfo.Name, newBoneInfoIndex); return(true); }
private void ExtractNodeSpace(Node node, ref H1SkeletalContext.JointNode jointNode) { jointNode.JointName = node.Name; Vector3D translation; Vector3D scaling; Assimp.Quaternion quaternion; node.Transform.Decompose(out scaling, out quaternion, out translation); jointNode.NodeLocalTransform.Translation = new Vector3(translation.X, translation.Y, translation.Z); jointNode.NodeLocalTransform.Scaling = new Vector3(scaling.X, scaling.Y, scaling.Z); jointNode.NodeLocalTransform.Rotation = new SharpDX.Quaternion(quaternion.X, quaternion.Y, quaternion.Z, quaternion.W); }
public Boolean ProcessAssetContext(H1StaticLODModel staticLODModel, H1MeshContext[] meshContexts, H1SkeletalContext skeletalContext) { if (m_Skeleton == null) { return(false); } // 1. process skeletalContext (H1SkeletalContext) H1SkeletalContext.JointNode rootNode = skeletalContext.Root; H1ReferenceSkeleton refSkeleton = m_Skeleton.RefSkeleton; #region Disabled // process recursively from root node //Dictionary<String, H1SkeletalContext.JointNode> mapNodeNameToJointNode = new Dictionary<string, H1SkeletalContext.JointNode>(); //if (!ProcessAssetContextRecursive(rootNode, ref refSkeleton, ref mapNodeNameToJointNode)) // return false; // failed to process nodes from root nodes #endregion // 2. process meshContexts (H1MeshContext) // collect all vertices for all different mesh contexts Int32[] meshContextOffsetsList = new Int32[meshContexts.Count()]; List <Vector3> positionsList = new List <Vector3>(); List <Vector3> tangentXList = new List <Vector3>(); List <Vector3> tangentYList = new List <Vector3>(); List <Vector3> tangentZList = new List <Vector3>(); List <Vector2> texcoordsList = new List <Vector2>(); Int32 currMeshContextIndex = 0; foreach (H1MeshContext meshContext in meshContexts) { meshContextOffsetsList[currMeshContextIndex] = positionsList.Count; positionsList.AddRange(meshContext.Positions); tangentXList.AddRange(meshContext.Tangents); tangentYList.AddRange(meshContext.Normals); tangentZList.AddRange(meshContext.BiTangents); texcoordsList.AddRange(meshContext.UVBuffers[0].Buffer); currMeshContextIndex++; } Vector3[] positions = positionsList.ToArray(); Vector3[] tangentX = tangentXList.ToArray(); Vector3[] tangentY = tangentYList.ToArray(); Vector3[] tangentZ = tangentZList.ToArray(); Vector2[] texcoords = texcoordsList.ToArray(); Int32 numPositions = positions.Count(); if (numPositions != texcoords.Count()) { return(false); } // create the list of vertex info List <VertexInfo> vertexInfoList = new List <VertexInfo>(); #if DEBUG List <Boolean> taggedVertexInfoList = new List <Boolean>(); #endif for (Int32 i = 0; i < numPositions; ++i) { vertexInfoList.Add(new VertexInfo()); vertexInfoList[i].VertexIndex = i; #if DEBUG taggedVertexInfoList.Add(false); #endif } // set the triangle index // create collection of index for all mesh contexts List <UInt32> indexList = new List <UInt32>(); Int32 meshContextIndex = 0; foreach (H1MeshContext meshContext in meshContexts) { // using meshContextOffsetsList information, remap indices correctly foreach (UInt32 vertexIndex in meshContext.Indices) { indexList.Add(Convert.ToUInt32(meshContextOffsetsList[meshContextIndex]) + vertexIndex); } meshContextIndex++; } UInt32[] indices = indexList.ToArray(); #if DEBUG Boolean[] taggedIndices = new Boolean[indexList.Count]; #endif // looping index, set triangle index for each vertexInfoList Int32 indexOfIndex = 0; foreach (UInt32 index in indices) { Int32 vertexIndex = Convert.ToInt32(index); Int32 triangleIndex = indexOfIndex / 3; // set the triangle index vertexInfoList[vertexIndex].TriangleIndices.Add(triangleIndex); #if DEBUG taggedIndices[indexOfIndex] = false; #endif indexOfIndex++; } // process vertex weights and bone index in advance by looping all H1MeshBoneInfo Int32 meshBoneInfoIndex = 0; foreach (H1MeshBoneInfo meshBoneInfo in refSkeleton.RefBoneInfoList) { Int32 jointNodeIndex = skeletalContext.JointNodes.FindIndex(x => x.JointName == meshBoneInfo.Name); H1SkeletalContext.JointNode node = skeletalContext.JointNodes[jointNodeIndex]; foreach (H1SkeletalContext.Joint nodeData in node.JointDataList) { // looping weighted vertices Int32 vertexIndexOffset = nodeData.MeshContextIndex == -1 ? 0 : meshContextOffsetsList[nodeData.MeshContextIndex]; // process vertex weights and bone index foreach (H1SkeletalContext.WeightedVertex weightedVertex in nodeData.WeightedVertices) { VertexInfo.InfluenceBoneData influencedBoneData = new VertexInfo.InfluenceBoneData(); influencedBoneData.BoneIndex = meshBoneInfoIndex; influencedBoneData.BoneWeight = weightedVertex.Weight; vertexInfoList[vertexIndexOffset + weightedVertex.VertexIndex].InfluencedBones.Add(influencedBoneData); } } meshBoneInfoIndex++; } #if DEBUG // verification code for vertex weights List <float> taggedVertexWeights = new List <float>(); foreach (VertexInfo vertexInfo in vertexInfoList) { float totalWeight = 0.0f; foreach (VertexInfo.InfluenceBoneData influencedBoneData in vertexInfo.InfluencedBones) { totalWeight += influencedBoneData.BoneWeight; } taggedVertexWeights.Add(totalWeight); } List <Int32> invalidVertexIndices = new List <Int32>(); Int32 currVertexIndex = 0; foreach (float totalWeight in taggedVertexWeights) { if (totalWeight < 0.99f) { invalidVertexIndices.Add(currVertexIndex); } currVertexIndex++; } if (invalidVertexIndices.Count > 0) { return(false); } #endif // looping meshBoneInfo, set bone index and bone weight foreach (H1MeshBoneInfo meshBoneInfo in refSkeleton.RefBoneInfoList) { Int32 jointNodeIndex = skeletalContext.JointNodes.FindIndex(x => x.JointName == meshBoneInfo.Name); H1SkeletalContext.JointNode node = skeletalContext.JointNodes[jointNodeIndex]; List <UInt32> ChunkVertexIndices = new List <UInt32>(); // vertex indices for this chunk (bone node) foreach (H1SkeletalContext.Joint nodeData in node.JointDataList) { // looping weighted vertices Int32 vertexIndexOffset = nodeData.MeshContextIndex == -1 ? 0 : meshContextOffsetsList[nodeData.MeshContextIndex]; // add chunk vertex indices foreach (H1SkeletalContext.WeightedVertex weightedVertex in nodeData.WeightedVertices) { ChunkVertexIndices.Add(Convert.ToUInt32(vertexIndexOffset + weightedVertex.VertexIndex)); } } // exceptional handling for MeshBoneInfo which doesn't contain any mesh data, no need to generate chunk for this BasePose if (ChunkVertexIndices.Count == 0) { continue; } // process skeletal mesh chunk H1SkelMeshChunk skelMeshChunk = new H1SkelMeshChunk(); // calculate triangles for this mesh chunk List <UInt32> triangles = new List <UInt32>(); foreach (Int32 vertexIndex in ChunkVertexIndices) { VertexInfo vertexInfo = vertexInfoList[vertexIndex]; H1SoftSkinVertex newSoftSkinVertex = new H1SoftSkinVertex(); newSoftSkinVertex.Position = positions[vertexInfo.VertexIndex]; newSoftSkinVertex.TangentX = tangentX[vertexInfo.VertexIndex]; newSoftSkinVertex.TangentY = tangentY[vertexInfo.VertexIndex]; newSoftSkinVertex.TangentZ = tangentZ[vertexInfo.VertexIndex]; // @TODO - in the future, it could increase texcoords (ex. light map texcoord) newSoftSkinVertex.UVs[0] = texcoords[vertexInfo.VertexIndex]; for (Int32 i = 0; i < H1ObjectGlobalDefinitions.MAX_TOTAL_INFLUENCES; ++i) { if (vertexInfo.InfluencedBones.Count <= i) { // insert the default value newSoftSkinVertex.InfluenceWeights[i] = 0; newSoftSkinVertex.InfluenceBones[i] = 0; continue; } VertexInfo.InfluenceBoneData boneData = vertexInfo.InfluencedBones[i]; newSoftSkinVertex.InfluenceWeights[i] = Convert.ToByte(boneData.BoneWeight * 255.0f); // @TODO - for this bone index, we can replace with 'm_BoneMap' newSoftSkinVertex.InfluenceBones[i] = Convert.ToByte(boneData.BoneIndex); } skelMeshChunk.SoftVertices.Add(newSoftSkinVertex); // process triangle indices foreach (Int32 triangleIndex in vertexInfo.TriangleIndices) { UInt32 triangleIndexUInt32 = Convert.ToUInt32(triangleIndex); if (!triangles.Exists(x => { return(x == triangleIndexUInt32); })) { triangles.Add(triangleIndexUInt32); } } } // build vertex buffers List <Vector4> chunkPositions = new List <Vector4>(); List <Vector4> chunkTangentZs = new List <Vector4>(); List <Vector4> chunkTangentXs = new List <Vector4>(); List <Vector2> chunkTexcoords = new List <Vector2>(); List <Int4> chunkBoneIndices = new List <Int4>(); List <Vector4> chunkBoneWeights = new List <Vector4>(); List <Vector4> chunkColors = new List <Vector4>(); Dictionary <Int32, Int32> vertexToChunkVertexMap = new Dictionary <Int32, Int32>(); foreach (Int32 triangleIndex in triangles) { for (Int32 faceIndex = 0; faceIndex < 3; ++faceIndex) { Int32 vertexIndex = Convert.ToInt32(indices[triangleIndex * 3 + faceIndex]); #if DEBUG taggedVertexInfoList[vertexIndex] = true; #endif // determine if the same vertex exists based on chunkPositions //if (chunkPositions.Contains(new Vector4(positions[vertexIndex], 1.0f))) if (vertexToChunkVertexMap.ContainsKey(vertexIndex)) { continue; } // position index is base-index Int32 newIndex = chunkPositions.Count; vertexToChunkVertexMap.Add(vertexIndex, newIndex); chunkPositions.Add(new Vector4(positions[vertexIndex], 1.0f)); chunkTangentZs.Add(new Vector4(tangentZ[vertexIndex], 1.0f)); chunkTangentXs.Add(new Vector4(tangentX[vertexIndex], 1.0f)); chunkTexcoords.Add(new Vector2(texcoords[vertexIndex].X, texcoords[vertexIndex].Y)); VertexInfo vertexInfo = vertexInfoList[vertexIndex]; Int4 influencedBones = new Int4(0); Vector4 influencedWeights = new Vector4(0.0f); Int32 insertedIndex = 0; foreach (VertexInfo.InfluenceBoneData boneData in vertexInfo.InfluencedBones) { influencedBones[insertedIndex] = boneData.BoneIndex; influencedWeights[insertedIndex] = boneData.BoneWeight; insertedIndex++; } chunkBoneIndices.Add(influencedBones); chunkBoneWeights.Add(influencedWeights); //@TODO - temporary set it as 'RED' chunkColors.Add(new Vector4(1.0f, 0.0f, 0.0f, 1.0f)); } } // create skeletal mesh vertex data // 1. position H1SkeletalMeshVertexData <Vector4> positionVertexData = new H1SkeletalMeshVertexData <Vector4>(); positionVertexData.SetVertexData(chunkPositions.ToArray(), H1SkeletalMeshVertexDataInterface.VertexDataType.Position, false); // 2. tangentZ H1SkeletalMeshVertexData <Vector4> tangentZVertexData = new H1SkeletalMeshVertexData <Vector4>(); tangentZVertexData.SetVertexData(chunkTangentZs.ToArray(), H1SkeletalMeshVertexDataInterface.VertexDataType.TangentZ, false); // 3. tangentX H1SkeletalMeshVertexData <Vector4> tangentXVertexData = new H1SkeletalMeshVertexData <Vector4>(); tangentXVertexData.SetVertexData(chunkTangentXs.ToArray(), H1SkeletalMeshVertexDataInterface.VertexDataType.TangentX, false); // 4. bone indices H1SkeletalMeshVertexData <Int4> boneIndicesVertexData = new H1SkeletalMeshVertexData <Int4>(); boneIndicesVertexData.SetVertexData(chunkBoneIndices.ToArray(), H1SkeletalMeshVertexDataInterface.VertexDataType.InfluencedBones, false); // 5. bone weights H1SkeletalMeshVertexData <Vector4> boneWeightsVertexData = new H1SkeletalMeshVertexData <Vector4>(); boneWeightsVertexData.SetVertexData(chunkBoneWeights.ToArray(), H1SkeletalMeshVertexDataInterface.VertexDataType.InfluencedWeights, false); // 6. texcoord H1SkeletalMeshVertexData <Vector2> texcoordVertexData = new H1SkeletalMeshVertexData <Vector2>(); texcoordVertexData.SetVertexData(chunkTexcoords.ToArray(), H1SkeletalMeshVertexDataInterface.VertexDataType.Texcoord, false); // 7. colors H1SkeletalMeshVertexData <Vector4> colorVertexData = new H1SkeletalMeshVertexData <Vector4>(); colorVertexData.SetVertexData(chunkColors.ToArray(), H1SkeletalMeshVertexDataInterface.VertexDataType.Color); Int32 newSkelMeshChunkIndex = staticLODModel.VertexBufferGPUSkin.SkeletalMeshVertexBuffers.Count; staticLODModel.VertexBufferGPUSkin.SkeletalMeshVertexBuffers.Add(new H1SkeletalMeshVertexBuffers(newSkelMeshChunkIndex)); staticLODModel.VertexBufferGPUSkin.SkeletalMeshVertexBuffers[newSkelMeshChunkIndex].AddSkeletalMeshVertexData(positionVertexData); staticLODModel.VertexBufferGPUSkin.SkeletalMeshVertexBuffers[newSkelMeshChunkIndex].AddSkeletalMeshVertexData(tangentZVertexData); staticLODModel.VertexBufferGPUSkin.SkeletalMeshVertexBuffers[newSkelMeshChunkIndex].AddSkeletalMeshVertexData(tangentXVertexData); staticLODModel.VertexBufferGPUSkin.SkeletalMeshVertexBuffers[newSkelMeshChunkIndex].AddSkeletalMeshVertexData(boneIndicesVertexData); staticLODModel.VertexBufferGPUSkin.SkeletalMeshVertexBuffers[newSkelMeshChunkIndex].AddSkeletalMeshVertexData(boneWeightsVertexData); staticLODModel.VertexBufferGPUSkin.SkeletalMeshVertexBuffers[newSkelMeshChunkIndex].AddSkeletalMeshVertexData(texcoordVertexData); staticLODModel.VertexBufferGPUSkin.SkeletalMeshVertexBuffers[newSkelMeshChunkIndex].AddSkeletalMeshVertexData(colorVertexData); // build index buffer List <UInt32> chunkIndices = new List <UInt32>(); foreach (Int32 triangleIndex in triangles) { for (Int32 faceIndex = 0; faceIndex < 3; ++faceIndex) { #if DEBUG taggedIndices[triangleIndex * 3 + faceIndex] = true; #endif Int32 vertexIndex = Convert.ToInt32(indices[triangleIndex * 3 + faceIndex]); chunkIndices.Add(Convert.ToUInt32(vertexToChunkVertexMap[vertexIndex])); } } staticLODModel.MultiSizeIndexContainer.Indices.AddRange(chunkIndices.ToArray()); // add new chunk Int32 newChunkIndex = staticLODModel.Chunks.Count; staticLODModel.Chunks.Add(skelMeshChunk); // process skeletal mesh section H1SkelMeshSection skelMeshSection = new H1SkelMeshSection(); skelMeshSection.ChunkIndex = Convert.ToUInt16(newChunkIndex); // @TODO - fix this num triangle inconsistency //skelMeshSection.NumTriangles = Convert.ToUInt16(triangles.Count); skelMeshSection.NumTriangles = Convert.ToUInt16(chunkIndices.Count / 3); //@TODO - add material index // add skeletal sections staticLODModel.Sections.Add(skelMeshSection); } #if DEBUG // debugging code for detecting missing vertex to draw meshes List <Int32> notTaggedVertexIndex = new List <Int32>(); Int32 currTaggedVertexIndex = 0; foreach (Boolean isTaggedVertex in taggedVertexInfoList) { if (isTaggedVertex == false) { notTaggedVertexIndex.Add(currTaggedVertexIndex); } currTaggedVertexIndex++; } if (notTaggedVertexIndex.Count > 0) { return(false); } // debugging code for detecting missing index to draw meshes List <Int32> notTaggedIndex = new List <Int32>(); Int32 currIndexOfIndex = 0; foreach (Boolean isTaggedIndex in taggedIndices) { if (isTaggedIndex == false) { notTaggedIndex.Add(currIndexOfIndex); } currIndexOfIndex++; } if (notTaggedIndex.Count > 0) { return(false); } #endif return(true); }
public bool Load(String fileName) { // @TODO - change the file path for asset folder in the future currently, set as executable path String path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Assets\\"); m_FileName = path + fileName; // setting assimp context AssimpContext importer = new AssimpContext(); importer.SetConfig(new NormalSmoothingAngleConfig(66.0f)); // loading scene Scene scene = null; try { scene = importer.ImportFile(m_FileName, PostProcessPreset.TargetRealTimeMaximumQuality | PostProcessSteps.Triangulate | PostProcessSteps.FlipUVs | PostProcessSteps.FlipWindingOrder); } catch (Exception e) // handling assimp exception { if (e.Source != null) { Console.WriteLine("Error: {0}", e.Message); } } asset = new H1AssetContext(); H1ModelContext convertedModel = asset.AddModel(); // extract the meshes if (scene.HasMeshes) { Int32 meshCount = scene.Meshes.Count; List <H1MeshContext> meshContexts = new List <H1MeshContext>(); ExtractMeshData(scene, ref meshContexts); foreach (H1MeshContext meshContext in meshContexts) { convertedModel.Meshes.Add(meshContext); } } // extract skeletal data // what I learned from this failed algorithm // 1. root bone nodes can be multiple // 2. for example, blabla_Hub(containing null meshes, only space infos) have null information for weighted vertices, but it should not be considered as leaf node! #region Disabled Extracting skeletal mesh data methods /*if (scene.HasMeshes) * { * // collect all bones from meshes * List<Bone> bones = new List<Bone>(); * foreach (Mesh mesh in scene.Meshes) * { * if (mesh.HasBones) * { * bones.AddRange(mesh.Bones); * } * } * * // find root node for all meshes * Node boneRootNode = FindRootBone(bones, scene.RootNode); * * H1SkeletalContext skeletalContext = null; * skeletalContext = new H1SkeletalContext(); * //@TODO - need to optimize for deep copies * ExtractSkeletalMeshData(boneRootNode, bones, ref skeletalContext); * * convertedModel.SkeletalContexts.Add(skeletalContext); * }*/ #endregion // what I learned from this failed algorithm // 1. the counts of bones and bone nodes (joint nodes) can be different // 2. bone node can be used for just transforming spaces // 3. bone node could have empty meshes (just includes space information) // 4. bones are subsidiary for bone nodes // 5. don't consider aiBone as bone node in hierarchy rather regards node(aiNode) as bone node in hierarchy // 6. consider aiBone as node bone data containing all necessary data #region Failed Algorithm // new algorithms to handle multiple root node (like pelvis and head) more efficient way /*if (scene.HasMeshes) * { * // 1. extracted all bones for all meshes in the scene * // NOTICE - multiple meshes in file are considered in same body but chunked several parts * Dictionary<String, Bone> extractedBones = new Dictionary<String, Bone>(); * Dictionary<String, H1SkeletalContext.JointNode> jointNodes = new Dictionary<string, H1SkeletalContext.JointNode>(); * foreach (Mesh mesh in scene.Meshes) * { * if (mesh.HasBones) * { * foreach (Bone bone in mesh.Bones) * { * String boneName = bone.Name; * if (!extractedBones.ContainsKey(boneName)) * { * extractedBones.Add(boneName, bone); * * H1SkeletalContext.JointNode newJointNode = new H1SkeletalContext.JointNode(); * newJointNode.JointData.JointName = boneName; * jointNodes.Add(boneName, newJointNode); * } * } * } * } * * // 2. process extracted bones * // @TODO - naive algorithm need to optimize by searching tree and set the bones parent * foreach (KeyValuePair<String, Bone> extractedBone in extractedBones) * { * Node node = scene.RootNode.FindNode(extractedBone.Key); * H1SkeletalContext.JointNode jointNode = jointNodes[node.Name]; * H1SkeletalContext.JointNode parentJointNode = jointNodes[node.Parent.Name]; * * // set proper properties * jointNode.Parent = parentJointNode; * foreach (Node child in node.Children) * { * H1SkeletalContext.JointNode childJointNode = null; * if (jointNodes.TryGetValue(child.Name, out childJointNode)) * { * jointNode.Children.Add(childJointNode); * } * } * ExtractBone(extractedBone.Value, ref jointNode.JointData); * } * * // 3. find root node(s) and set them at the front * }*/ #endregion if (scene.HasMeshes) { H1SkeletalContext skeletalContext = null; skeletalContext = new H1SkeletalContext(); // prepare data for BFS search List <H1SkeletalContext.JointNode> jointNodes = skeletalContext.JointNodes; Dictionary <String, Int32> jointNameToJointNodeIndex = new Dictionary <String, Int32>(); // extractedBones could have same bone name and multiple bone data Dictionary <String, List <Bone> > extractedBones = new Dictionary <String, List <Bone> >(); // boneToMeshContexIndex should be mirrored same as extractedBones (index of list should be same) Dictionary <String, List <Int32> > boneToMeshContextIndex = new Dictionary <String, List <Int32> >(); #region Debug Validation Weight Vertex Counts #if DEBUG List <Boolean> taggedVertexIndex = new List <Boolean>(); // verification to tag all needed vertices List <float> taggedVertexWeights = new List <float>(); // verification to tag all vertex weight for sum of each vertex List <Int32> taggedVertexIndexOffset = new List <Int32>(); // pre-test for validation foreach (Mesh mesh in scene.Meshes) { if (mesh.HasVertices) { foreach (Vector3D vertex in mesh.Vertices) { taggedVertexIndex.Add(false); } } if (mesh.HasBones) { foreach (Bone bone in mesh.Bones) { if (bone.HasVertexWeights) { foreach (VertexWeight vertexWeight in bone.VertexWeights) { taggedVertexIndex[vertexWeight.VertexID] = true; } } } } List <Int32> notTaggedVertexIndex0 = new List <Int32>(); Int32 currVertexIndex0 = 0; foreach (Boolean isTagged in taggedVertexIndex) { if (isTagged == false) { notTaggedVertexIndex0.Add(currVertexIndex0); } currVertexIndex0++; } if (notTaggedVertexIndex0.Count > 0) { return(false); } taggedVertexIndex.Clear(); } #endif #endregion Int32 validMeshContextIndex = 0; foreach (Mesh mesh in scene.Meshes) { if (mesh.HasVertices && mesh.HasNormals && mesh.HasTangentBasis && mesh.TextureCoordinateChannels[0].Count > 0 && mesh.HasFaces) // process vertices in the mesh { if (mesh.HasBones) { foreach (Bone bone in mesh.Bones) { String boneName = bone.Name; if (!extractedBones.ContainsKey(boneName)) { // add new list of bone list extractedBones.Add(boneName, new List <Bone>()); extractedBones[boneName].Add(bone); // add new list of mesh context index boneToMeshContextIndex.Add(boneName, new List <Int32>()); boneToMeshContextIndex[boneName].Add(validMeshContextIndex); } else // same bone name, but different bone data exists { // no need to create list of data, just add new item // bone data could have different set of weighted vertices, but with same bone name extractedBones[boneName].Add(bone); boneToMeshContextIndex[boneName].Add(validMeshContextIndex); } } validMeshContextIndex++; #region Debug Validation Weight Vertex Count #if DEBUG taggedVertexIndexOffset.Add(taggedVertexIndex.Count); for (Int32 i = 0; i < mesh.VertexCount; ++i) { taggedVertexIndex.Add(false); taggedVertexWeights.Add(0.0f); } #endif #endregion } } } // BFS search to construct bone data Stack <Node> nodes = new Stack <Node>(); nodes.Push(scene.RootNode); while (nodes.Count != 0) { Node currNode = nodes.Pop(); H1SkeletalContext.JointNode jointNode = new H1SkeletalContext.JointNode(); H1SkeletalContext.JointNode parentJointNode = jointNodes.Find(x => (x.JointName == currNode.Parent.Name)); jointNode.Parent = parentJointNode; ExtractNodeSpace(currNode, ref jointNode); List <Bone> boneDataList = null; List <Int32> meshContextIndexList = null; if (extractedBones.TryGetValue(jointNode.JointName, out boneDataList)) { meshContextIndexList = boneToMeshContextIndex[jointNode.JointName]; // looping bone data list, extract bone data Int32 currBoneIndex = 0; foreach (Bone bone in boneDataList) { H1SkeletalContext.Joint newJointData = new H1SkeletalContext.Joint(); ExtractBone(bone, ref newJointData); newJointData.MeshContextIndex = meshContextIndexList[currBoneIndex]; currBoneIndex++; // store mesh context local-to-global H1Transform for later transformation of offsetMatrix for animation newJointData.MeshContextLocalToGlobal = convertedModel.Meshes[newJointData.MeshContextIndex].LocalToGlobalTransform; // add new joint data jointNode.JointDataList.Add(newJointData); } // mark this node is bone-space jointNode.MarkedAsBoneSpace = true; // tag its parent until it reaches the state that 'MarkedAsBoneSpace' is true H1SkeletalContext.JointNode markNode = jointNode.Parent; while (markNode != null && markNode.MarkedAsBoneSpace != true) { markNode.MarkedAsBoneSpace = true; markNode = markNode.Parent; } } else { // for debugging } foreach (Node child in currNode.Children) { nodes.Push(child); // tag child names to process child nodes after BFS search jointNode.ChildNodeNames.Add(child.Name); } // add new joint node Int32 newJointNodeIndex = jointNodes.Count; jointNodes.Add(jointNode); jointNameToJointNodeIndex.Add(jointNode.JointName, newJointNodeIndex); } // process tagged child nodes foreach (H1SkeletalContext.JointNode node in jointNodes) { foreach (String childName in node.ChildNodeNames) { node.Children.Add(jointNodes[jointNameToJointNodeIndex[childName]]); } } #region Debug Validation for total weight value of weighted vertices & vertex counts #if DEBUG foreach (H1SkeletalContext.JointNode node in jointNodes) { foreach (H1SkeletalContext.Joint jointData in node.JointDataList) { foreach (H1SkeletalContext.WeightedVertex weightedVertex in jointData.WeightedVertices) { // confirm all vertices in Mesh are tagged by weighted vertices taggedVertexIndex[taggedVertexIndexOffset[jointData.MeshContextIndex] + weightedVertex.VertexIndex] = true; // add vertex weight to verify taggedVertexWeights[taggedVertexIndexOffset[jointData.MeshContextIndex] + weightedVertex.VertexIndex] += weightedVertex.Weight; } } } // verification code to extract not tagged vertex index List <Int32> notTaggedVertexIndex = new List <Int32>(); Int32 currVertexIndex = 0; foreach (Boolean isTagged in taggedVertexIndex) { if (isTagged == false) { notTaggedVertexIndex.Add(currVertexIndex); } currVertexIndex++; } if (notTaggedVertexIndex.Count > 0) { return(false); } // verification code to extract vertex which has invalid vertex weight value List <Int32> invalidVertexWeightVertexIndex = new List <Int32>(); currVertexIndex = 0; foreach (float currVertexWeights in taggedVertexWeights) { if (currVertexWeights < 0.99f) { invalidVertexWeightVertexIndex.Add(currVertexIndex); } } if (invalidVertexWeightVertexIndex.Count > 0) { return(false); } #endif #endregion convertedModel.SkeletalContexts.Add(skeletalContext); } // extract the animations if (scene.HasAnimations) { H1AnimationContext newAnimContext = new H1AnimationContext(); for (Int32 animIndex = 0; animIndex < scene.AnimationCount; ++animIndex) { Animation currAnimation = scene.Animations[animIndex]; if (currAnimation.HasNodeAnimations) // we only handle node animations (not vertex animation) { // create new animation sequence H1AnimationContext.AnimSequence newAnimSeq = new H1AnimationContext.AnimSequence(); newAnimSeq.AnimSeqName = currAnimation.Name; newAnimSeq.Duration = currAnimation.DurationInTicks; newAnimSeq.TicksPerSecond = currAnimation.TicksPerSecond; foreach (NodeAnimationChannel animChannel in currAnimation.NodeAnimationChannels) { H1AnimationContext.JointAnimation newJointAnim = new H1AnimationContext.JointAnimation(); newJointAnim.BoneName = animChannel.NodeName; // calculate maximum number of keys to fill up the special case (only holding one key) Int32 maxNumKeys = Math.Max(Math.Max(animChannel.ScalingKeyCount, animChannel.RotationKeyCount), animChannel.PositionKeyCount); if (animChannel.HasPositionKeys) { if (animChannel.PositionKeyCount == 1) // special handling (only holding one key) { VectorKey positionKey = animChannel.PositionKeys[0]; for (Int32 numKey = 0; numKey < maxNumKeys; ++numKey) { H1AnimationContext.PositionKey newPosKey = new H1AnimationContext.PositionKey(); newPosKey.Time = positionKey.Time; newPosKey.Value = new Vector3(positionKey.Value.X, positionKey.Value.Y, positionKey.Value.Z); newJointAnim.PosKeys.Add(newPosKey); } } else { foreach (VectorKey positionKey in animChannel.PositionKeys) { H1AnimationContext.PositionKey newPosKey = new H1AnimationContext.PositionKey(); newPosKey.Time = positionKey.Time; newPosKey.Value = new Vector3(positionKey.Value.X, positionKey.Value.Y, positionKey.Value.Z); newJointAnim.PosKeys.Add(newPosKey); } } } if (animChannel.HasRotationKeys) { if (animChannel.RotationKeyCount == 1) // special handling (only holding one key) { QuaternionKey quatKey = animChannel.RotationKeys[0]; for (Int32 numKey = 0; numKey < maxNumKeys; ++numKey) { H1AnimationContext.QuaternionKey newQuatKey = new H1AnimationContext.QuaternionKey(); newQuatKey.Time = quatKey.Time; newQuatKey.Value = new SharpDX.Quaternion(quatKey.Value.X, quatKey.Value.Y, quatKey.Value.Z, quatKey.Value.W); newJointAnim.RotKeys.Add(newQuatKey); } } else { foreach (QuaternionKey quatKey in animChannel.RotationKeys) { H1AnimationContext.QuaternionKey newQuatKey = new H1AnimationContext.QuaternionKey(); newQuatKey.Time = quatKey.Time; newQuatKey.Value = new SharpDX.Quaternion(quatKey.Value.X, quatKey.Value.Y, quatKey.Value.Z, quatKey.Value.W); newJointAnim.RotKeys.Add(newQuatKey); } } } if (animChannel.HasScalingKeys) { if (animChannel.ScalingKeyCount == 1) // special handling (only holding one key) { VectorKey scalingKey = animChannel.ScalingKeys[0]; for (Int32 numKey = 0; numKey < maxNumKeys; ++numKey) { H1AnimationContext.ScalingKey newScalingKey = new H1AnimationContext.ScalingKey(); newScalingKey.Time = scalingKey.Time; newScalingKey.Value = new Vector3(scalingKey.Value.X, scalingKey.Value.Y, scalingKey.Value.Z); newJointAnim.ScaleKeys.Add(newScalingKey); } } else { foreach (VectorKey scalingKey in animChannel.ScalingKeys) { H1AnimationContext.ScalingKey newScalingKey = new H1AnimationContext.ScalingKey(); newScalingKey.Time = scalingKey.Time; newScalingKey.Value = new Vector3(scalingKey.Value.X, scalingKey.Value.Y, scalingKey.Value.Z); newJointAnim.ScaleKeys.Add(newScalingKey); } } } newAnimSeq.BoneAnimations.Add(newJointAnim); } newAnimContext.AnimSequences.Add(newAnimSeq); } } // set the animation context convertedModel.AnimationContext = newAnimContext; } return(true); }