private static void CalculateNormals(MeshContent mesh, bool overwriteExistingNormals)
        {
            Debug.Assert(mesh != null);

            string name = VertexChannelNames.Normal();

            if (!overwriteExistingNormals && mesh.Geometry.All(geometry => geometry.Vertices.Channels.Contains(name)))
            {
                return; // Nothing to do.
            }
            // Accumulate triangle normals at vertices.
            // IMPORTANT: Calculating normals per submesh does not work!
            // - Normals at submesh borders are only correct if we consider adjacent submeshes.
            // - GeometryContent.Positions contains duplicated entries if neighbor triangles do
            //   have the same texture coordinates. MeshContent.Positions are unique (no duplicates).
            var positions = mesh.Positions.Select(p => (Vector3F)p).ToArray();
            var indices   = mesh.Geometry
                            .SelectMany(geometry => geometry.Indices.Select(i => geometry.Vertices.PositionIndices[i]))
                            .ToArray();

            // Calculate vertex normals.
            var normals = DirectXMesh.ComputeNormals(indices, positions, true, VertexNormalAlgorithm.WeightedByAngle);

            // Copy normals to vertex channels.
            foreach (var geometry in mesh.Geometry)
            {
                if (!geometry.Vertices.Channels.Contains(name))
                {
                    geometry.Vertices.Channels.Add <Vector3>(name, null);
                }
                else if (!overwriteExistingNormals)
                {
                    continue;
                }

                var normalChannel   = geometry.Vertices.Channels.Get <Vector3>(name);
                var positionIndices = geometry.Vertices.PositionIndices;
                for (int i = 0; i < normalChannel.Count; i++)
                {
                    normalChannel[i] = (Vector3)normals[positionIndices[i]];
                }
            }
        }
        private static void CalculateTangentFrames(GeometryContent geometry, string textureCoordinateChannelName, string tangentChannelName, string binormalChannelName)
        {
            Debug.Assert(!string.IsNullOrWhiteSpace(textureCoordinateChannelName));

            var indices            = geometry.Indices;
            var positions          = geometry.Vertices.Positions.Select(p => (Vector3F)p).ToArray();
            var normals            = geometry.Vertices.Channels.Get <Vector3>(VertexChannelNames.Normal()).Select(n => (Vector3F)n).ToArray();
            var textureCoordinates = geometry.Vertices.Channels.Get <Vector2>(textureCoordinateChannelName).Select(n => (Vector2F)n).ToArray();

            Vector3F[] tangents;
            Vector3F[] bitangents;
            DirectXMesh.ComputeTangentFrame(indices, positions, normals, textureCoordinates, out tangents, out bitangents);

            if (!string.IsNullOrEmpty(tangentChannelName))
            {
                geometry.Vertices.Channels.Add(tangentChannelName, tangents.Select(t => (Vector3)t));
            }

            if (!string.IsNullOrEmpty(binormalChannelName))
            {
                geometry.Vertices.Channels.Add(binormalChannelName, bitangents.Select(b => (Vector3)b));
            }
        }
        private /*static*/ void OptimizeForCache(IList <Vector3F> positions,  // In: Original positions.
                                                 IList <int> indices,         // In: Original indices. Out: Optimized indices.
                                                 out int[] vertexRemap,       // Maps original vertex location to optimized vertex location.
                                                 out int[] duplicateVertices, // Original locations of duplicate vertices.
                                                 ContentIdentity identity)
        {
            Debug.Assert(positions != null);
            Debug.Assert(indices != null);

#if COMPUTE_VERTEX_CACHE_MISS_RATE
            float acmrOld;
            float atvrOld;
            DirectXMesh.ComputeVertexCacheMissRate(indices, positions.Count, DirectXMesh.OPTFACES_V_DEFAULT, out acmrOld, out atvrOld);
#endif

            int numberOfVertices = positions.Count;

            int[] pointRep;
            int[] adjacency;
            DirectXMesh.GenerateAdjacencyAndPointReps(indices, positions, Numeric.EpsilonF, out pointRep, out adjacency);

            if (!DirectXMesh.Clean(indices, numberOfVertices, adjacency, null, false, out duplicateVertices))
            {
                List <string> validationMessages = new List <string>();
                DirectXMesh.Validate(indices, numberOfVertices, adjacency, MeshValidationOptions.Default, validationMessages);

                string message;
                if (validationMessages.Count == 0)
                {
                    message = "Mesh cleaning failed.";
                }
                else
                {
                    var messageBuilder = new StringBuilder();
                    messageBuilder.AppendLine("Mesh cleaning failed:");
                    foreach (var validationMessage in validationMessages)
                    {
                        messageBuilder.AppendLine(validationMessage);
                    }

                    message = messageBuilder.ToString();
                }

                throw new InvalidContentException(message, identity);
            }

            // Skip DirectXMesh.AttributeSort and DirectXMesh.ReorderIBAndAdjacency.
            // (GeometryContent already sorted.)

            int[] faceRemap;
            DirectXMesh.OptimizeFaces(indices, adjacency, null, out faceRemap);

            DirectXMesh.ReorderIB(indices, faceRemap);

            DirectXMesh.OptimizeVertices(indices, numberOfVertices, out vertexRemap);

            DirectXMesh.FinalizeIB(indices, vertexRemap);

            // Skip DirectXMesh.FinalizeVB.
            // (Needs to be handled by caller.)

            Debug.Assert(vertexRemap.Length == numberOfVertices + duplicateVertices.Length);

#if COMPUTE_VERTEX_CACHE_MISS_RATE
            int   newNumberOfVertices = vertexRemap.Count(i => i != -1);
            float acmrNew;
            float atvrNew;
            DirectXMesh.ComputeVertexCacheMissRate(indices, newNumberOfVertices, DirectXMesh.OPTFACES_V_DEFAULT, out acmrNew, out atvrNew);

            _context.Logger.LogMessage(
                "Mesh optimization: Vertices before {0}, after {1}; ACMR before {2}, after {3}; ATVR before {4}, after {5}",
                numberOfVertices, newNumberOfVertices, acmrOld, acmrNew, atvrOld, atvrNew);
#endif
        }
