public void TestImportFromStream()
        {
            String path = Path.Combine(TestHelper.RootPath, "TestFiles/duck.dae");

            FileStream fs = File.OpenRead(path);

            AssimpContext importer = new AssimpContext();

            LogStream.IsVerboseLoggingEnabled = true;

            LogStream logstream = new LogStream(delegate(String msg, String userData)
            {
                Console.WriteLine(msg);
            });

            logstream.Attach();

            Scene scene = importer.ImportFileFromStream(fs, ".dae");

            fs.Close();

            Assert.IsNotNull(scene);
            Assert.IsTrue((scene.SceneFlags & SceneFlags.Incomplete) != SceneFlags.Incomplete);
        }
        public void TestImportFromStream()
        {
            String path = Path.Combine(TestHelper.RootPath, "TestFiles\\duck.dae");

            FileStream fs = File.OpenRead(path);

            AssimpContext importer = new AssimpContext();
            LogStream.IsVerboseLoggingEnabled = true;

            LogStream logstream = new LogStream(delegate(String msg, String userData)
            {
                Console.WriteLine(msg);
            });

            logstream.Attach();

            Scene scene = importer.ImportFileFromStream(fs, ".dae");

            fs.Close();

            Assert.IsNotNull(scene);
            Assert.IsTrue((scene.SceneFlags & SceneFlags.Incomplete) != SceneFlags.Incomplete);
        }
        private void Init(
            GraphicsDevice gd,
            ResourceFactory factory,
            Stream stream,
            string extension,
            VertexElementSemantic[] elementSemantics,
            ModelCreateInfo?createInfo,
            PostProcessSteps flags = DefaultPostProcessSteps)
        {
            // Load file
            AssimpContext assimpContext = new AssimpContext();
            Scene         pScene        = assimpContext.ImportFileFromStream(stream, DefaultPostProcessSteps, extension);

            parts.Clear();
            parts.Count = (uint)pScene.Meshes.Count;

            Vector3 scale   = new Vector3(1.0f);
            Vector2 uvscale = new Vector2(1.0f);
            Vector3 center  = new Vector3(0.0f);

            if (createInfo != null)
            {
                scale   = createInfo.Value.Scale;
                uvscale = createInfo.Value.UVScale;
                center  = createInfo.Value.Center;
            }

            RawList <float> vertices = new RawList <float>();
            RawList <uint>  indices  = new RawList <uint>();

            VertexCount = 0;
            IndexCount  = 0;

            // Load meshes
            for (int i = 0; i < pScene.Meshes.Count; i++)
            {
                var paiMesh = pScene.Meshes[i];

                parts[i]            = new ModelPart();
                parts[i].vertexBase = VertexCount;
                parts[i].indexBase  = IndexCount;

                VertexCount += (uint)paiMesh.VertexCount;

                var pColor = pScene.Materials[paiMesh.MaterialIndex].ColorDiffuse;

                Vector3D Zero3D = new Vector3D(0.0f, 0.0f, 0.0f);

                for (int j = 0; j < paiMesh.VertexCount; j++)
                {
                    Vector3D pPos       = paiMesh.Vertices[j];
                    Vector3D pNormal    = paiMesh.Normals[j];
                    Vector3D pTexCoord  = paiMesh.HasTextureCoords(0) ? paiMesh.TextureCoordinateChannels[0][j] : Zero3D;
                    Vector3D pTangent   = paiMesh.HasTangentBasis ? paiMesh.Tangents[j] : Zero3D;
                    Vector3D pBiTangent = paiMesh.HasTangentBasis ? paiMesh.BiTangents[j] : Zero3D;

                    foreach (VertexElementSemantic component in elementSemantics)
                    {
                        switch (component)
                        {
                        case VertexElementSemantic.Position:
                            vertices.Add(pPos.X * scale.X + center.X);
                            vertices.Add(-pPos.Y * scale.Y + center.Y);
                            vertices.Add(pPos.Z * scale.Z + center.Z);
                            break;

                        case VertexElementSemantic.Normal:
                            vertices.Add(pNormal.X);
                            vertices.Add(-pNormal.Y);
                            vertices.Add(pNormal.Z);
                            break;

                        case VertexElementSemantic.TextureCoordinate:
                            vertices.Add(pTexCoord.X * uvscale.X);
                            vertices.Add(pTexCoord.Y * uvscale.Y);
                            break;

                        case VertexElementSemantic.Color:
                            vertices.Add(pColor.R);
                            vertices.Add(pColor.G);
                            vertices.Add(pColor.B);
                            break;

                        default: throw new System.NotImplementedException();
                        }
                        ;
                    }

                    dim.Max.X = Math.Max(pPos.X, dim.Max.X);
                    dim.Max.Y = Math.Max(pPos.Y, dim.Max.Y);
                    dim.Max.Z = Math.Max(pPos.Z, dim.Max.Z);

                    dim.Min.X = Math.Min(pPos.X, dim.Min.X);
                    dim.Min.Y = Math.Min(pPos.Y, dim.Min.Y);
                    dim.Min.Z = Math.Min(pPos.Z, dim.Min.Z);
                }

                dim.Size = dim.Max - dim.Min;

                parts[i].vertexCount = (uint)paiMesh.VertexCount;

                uint indexBase = indices.Count;
                for (uint j = 0; j < paiMesh.FaceCount; j++)
                {
                    Face Face = paiMesh.Faces[(int)j];
                    if (Face.IndexCount != 3)
                    {
                        continue;
                    }
                    indices.Add(indexBase + (uint)Face.Indices[0]);
                    indices.Add(indexBase + (uint)Face.Indices[1]);
                    indices.Add(indexBase + (uint)Face.Indices[2]);
                    parts[i].indexCount += 3;
                    IndexCount          += 3;
                }
            }


            uint vBufferSize = (vertices.Count) * sizeof(float);
            uint iBufferSize = (indices.Count) * sizeof(uint);

            VertexBuffer = factory.CreateBuffer(new BufferDescription(vBufferSize, BufferUsage.VertexBuffer));
            IndexBuffer  = factory.CreateBuffer(new BufferDescription(iBufferSize, BufferUsage.IndexBuffer));

            gd.UpdateBuffer(VertexBuffer, 0, ref vertices[0], vBufferSize);
            gd.UpdateBuffer(IndexBuffer, 0, ref indices[0], iBufferSize);
        }
        public override void LoadAndWriteToStream(FileInfo inputFile, IAssetMeta meta, Stream outputStream)
        {
            PostProcessSteps assimpFlags = PostProcessSteps.FlipWindingOrder
                                           | PostProcessSteps.Triangulate
                                           | PostProcessSteps.PreTransformVertices
                                           | PostProcessSteps.GenerateUVCoords
                                           | PostProcessSteps.GenerateSmoothNormals
                                           | PostProcessSteps.FlipUVs
            ;

            var context = new AssimpContext();

            context.SetConfig(new FloatPropertyConfig("AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE", 80f));

            string extension = inputFile.Extension;

            using FileStream fs = inputFile.OpenRead();

            var scene = context.ImportFileFromStream(fs, assimpFlags, extension);

            // Generate vertex buffer from ASSIMP scene data
            float scale = 1.0f;

            MeshData newMesh = new MeshData();

            newMesh.subMeshes = new SubMeshData[scene.MeshCount];

            for (int m = 0; m < scene.MeshCount; m++)
            {
                Mesh     mesh     = scene.Meshes[m];
                Vertex[] vertices = new Vertex[mesh.VertexCount];
                Face[]   faces    = mesh.Faces.Where(x => x.IndexCount == 3).ToArray();            // Remove any degenerate faces
                UInt32[] indices  = new UInt32[faces.Length * 3];

                //DebugHelper.AssertThrow<OverflowException>(faces.Length * 3 <= UInt32.MaxValue);

                for (int v = 0; v < mesh.VertexCount; v++)
                {
                    Vertex vertex;

                    vertex.position = new vec3(mesh.Vertices[v].X, mesh.Vertices[v].Y, mesh.Vertices[v].Z) * scale;
                    vertex.normal   = new vec3(mesh.Normals[v].X, mesh.Normals[v].Y, mesh.Normals[v].Z);
                    if (mesh.HasTextureCoords(0))
                    {
                        vertex.uv0 = new vec2(mesh.TextureCoordinateChannels[0][v].X, mesh.TextureCoordinateChannels[0][v].Y);
                    }
                    else
                    {
                        vertex.uv0 = vec2.Zero;
                    }

                    vertices[v] = vertex;
                }

                for (int i = 0; i < faces.Length; i++)
                {
                    indices[i * 3 + 0] = (UInt16)faces[i].Indices[0];
                    indices[i * 3 + 1] = (UInt16)faces[i].Indices[1];
                    indices[i * 3 + 2] = (UInt16)faces[i].Indices[2];
                }

                newMesh.subMeshes[m] = new SubMeshData(vertices, indices);
            }


            //Write mesh to stream
            using BinaryWriter writer = new BinaryWriter(outputStream, Encoding.UTF8, true);
            writer.Write(newMesh.subMeshes.Length);
            foreach (SubMeshData subMesh in newMesh.subMeshes)
            {
                writer.Write(subMesh.vertices.Length);
                Span <byte> verts = new Span <Vertex>(subMesh.vertices).Cast <Vertex, byte>();
                writer.Write(verts);

                writer.Write(subMesh.indices.Length);
                Span <uint> indsU = subMesh.indices;
                var         inds  = indsU.Cast <UInt32, byte>();
                writer.Write(inds);
            }
            writer.Flush();
        }
