Ejemplo n.º 1
0
        /// <summary>
        /// Generate LiveMesh instance from FBX data stream
        /// </summary>
        /// <param name="stream">FBX data to read</param>
        /// <param name="boneRegexs">Optional bone regex mappings</param>
        /// <param name="attachPointRegexs">Optional attach point regex mappings</param>
        /// <param name="debugLog">Debug log output function</param>
        /// <returns>LiveMesh instance</returns>
        public static LiveMesh GenerateLiveMesh(Stream stream, Dictionary <string, BoneType> boneRegexs = null,
                                                Dictionary <string, AttachPointType> attachPointRegexs  = null, Action <string> debugLog = null)
        {
            // Relevant assimp documentation can be found here:
            // http://assimp.sourceforge.net/lib_html/data.html
            Scene scene;

            // API goes into native code to load FBX
            using (var ctx = new AssimpContext())
                scene = ctx.ImportFileFromStream(stream, PostProcessSteps.Triangulate, "fbx");
            var bones        = new Dictionary <Bone, int>();
            var mBones       = new List <Modeling.Bone>();
            var uCount       = 0;
            var uACount      = 0;
            var attachPoints = new List <AttachPoint>();
            var subMeshes    = new LiveSubMesh[scene.MeshCount];

            debugLog?.Invoke($"Sub-meshes: {scene.MeshCount}");
            // Load sub-meshes
            for (var iSubMesh = 0; iSubMesh < subMeshes.Length; iSubMesh++)
            {
                // Define sub-mesh
                var subMesh = scene.Meshes[iSubMesh];
                debugLog?.Invoke($"-Sub-mesh [{subMesh.Name}]");
                var vertices    = new float[subMesh.VertexCount * 3];
                var uvs         = new float[subMesh.VertexCount * 2];
                var normals     = new float[subMesh.VertexCount * 3];
                var boneIds     = new int[subMesh.VertexCount * 4];
                var boneWeights = new float[subMesh.VertexCount * 4];
                var boneCounts  = new int[subMesh.VertexCount];
                var triangles   = new int[subMesh.FaceCount * 3];
                subMeshes[iSubMesh] = new LiveSubMesh {
                    Vertices    = vertices, UVs = uvs, Normals = normals, BoneIds = boneIds, BoneWeights = boneWeights,
                    Triangles   = triangles,
                    MaterialIdx = subMesh.MaterialIndex, VertexCount = subMesh.VertexCount
                };
                // Get vertices
                debugLog?.Invoke($"--Vertices: {subMesh.VertexCount}");
                for (var iVertex = 0; iVertex < subMesh.Vertices.Count; iVertex++)
                {
                    vertices[iVertex * 3]     = subMesh.Vertices[iVertex].X;
                    vertices[iVertex * 3 + 1] = subMesh.Vertices[iVertex].Y;
                    vertices[iVertex * 3 + 2] = subMesh.Vertices[iVertex].Z;
                }

                // Get UVs
                for (var iUv = 0; iUv < subMesh.TextureCoordinateChannels[0].Count; iUv++)
                {
                    uvs[iUv * 2]     = subMesh.TextureCoordinateChannels[0][iUv].X;
                    uvs[iUv * 2 + 1] = subMesh.TextureCoordinateChannels[0][iUv].X;
                }

                // Get normals
                for (var iNormal = 0; iNormal < subMesh.Normals.Count; iNormal++)
                {
                    normals[iNormal * 3]     = subMesh.Normals[iNormal].X;
                    normals[iNormal * 3 + 1] = subMesh.Normals[iNormal].Y;
                    normals[iNormal * 3 + 2] = subMesh.Normals[iNormal].Z;
                }

                // Get triangles
                debugLog?.Invoke($"--Triangles: {subMesh.FaceCount}");
                for (var iTriangle = 0; iTriangle < subMesh.FaceCount; iTriangle++)
                {
                    var x = subMesh.Faces[iTriangle];
                    for (var i = 0; i < 3; i++)
                    {
                        triangles[3 * iTriangle + i] = x.Indices[i];
                    }
                }

                // Get bones
                debugLog?.Invoke($"--Bones: {subMesh.BoneCount}");
                foreach (var bone in subMesh.Bones)
                {
                    foreach (var vWeight in bone.VertexWeights)
                    {
                        var count = boneCounts[vWeight.VertexID];
                        if (count == 4)
                        {
                            continue;
                        }
                        boneIds[4 * vWeight.VertexID + count] =
                            bones.TryGetValue(bone, out var bId) ? bId : bones.Count;
                        boneWeights[4 * vWeight.VertexID + count] = vWeight.Weight;
                        boneCounts[vWeight.VertexID]++;
                    }

                    // Skip bone if already processed
                    if (bones.ContainsKey(bone))
                    {
                        continue;
                    }
                    debugLog?.Invoke($"{{Bone [{bone.Name}]}}");
                    // Add bone
                    var mBone = new Modeling.Bone {
                        BoneName = bone.Name, BindPose = bone.OffsetMatrix.ToMatrix4x4()
                    };
                    if (boneRegexs != null)
                    {
                        foreach (var entry in boneRegexs)
                        {
                            if (Regex.IsMatch(bone.Name, entry.Key))
                            {
                                mBone.Type = entry.Value;
                                uCount++;
                                break;
                            }
                        }
                    }

                    bones.Add(bone, bones.Count);
                    mBones.Add(mBone);
                    // Add attach point if applicable
                    if (attachPointRegexs == null)
                    {
                        continue;
                    }
                    foreach (var entry in attachPointRegexs)
                    {
                        if (Regex.IsMatch(bone.Name, entry.Key))
                        {
                            attachPoints.Add(new AttachPoint {
                                BoneName = bone.Name,
                                BindPose = bone.OffsetMatrix.ToMatrix4x4Array(),
                                Type     = entry.Value
                            });
                            uACount++;
                            break;
                        }
                    }
                }
            }

            debugLog?.Invoke($"Total stored bones: {mBones.Count}");
            debugLog?.Invoke($"Total BoneType-matched bones: {uCount}");
            debugLog?.Invoke($"Total AttachPointType-matched bones: {uACount}");

            return(new LiveMesh {
                Bones = mBones.ToArray(),
                DefaultAttachPoints = attachPoints.ToArray(),
                SubMeshes = subMeshes
            });
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Generate LiveAnim instance from FBX data stream
        /// </summary>
        /// <param name="stream">FBX data to read</param>
        /// <param name="boneRegexs">Optional bone regex mappings</param>
        /// <param name="debugLog">Debug log output function</param>
        /// <returns>LiveAnim instance</returns>
        public static LiveAnim GenerateLiveAnim(Stream stream, Dictionary <string, BoneType> boneRegexs = null,
                                                Action <string> debugLog = null)
        {
            // Relevant assimp documentation can be found here:
            // http://assimp.sourceforge.net/lib_html/data.html
            Scene scene;

            // API goes into native code to load FBX
            using (var ctx = new AssimpContext())
                scene = ctx.ImportFileFromStream(stream, PostProcessSteps.Triangulate, "fbx");
            var bones    = new Dictionary <string, int>();
            var mBones   = new List <Modeling.Bone>();
            var uCount   = 0;
            var subAnims = new List <LiveSubAnim>();

            debugLog?.Invoke($"Sub-meshes: {scene.MeshCount}");
            // Get bones
            foreach (var subMesh in scene.Meshes)
            {
                debugLog?.Invoke($"-Sub-mesh [{subMesh.Name}]");
                debugLog?.Invoke($"--Bones: {subMesh.BoneCount}");
                foreach (var bone in subMesh.Bones)
                {
                    // Skip bone if already processed
                    if (bones.ContainsKey(bone.Name))
                    {
                        continue;
                    }
                    debugLog?.Invoke($"{{Bone [{bone.Name}]}}");
                    // Add bone
                    var mBone = new Modeling.Bone {
                        BoneName = bone.Name, BindPose = bone.OffsetMatrix.ToMatrix4x4()
                    };
                    if (boneRegexs != null)
                    {
                        foreach (var entry in boneRegexs)
                        {
                            if (Regex.IsMatch(bone.Name, entry.Key))
                            {
                                mBone.Type = entry.Value;
                                uCount++;
                                break;
                            }
                        }
                    }

                    bones.Add(bone.Name, bones.Count);
                    mBones.Add(mBone);
                }
            }

            debugLog?.Invoke($"Total stored bones: {mBones.Count}");
            debugLog?.Invoke($"Total BoneType-matched bones: {uCount}");

            // ?? Potential: Add internal structure for full mesh hierarchy with bone toggles (sort of like suggested)
            foreach (var anim in scene.Animations)
            {
                debugLog?.Invoke($"-Animation [{anim.Name}]");
                foreach (var chan in anim.NodeAnimationChannels)
                {
                    debugLog?.Invoke($"--Channel [{chan.NodeName}]");
                    // Hacky name matching
                    var nodeName  = chan.NodeName;
                    var idxAssimp = nodeName.IndexOf("_$AssimpFbx$_", StringComparison.Ordinal);
                    if (idxAssimp != -1)
                    {
                        nodeName = nodeName.Substring(0, idxAssimp);
                    }
                    var sub = new LiveSubAnim {
                        BoneId = bones[nodeName]
                    };
                    if (chan.HasPositionKeys)
                    {
                        debugLog?.Invoke($"---Position keys: {chan.PositionKeyCount}");
                        sub.PositionCount = chan.PositionKeyCount;
                        sub.PositionTimes = new float[chan.PositionKeyCount];
                        sub.PositionX     = new float[chan.PositionKeyCount];
                        sub.PositionY     = new float[chan.PositionKeyCount];
                        sub.PositionZ     = new float[chan.PositionKeyCount];
                        for (var i = 0; i < chan.PositionKeyCount; i++)
                        {
                            sub.PositionTimes[i] = (float)chan.PositionKeys[i].Time;
                            var vec = chan.PositionKeys[i].Value;
                            sub.PositionX[i] = vec.X;
                            sub.PositionY[i] = vec.Y;
                            sub.PositionZ[i] = vec.Z;
                        }
                    }

                    if (chan.HasRotationKeys)
                    {
                        debugLog?.Invoke($"---Rotation keys: {chan.RotationKeyCount}");
                        sub.RotationCount = chan.RotationKeyCount;
                        sub.RotationTimes = new float[chan.RotationKeyCount];
                        sub.RotationW     = new float[chan.PositionKeyCount];
                        sub.RotationX     = new float[chan.PositionKeyCount];
                        sub.RotationY     = new float[chan.PositionKeyCount];
                        sub.RotationZ     = new float[chan.PositionKeyCount];
                        for (var i = 0; i < chan.RotationKeyCount; i++)
                        {
                            sub.RotationTimes[i] = (float)chan.RotationKeys[i].Time;
                            var qua = chan.RotationKeys[i].Value;
                            sub.RotationW[i] = qua.W;
                            sub.RotationX[i] = qua.X;
                            sub.RotationY[i] = qua.Y;
                            sub.RotationZ[i] = qua.Z;
                        }
                    }

                    if (chan.HasScalingKeys)
                    {
                        debugLog?.Invoke($"---Scaling keys: {chan.ScalingKeyCount}");
                        sub.ScalingCount = chan.ScalingKeyCount;
                        sub.ScalingTimes = new float[chan.ScalingKeyCount];
                        sub.ScalingX     = new float[chan.ScalingKeyCount];
                        sub.ScalingY     = new float[chan.ScalingKeyCount];
                        sub.ScalingZ     = new float[chan.ScalingKeyCount];
                        for (var i = 0; i < chan.ScalingKeyCount; i++)
                        {
                            sub.ScalingTimes[i] = (float)chan.ScalingKeys[i].Time;
                            var sca = chan.ScalingKeys[i].Value;
                            sub.ScalingX[i] = sca.X;
                            sub.ScalingY[i] = sca.Y;
                            sub.ScalingZ[i] = sca.Z;
                        }
                    }

                    subAnims.Add(sub);
                }
            }

            debugLog?.Invoke($"Total stored animation channels: {subAnims.Count}");

            return(new LiveAnim {
                Bones = mBones.ToArray(),
                BoneSubAnims = subAnims.ToArray()
            });
        }