Ejemplo n.º 4
0
        public void ReadWriteHalfTest(int format, uint redMask, uint greenMask, uint blueMask, uint alphaMask)
        {
            const int  numberOfVertices    = 100;
            DataFormat vertexElementFormat = (DataFormat)format;
            int        bytesPerElement     = DirectXMesh.BytesPerElement(vertexElementFormat);

            Assert.Greater(bytesPerElement, 0);

            var vertexDeclaration = new[]
            {
                new VertexElement(VertexElementSemantic.Position, 0, vertexElementFormat, -1),
                new VertexElement(VertexElementSemantic.Normal, 0, vertexElementFormat, -1)
            };
            var vbAccessor = new VertexBufferAccessor(vertexDeclaration);

            var positions = new Vector4F[numberOfVertices];

            for (int i = 0; i < positions.Length; i++)
            {
                positions[i] = new Vector4F(
                    i / (float)numberOfVertices,
                    (i + 10) / (float)numberOfVertices,
                    (i + 20) / (float)numberOfVertices,
                    (i + 30) / (float)numberOfVertices);
            }

            var normals = new Vector4F[numberOfVertices];

            for (int i = 0; i < normals.Length; i++)
            {
                normals[i] = new Vector4F(
                    (i + 40) / (float)numberOfVertices,
                    (i + 50) / (float)numberOfVertices,
                    (i + 60) / (float)numberOfVertices,
                    (i + 70) / (float)numberOfVertices);
            }

            vbAccessor.SetElements(positions, VertexElementSemantic.Position, 0);
            vbAccessor.SetElements(normals, VertexElementSemantic.Normal, 0);

            byte[] vb;
            int    n;
            int    stride;

            vbAccessor.GetStream(0, out vb, out n, out stride);

            Assert.NotNull(vb);
            Assert.AreEqual(numberOfVertices, n);
            Assert.AreEqual(2 * bytesPerElement, stride);
            Assert.AreEqual(stride * n, vb.Length);

            vbAccessor = new VertexBufferAccessor(vertexDeclaration);
            vbAccessor.SetStream(0, vb, numberOfVertices);

            var positions1 = new Vector4F[numberOfVertices];
            var normals1   = new Vector4F[numberOfVertices];

            vbAccessor.GetElements(positions1, VertexElementSemantic.Position, 0);
            vbAccessor.GetElements(normals1, VertexElementSemantic.Normal, 0);

            for (int i = 0; i < positions.Length; i++)
            {
                Vector4F expected = AsHalf(positions[i], redMask, greenMask, blueMask, alphaMask);
                Assert.AreEqual(expected.X, positions1[i].X);
                Assert.AreEqual(expected.Y, positions1[i].Y);
                Assert.AreEqual(expected.Z, positions1[i].Z);
                Assert.AreEqual(expected.W, positions1[i].W);
            }

            for (int i = 0; i < normals.Length; i++)
            {
                Vector4F expected = AsHalf(normals[i], redMask, greenMask, blueMask, alphaMask);
                Assert.AreEqual(expected.X, normals1[i].X);
                Assert.AreEqual(expected.Y, normals1[i].Y);
                Assert.AreEqual(expected.Z, normals1[i].Z);
                Assert.AreEqual(expected.W, normals1[i].W);
            }
        }