Exemple #5
0
        public unsafe override ProcessedModel ProcessT(Stream stream, string extension)
        {
            AssimpContext ac    = new AssimpContext();
            Scene         scene = ac.ImportFileFromStream(
                stream,
                PostProcessSteps.FlipWindingOrder | PostProcessSteps.GenerateNormals | PostProcessSteps.FlipUVs,
                extension);
            aiMatrix4x4 rootNodeInverseTransform = scene.RootNode.Transform;

            rootNodeInverseTransform.Inverse();

            List <ProcessedMeshPart>  parts      = new List <ProcessedMeshPart>();
            List <ProcessedAnimation> animations = new List <ProcessedAnimation>();

            HashSet <string> encounteredNames = new HashSet <string>();

            for (int meshIndex = 0; meshIndex < scene.MeshCount; meshIndex++)
            {
                Mesh   mesh     = scene.Meshes[meshIndex];
                string meshName = mesh.Name;
                if (string.IsNullOrEmpty(meshName))
                {
                    meshName = $"mesh_{meshIndex}";
                }
                int counter = 1;
                while (!encounteredNames.Add(meshName))
                {
                    meshName = mesh.Name + "_" + counter.ToString();
                    counter += 1;
                }
                int vertexCount = mesh.VertexCount;

                int positionOffset    = 0;
                int normalOffset      = 12;
                int texCoordsOffset   = -1;
                int boneWeightOffset  = -1;
                int boneIndicesOffset = -1;

                List <VertexElementDescription> elementDescs = new List <VertexElementDescription>();
                elementDescs.Add(new VertexElementDescription("Position", VertexElementSemantic.Position, VertexElementFormat.Float3));
                elementDescs.Add(new VertexElementDescription("Normal", VertexElementSemantic.Normal, VertexElementFormat.Float3));
                normalOffset = 12;

                int vertexSize = 24;

                bool hasTexCoords = mesh.HasTextureCoords(0);
                elementDescs.Add(new VertexElementDescription("TexCoords", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float2));
                texCoordsOffset = vertexSize;
                vertexSize     += 8;

                bool hasBones = mesh.HasBones;
                if (hasBones)
                {
                    elementDescs.Add(new VertexElementDescription("BoneWeights", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float4));
                    elementDescs.Add(new VertexElementDescription("BoneIndices", VertexElementSemantic.TextureCoordinate, VertexElementFormat.UInt4));

                    boneWeightOffset = vertexSize;
                    vertexSize      += 16;

                    boneIndicesOffset = vertexSize;
                    vertexSize       += 16;
                }

                byte[]            vertexData = new byte[vertexCount * vertexSize];
                VertexDataBuilder builder    = new VertexDataBuilder(vertexData, vertexSize);
                Vector3           min        = vertexCount > 0 ? mesh.Vertices[0].ToSystemVector3() : Vector3.Zero;
                Vector3           max        = vertexCount > 0 ? mesh.Vertices[0].ToSystemVector3() : Vector3.Zero;

                for (int i = 0; i < vertexCount; i++)
                {
                    Vector3 position = mesh.Vertices[i].ToSystemVector3();
                    min = Vector3.Min(min, position);
                    max = Vector3.Max(max, position);

                    builder.WriteVertexElement(
                        i,
                        positionOffset,
                        position);

                    Vector3 normal = mesh.Normals[i].ToSystemVector3();
                    builder.WriteVertexElement(i, normalOffset, normal);

                    if (mesh.HasTextureCoords(0))
                    {
                        builder.WriteVertexElement(
                            i,
                            texCoordsOffset,
                            new Vector2(mesh.TextureCoordinateChannels[0][i].X, mesh.TextureCoordinateChannels[0][i].Y));
                    }
                    else
                    {
                        builder.WriteVertexElement(
                            i,
                            texCoordsOffset,
                            new Vector2());
                    }
                }

                List <int> indices = new List <int>();
                foreach (Face face in mesh.Faces)
                {
                    if (face.IndexCount == 3)
                    {
                        indices.Add(face.Indices[0]);
                        indices.Add(face.Indices[1]);
                        indices.Add(face.Indices[2]);
                    }
                }

                Dictionary <string, uint>   boneIDsByName = new Dictionary <string, uint>();
                System.Numerics.Matrix4x4[] boneOffsets   = new System.Numerics.Matrix4x4[mesh.BoneCount];

                if (hasBones)
                {
                    Dictionary <int, int> assignedBoneWeights = new Dictionary <int, int>();
                    for (uint boneID = 0; boneID < mesh.BoneCount; boneID++)
                    {
                        Bone   bone     = mesh.Bones[(int)boneID];
                        string boneName = bone.Name;
                        int    suffix   = 1;
                        while (boneIDsByName.ContainsKey(boneName))
                        {
                            boneName = bone.Name + "_" + suffix.ToString();
                            suffix  += 1;
                        }

                        boneIDsByName.Add(boneName, boneID);
                        foreach (VertexWeight weight in bone.VertexWeights)
                        {
                            int relativeBoneIndex = GetAndIncrementRelativeBoneIndex(assignedBoneWeights, weight.VertexID);
                            builder.WriteVertexElement(weight.VertexID, boneIndicesOffset + (relativeBoneIndex * sizeof(uint)), boneID);
                            builder.WriteVertexElement(weight.VertexID, boneWeightOffset + (relativeBoneIndex * sizeof(float)), weight.Weight);
                        }

                        System.Numerics.Matrix4x4 offsetMat = bone.OffsetMatrix.ToSystemMatrixTransposed();
                        System.Numerics.Matrix4x4.Decompose(offsetMat, out var scale, out var rot, out var trans);
                        offsetMat = System.Numerics.Matrix4x4.CreateScale(scale)
                                    * System.Numerics.Matrix4x4.CreateFromQuaternion(rot)
                                    * System.Numerics.Matrix4x4.CreateTranslation(trans);

                        boneOffsets[boneID] = offsetMat;
                    }
                }
                builder.FreeGCHandle();

                uint indexCount = (uint)indices.Count;

                int[]  int32Indices = indices.ToArray();
                byte[] indexData    = new byte[indices.Count * sizeof(uint)];
                fixed(byte *indexDataPtr = indexData)
                {
                    fixed(int *int32Ptr = int32Indices)
                    {
                        Buffer.MemoryCopy(int32Ptr, indexDataPtr, indexData.Length, indexData.Length);
                    }
                }

                ProcessedMeshPart part = new ProcessedMeshPart(
                    vertexData,
                    elementDescs.ToArray(),
                    indexData,
                    IndexFormat.UInt32,
                    (uint)indices.Count,
                    boneIDsByName,
                    boneOffsets);
                parts.Add(part);
            }

            // Nodes
            Node rootNode = scene.RootNode;
            List <ProcessedNode> processedNodes = new List <ProcessedNode>();

            ConvertNode(rootNode, -1, processedNodes);

            ProcessedNodeSet nodes = new ProcessedNodeSet(processedNodes.ToArray(), 0, rootNodeInverseTransform.ToSystemMatrixTransposed());

            for (int animIndex = 0; animIndex < scene.AnimationCount; animIndex++)
            {
                Animation animation = scene.Animations[animIndex];
                Dictionary <string, ProcessedAnimationChannel> channels = new Dictionary <string, ProcessedAnimationChannel>();
                for (int channelIndex = 0; channelIndex < animation.NodeAnimationChannelCount; channelIndex++)
                {
                    NodeAnimationChannel nac = animation.NodeAnimationChannels[channelIndex];
                    channels[nac.NodeName] = ConvertChannel(nac);
                }

                string baseAnimName = animation.Name;
                if (string.IsNullOrEmpty(baseAnimName))
                {
                    baseAnimName = "anim_" + animIndex;
                }

                string animationName = baseAnimName;


                int counter = 1;
                while (!encounteredNames.Add(animationName))
                {
                    animationName = baseAnimName + "_" + counter.ToString();
                    counter      += 1;
                }
            }

            return(new ProcessedModel()
            {
                MeshParts = parts.ToArray(),
                Animations = animations.ToArray(),
                Nodes = nodes
            });
        }
        protected override void CreateResources(ResourceFactory factory)
        {
            _projectionBuffer = factory.CreateBuffer(new BufferDescription(64, BufferUsage.UniformBuffer | BufferUsage.Dynamic));
            _viewBuffer       = factory.CreateBuffer(new BufferDescription(64, BufferUsage.UniformBuffer | BufferUsage.Dynamic));
            _worldBuffer      = factory.CreateBuffer(new BufferDescription(64, BufferUsage.UniformBuffer | BufferUsage.Dynamic));
            Matrix4x4 worldMatrix =
                Matrix4x4.CreateTranslation(0, 15000, -5000)
                * Matrix4x4.CreateRotationX(3 * (float)Math.PI / 2)
                * Matrix4x4.CreateScale(0.05f);

            GraphicsDevice.UpdateBuffer(_worldBuffer, 0, ref worldMatrix);

            ResourceLayout layout = factory.CreateResourceLayout(new ResourceLayoutDescription(
                                                                     new ResourceLayoutElementDescription("Projection", ResourceKind.UniformBuffer, ShaderStages.Vertex),
                                                                     new ResourceLayoutElementDescription("View", ResourceKind.UniformBuffer, ShaderStages.Vertex),
                                                                     new ResourceLayoutElementDescription("World", ResourceKind.UniformBuffer, ShaderStages.Vertex),
                                                                     new ResourceLayoutElementDescription("Bones", ResourceKind.UniformBuffer, ShaderStages.Vertex),
                                                                     new ResourceLayoutElementDescription("SurfaceTex", ResourceKind.TextureReadOnly, ShaderStages.Fragment),
                                                                     new ResourceLayoutElementDescription("SurfaceSampler", ResourceKind.Sampler, ShaderStages.Fragment)));

            Texture texture;

            using (Stream ktxStream = OpenEmbeddedAssetStream("goblin_bc3_unorm.ktx"))
            {
                texture = KtxFile.LoadTexture(
                    GraphicsDevice,
                    factory,
                    ktxStream,
                    PixelFormat.BC3_UNorm);
            }
            _texView = ResourceFactory.CreateTextureView(texture);

            VertexLayoutDescription vertexLayouts = new VertexLayoutDescription(
                new[]
            {
                new VertexElementDescription("Position", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float3),
                new VertexElementDescription("UV", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float2),
                new VertexElementDescription("BoneWeights", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float4),
                new VertexElementDescription("BoneIndices", VertexElementSemantic.TextureCoordinate, VertexElementFormat.UInt4),
            });

            GraphicsPipelineDescription gpd = new GraphicsPipelineDescription(
                BlendStateDescription.SingleOverrideBlend,
                DepthStencilStateDescription.DepthOnlyLessEqual,
                new RasterizerStateDescription(FaceCullMode.Back, PolygonFillMode.Solid, FrontFace.CounterClockwise, true, false),
                PrimitiveTopology.TriangleList,
                new ShaderSetDescription(
                    new[] { vertexLayouts },
                    factory.CreateFromSpirv(
                        new ShaderDescription(ShaderStages.Vertex, Encoding.UTF8.GetBytes(VertexCode), "main"),
                        new ShaderDescription(ShaderStages.Fragment, Encoding.UTF8.GetBytes(FragmentCode), "main"))),
                layout,
                GraphicsDevice.SwapchainFramebuffer.OutputDescription);

            _pipeline = factory.CreateGraphicsPipeline(ref gpd);

            AssimpContext ac = new AssimpContext();

            using (Stream modelStream = OpenEmbeddedAssetStream("goblin.dae"))
            {
                _scene = ac.ImportFileFromStream(modelStream, "dae");
            }
            _rootNodeInverseTransform = _scene.RootNode.Transform;
            _rootNodeInverseTransform.Inverse();

            _firstMesh = _scene.Meshes[0];
            AnimatedVertex[] vertices = new AnimatedVertex[_firstMesh.VertexCount];
            for (int i = 0; i < vertices.Length; i++)
            {
                vertices[i].Position = new Vector3(_firstMesh.Vertices[i].X, _firstMesh.Vertices[i].Y, _firstMesh.Vertices[i].Z);
                vertices[i].UV       = new Vector2(_firstMesh.TextureCoordinateChannels[0][i].X, _firstMesh.TextureCoordinateChannels[0][i].Y);
            }

            _animation = _scene.Animations[0];

            List <int> indices = new List <int>();

            foreach (Face face in _firstMesh.Faces)
            {
                if (face.IndexCount == 3)
                {
                    indices.Add(face.Indices[0]);
                    indices.Add(face.Indices[1]);
                    indices.Add(face.Indices[2]);
                }
            }

            for (uint boneID = 0; boneID < _firstMesh.BoneCount; boneID++)
            {
                Bone bone = _firstMesh.Bones[(int)boneID];
                _boneIDsByName.Add(bone.Name, boneID);
                foreach (VertexWeight weight in bone.VertexWeights)
                {
                    vertices[weight.VertexID].AddBone(boneID, weight.Weight);
                }
            }
            Array.Resize(ref _boneTransformations, _firstMesh.BoneCount);

            _bonesBuffer = ResourceFactory.CreateBuffer(new BufferDescription(
                                                            64 * 64, BufferUsage.UniformBuffer | BufferUsage.Dynamic));

            _rs = factory.CreateResourceSet(new ResourceSetDescription(layout,
                                                                       _projectionBuffer, _viewBuffer, _worldBuffer, _bonesBuffer, _texView, GraphicsDevice.Aniso4xSampler));

            _indexCount = (uint)indices.Count;

            _vertexBuffer = ResourceFactory.CreateBuffer(new BufferDescription(
                                                             (uint)(vertices.Length * Unsafe.SizeOf <AnimatedVertex>()), BufferUsage.VertexBuffer));
            GraphicsDevice.UpdateBuffer(_vertexBuffer, 0, vertices);

            _indexBuffer = ResourceFactory.CreateBuffer(new BufferDescription(
                                                            _indexCount * 4, BufferUsage.IndexBuffer));
            GraphicsDevice.UpdateBuffer(_indexBuffer, 0, indices.ToArray());

            _cl = factory.CreateCommandList();
            _camera.Position    = new Vector3(110, -87, -532);
            _camera.Yaw         = 0.45f;
            _camera.Pitch       = -0.55f;
            _camera.MoveSpeed   = 1000f;
            _camera.FarDistance = 100000;
        }
Exemple #7
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
            });
        }
Exemple #8
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()
            });
        }
        public static CustomModel Load(string path, PostProcessSteps ppSteps, bool loadFromExe = false, params PropertyConfig[] configs)
        {
            Log.Info($"Loading model: {path}");

            var model = new CustomModel();

            if (!File.Exists(path) && !loadFromExe)
            {
                Log.Error($"Model {path} does not exist.");
                return(model);
            }

            var importer = new AssimpContext();

            if (configs != null)
            {
                foreach (var config in configs)
                {
                    importer.SetConfig(config);
                }
            }

            try
            {
                Scene scene;
                if (loadFromExe)
                {
                    var    assembly = Assembly.GetAssembly(typeof(ModelLoader));
                    Stream stream   = assembly.GetManifestResourceStream(ModelsPath + path);
                    scene = importer.ImportFileFromStream(stream, ppSteps, Path.GetExtension(path));
                    stream.Dispose();
                }
                else
                {
                    scene = importer.ImportFile(path, ppSteps);
                }
                if (scene == null)
                {
                    Log.Error($"Error loading model {path}. Scene was null.");
                    return(model);
                }

                if (scene.Meshes.Count == 0)
                {
                    Log.Error($"Error loading model {path}. No meshes found.");
                    return(model);
                }

                if (scene.Meshes.Count > 1)
                {
                    Log.Warn($"Model {path} containing more than one mesh. Using first mesh.");
                }

                var mesh = new Mesh(new List <float>(), new List <float>(), new List <float>(), new List <int>());
                for (int i = 0; i < scene.MeshCount; i++)
                {
                    mesh += ProcessMesh(scene.Meshes[i]);
                }

                var material = ProcessMaterial(scene.Materials[scene.Meshes[0].MaterialIndex],
                                               loadFromExe ? "" : Path.GetDirectoryName(Path.GetFullPath(path)));

                model.Mesh     = mesh;
                model.Material = material;

                return(model);
            }
            catch (AssimpException e)
            {
                Log.Error("Assimp has thrown an exception.", e);
                Console.WriteLine(e.Message);
                return(model);
            }
        }
Exemple #10
0
        internal static GeoModel LoadModel(string filename, bool flipTextureCoordinates = false, AssemblyMode am = AssemblyMode.Internal)
        {
            AssimpContext importer = new AssimpContext();

            importer.SetConfig(new VertexBoneWeightLimitConfig(KWEngine.MAX_BONE_WEIGHTS));
            importer.SetConfig(new MaxBoneCountConfig(KWEngine.MAX_BONES));
            FileType type  = CheckFileEnding(filename);
            Scene    scene = null;

            if (am != AssemblyMode.File)
            {
                if (type == FileType.Invalid)
                {
                    throw new Exception("Model file has invalid type.");
                }
                string   resourceName;
                Assembly assembly;

                if (am == AssemblyMode.Internal)
                {
                    assembly     = Assembly.GetExecutingAssembly();
                    resourceName = "KWEngine2.Assets.Models." + filename;
                }
                else
                {
                    assembly     = Assembly.GetEntryAssembly();
                    resourceName = assembly.GetName().Name + "." + filename;
                }

                using (Stream s = assembly.GetManifestResourceStream(resourceName))
                {
                    PostProcessSteps steps =
                        PostProcessSteps.LimitBoneWeights
                        | PostProcessSteps.Triangulate
                        | PostProcessSteps.ValidateDataStructure
                        | PostProcessSteps.GenerateUVCoords
                        | PostProcessSteps.CalculateTangentSpace;
                    if (filename != "kwcube.obj" && filename != "kwcube6.obj")
                    {
                        steps |= PostProcessSteps.JoinIdenticalVertices;
                    }
                    if (type == FileType.DirectX)
                    {
                        steps |= PostProcessSteps.FlipWindingOrder;
                    }
                    if (flipTextureCoordinates)
                    {
                        steps |= PostProcessSteps.FlipUVs;
                    }
                    scene = importer.ImportFileFromStream(s, steps);
                }
            }
            else
            {
                if (type != FileType.Invalid)
                {
                    PostProcessSteps steps =
                        PostProcessSteps.LimitBoneWeights
                        | PostProcessSteps.Triangulate
                        //| PostProcessSteps.FixInFacingNormals
                        | PostProcessSteps.ValidateDataStructure
                        | PostProcessSteps.GenerateUVCoords
                        | PostProcessSteps.CalculateTangentSpace
                        | PostProcessSteps.JoinIdenticalVertices
                    ;
                    if (type == FileType.DirectX)
                    {
                        steps |= PostProcessSteps.FlipWindingOrder;
                    }
                    if (flipTextureCoordinates)
                    {
                        steps |= PostProcessSteps.FlipUVs;
                    }

                    scene = importer.ImportFile(filename, steps);
                }
                else
                {
                    throw new Exception("Could not load model: only OBJ, DAE, FBX and X are supported (GLTF support coming soon).");
                }
            }
            if (scene == null)
            {
                throw new Exception("Could not load or find model: " + filename);
            }

            GeoModel model = ProcessScene(scene, am == AssemblyMode.File ? filename.ToLower().Trim() : filename, am);

            return(model);
        }
Exemple #11
0
        protected override void CreateInternal(ReadOnlyMemory <byte> data)
        {
            var str = new ReadOnlyLinkedMemoryStream();

            str.AddMemory(data);

            _assContext ??= new AssimpContext();
            Scene scene = _assContext.ImportFileFromStream(str,
                                                           PostProcessSteps.Triangulate |
                                                           PostProcessSteps.FlipUVs |
                                                           PostProcessSteps.OptimizeGraph |
                                                           PostProcessSteps.OptimizeMeshes);

            var embeddedTextures = new List <Texture>();

            for (var i = 0; i < scene.TextureCount; i++)
            {
                EmbeddedTexture assTexture      = scene.Textures[i];
                var             embeddedTexture = new TextureAsset();
                embeddedTexture.Create(assTexture.CompressedData);
                embeddedTextures.Add(embeddedTexture.Texture);
            }

            _materials = new List <MeshMaterial>();
            for (var i = 0; i < scene.MaterialCount; i++)
            {
                Material material  = scene.Materials[i];
                Color4D  diffColor = material.ColorDiffuse;

                bool embeddedTexture = material.HasTextureDiffuse && embeddedTextures.Count > material.TextureDiffuse.TextureIndex;
                var  emotionMaterial = new MeshMaterial
                {
                    Name               = material.Name,
                    DiffuseColor       = new Color(new Vector4(diffColor.R, diffColor.G, diffColor.B, diffColor.A)),
                    DiffuseTextureName = embeddedTexture ? $"EmbeddedTexture{material.TextureDiffuse.TextureIndex}" : null,
                    DiffuseTexture     = embeddedTexture ? embeddedTextures[material.TextureDiffuse.TextureIndex] : null
                };

                _materials.Add(emotionMaterial);
            }

            _animations = new List <SkeletalAnimation>();
            ProcessAnimations(scene);

            _meshes = new List <Mesh>();
            Node rootNode = scene.RootNode;
            SkeletonAnimRigNode animRigRoot = ProcessNode(scene, rootNode);

            animRigRoot.LocalTransform *= Matrix4x4.CreateRotationX(-90 * Maths.DEG2_RAD); // Convert to right handed Z is up.

            Entity = new MeshEntity
            {
                Name         = Name,
                Meshes       = _meshes.ToArray(),
                Animations   = _animations.ToArray(),
                AnimationRig = animRigRoot
            };

            object scaleData = scene.RootNode.Metadata.GetValueOrDefault("UnitScaleFactor").Data;
            var    scaleF    = 1f;

            if (scaleData is float f)
            {
                scaleF = f;
            }
            Entity.Scale = scaleF;
        }