Пример #1
0
        public HeightMapInfoContent(ModelContent model, MeshContent terrainMesh, float terrainScale, int terrainWidth, int terrainLength)
        {
            Model = model;
            if (terrainMesh == null)
            {
                throw new ArgumentNullException("terrainMesh");
            }

            if (terrainWidth <= 0)
            {
                throw new ArgumentOutOfRangeException("terrainWidth");
            }
            if (terrainLength <= 0)
            {
                throw new ArgumentOutOfRangeException("terrainLength");
            }

            TerrainScale = terrainScale;
            Height       = new float[terrainWidth, terrainLength];
            Normals      = new Vector3[terrainWidth, terrainLength];
            GeometryContent item = terrainMesh.Geometry[0];

            for (int i = 0; i < item.Vertices.VertexCount; i++)
            {
                Vector3 vector3 = item.Vertices.Positions[i];
                Vector3 item1   = (Vector3)item.Vertices.Channels[VertexChannelNames.Normal()][i];
                int     x       = (int)(vector3.X / terrainScale + (terrainWidth - 1) / 2f);
                int     z       = (int)(vector3.Z / terrainScale + (terrainLength - 1) / 2f);
                Height[x, z]  = vector3.Y;
                Normals[x, z] = item1;
            }
        }
Пример #2
0
        public static string GetXNAName(VertexAttribute attr)
        {
            switch (attr.usage)
            {
            case COLOR:
                return(VertexChannelNames.Color(0));

            case NORMAL:
                return(VertexChannelNames.Normal());

            case TEX_COORD:
                return(VertexChannelNames.TextureCoordinate(attr.attrIndex));

            case BONE_WEIGHT:
                return(VertexChannelNames.Weights(attr.attrIndex));

            case TANGENT:
                return(VertexChannelNames.Tangent(0));

            case BINORMAL:
                return(VertexChannelNames.Binormal(0));
            }

            return(null);
        }
Пример #3
0
        public void BasicMeshBuilderTest()
        {
            var output = CreateBasicMesh(material1);

            Assert.NotNull(output);
            Assert.NotNull(output.Geometry);
            Assert.NotNull(output.Positions);

            Assert.AreEqual(new Vector3(0, 0, 0), output.Positions[0]);
            Assert.AreEqual(new Vector3(1, 0, 0), output.Positions[1]);
            Assert.AreEqual(new Vector3(1, 1, 1), output.Positions[2]);

            Assert.AreEqual(1, output.Geometry.Count);

            Assert.AreEqual(0, output.Geometry[0].Indices[0]);
            Assert.AreEqual(1, output.Geometry[0].Indices[1]);
            Assert.AreEqual(2, output.Geometry[0].Indices[2]);

            Assert.AreEqual(0, output.Geometry[0].Vertices.PositionIndices[0]);
            Assert.AreEqual(1, output.Geometry[0].Vertices.PositionIndices[1]);
            Assert.AreEqual(2, output.Geometry[0].Vertices.PositionIndices[2]);

            Assert.AreEqual(new Vector3(0, 0, 0), output.Geometry[0].Vertices.Positions[0]);
            Assert.AreEqual(new Vector3(1, 0, 0), output.Geometry[0].Vertices.Positions[1]);
            Assert.AreEqual(new Vector3(1, 1, 1), output.Geometry[0].Vertices.Positions[2]);

            //Check if normals are generated
            Assert.NotNull(output.Geometry[0].Vertices.Channels[VertexChannelNames.Normal(0)]);

            Assert.AreEqual(material1, output.Geometry[0].Material);
            Assert.AreEqual(Matrix.Identity, output.Transform);

            Assert.AreEqual(3, output.Positions.Count);
            Assert.AreEqual("Mesh1", output.Name);
        }
Пример #4
0
        void startMesh(string name)
        {
            meshBuilder = MeshBuilder.StartMesh(name);
            meshBuilder.SwapWindingOrder = true;

            textureCoordinateDataIndex = meshBuilder.CreateVertexChannel <Vector2>(VertexChannelNames.TextureCoordinate(0));
            normalDataIndex            = meshBuilder.CreateVertexChannel <Vector3>(VertexChannelNames.Normal( ));
            positionMap = new int[positions.Count];

            for (int i = 0; i < positions.Count; i++)
            {
                positionMap[i] = meshBuilder.CreatePosition(positions[i]);
            }
        }
Пример #5
0
        /// <summary>
        /// Helper function creates a new geometry object,
        /// and sets it to use our billboard effect.
        /// </summary>
        static GeometryContent CreateVegetationGeometry(string textureFilename,
                                                        float width, float height,
                                                        float windAmount,
                                                        ContentIdentity identity)
        {
            GeometryContent geometry = new GeometryContent();

            // Add the vertex channels needed for our billboard geometry.
            VertexChannelCollection channels = geometry.Vertices.Channels;

            // Add a vertex channel holding normal vectors.
            channels.Add <Vector3>(VertexChannelNames.Normal(), null);

            // Add a vertex channel holding texture coordinates.
            channels.Add <Vector2>(VertexChannelNames.TextureCoordinate(0), null);

            // Add a second texture coordinate channel, holding a per-billboard
            // random number. This is used to make each billboard come out a
            // slightly different size, and to animate at different speeds.
            channels.Add <float>(VertexChannelNames.TextureCoordinate(1), null);

            // Create a material for rendering the billboards.
            EffectMaterialContent material = new EffectMaterialContent();

            // Point the material at our custom billboard effect.
            string directory = Path.GetDirectoryName(identity.SourceFilename);

            string effectFilename = Path.Combine(directory, "Billboard.fx");

            material.Effect = new ExternalReference <EffectContent>(effectFilename);

            // Set the texture to be used by these billboards.
            textureFilename = Path.Combine(directory, textureFilename);

            material.Textures.Add("Texture", new ExternalReference <TextureContent>(textureFilename));

            // Set effect parameters describing the size and
            // wind sensitivity of these billboards.
            material.OpaqueData.Add("BillboardWidth", width);
            material.OpaqueData.Add("BillboardHeight", height);
            material.OpaqueData.Add("WindAmount", windAmount);

            geometry.Material = material;

            return(geometry);
        }
        /// <summary>
        /// This constructor will initialize the height array from the values in the
        /// bitmap. Each pixel in the bitmap corresponds to one entry in the height
        /// array.
        /// </summary>
        public HeightMapInfoContent(MeshContent terrainMesh, float terrainScale,
                                    int terrainWidth, int terrainLength)
        {
            // validate the parameters
            if (terrainMesh == null)
            {
                throw new ArgumentNullException("terrainMesh");
            }
            if (terrainWidth <= 0)
            {
                throw new ArgumentOutOfRangeException("terrainWidth");
            }
            if (terrainLength <= 0)
            {
                throw new ArgumentOutOfRangeException("terrainLength");
            }

            this.terrainScale = terrainScale;

            // create new arrays of the requested size.
            height  = new float[terrainWidth, terrainLength];
            normals = new Vector3[terrainWidth, terrainLength];

            // to fill those arrays, we'll look at the position and normal data
            // contained in the terrainMesh.
            GeometryContent geometry = terrainMesh.Geometry[0];

            // we'll go through each vertex....
            for (int i = 0; i < geometry.Vertices.VertexCount; i++)
            {
                // ... and look up its position and normal.
                Vector3 position = geometry.Vertices.Positions[i];
                Vector3 normal   = (Vector3)geometry.Vertices.Channels
                                   [VertexChannelNames.Normal()][i];

                // from the position's X and Z value, we can tell what X and Y
                // coordinate of the arrays to put the height and normal into.
                int arrayX = (int)
                             ((position.X / terrainScale) + (terrainWidth - 1) / 2.0f);
                int arrayY = (int)
                             ((position.Z / terrainScale) + (terrainLength - 1) / 2.0f);

                height[arrayX, arrayY]  = position.Y;
                normals[arrayX, arrayY] = normal;
            }
        }
        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]];
                }
            }
        }
        /// <summary>
        /// Starts a new mesh and fills it with mesh mapped positions.
        /// </summary>
        /// <param name="name">Name of mesh.</param>
        private void StartMesh(string name)
        {
            meshBuilder = MeshBuilder.StartMesh(name);

            // Obj files need their winding orders swapped
            meshBuilder.SwapWindingOrder = true;

            // Add additional vertex channels for texture coordinates and normals
            textureCoordinateDataIndex = meshBuilder.CreateVertexChannel <Vector2>(
                VertexChannelNames.TextureCoordinate(0));
            normalDataIndex =
                meshBuilder.CreateVertexChannel <Vector3>(VertexChannelNames.Normal());

            // Add each position to this mesh with CreatePosition
            positionMap = new int[positions.Count];
            for (int i = 0; i < positions.Count; i++)
            {
                // positionsMap redirects from the original positions in the order
                // they were read from file to indices returned from CreatePosition
                positionMap[i] = meshBuilder.CreatePosition(positions[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));
            }
        }
Пример #10
0
        private GeometryContent CreateGeometry(MeshContent mesh, Mesh aiMesh)
        {
            var geom = new GeometryContent {
                Material = _materials[aiMesh.MaterialIndex]
            };

            // Vertices
            var baseVertex = mesh.Positions.Count;

            foreach (var vert in aiMesh.Vertices)
            {
                mesh.Positions.Add(ToXna(vert));
            }
            geom.Vertices.AddRange(Enumerable.Range(baseVertex, aiMesh.VertexCount));
            geom.Indices.AddRange(aiMesh.GetIndices());

            if (aiMesh.HasBones)
            {
                var xnaWeights = new List <BoneWeightCollection>();
                for (var i = 0; i < geom.Indices.Count; i++)
                {
                    var list = new BoneWeightCollection();
                    for (var boneIndex = 0; boneIndex < aiMesh.BoneCount; boneIndex++)
                    {
                        var bone = aiMesh.Bones[boneIndex];
                        foreach (var weight in bone.VertexWeights)
                        {
                            if (weight.VertexID != i)
                            {
                                continue;
                            }

                            list.Add(new BoneWeight(bone.Name, weight.Weight));
                        }
                    }
                    if (list.Count > 0)
                    {
                        xnaWeights.Add(list);
                    }
                }

                geom.Vertices.Channels.Add(VertexChannelNames.Weights(0), xnaWeights);
            }

            // Individual channels go here
            if (aiMesh.HasNormals)
            {
                geom.Vertices.Channels.Add(VertexChannelNames.Normal(), ToXna(aiMesh.Normals));
            }

            for (var i = 0; i < aiMesh.TextureCoordinateChannelCount; i++)
            {
                geom.Vertices.Channels.Add(VertexChannelNames.TextureCoordinate(i),
                                           ToXnaTexCoord(aiMesh.TextureCoordinateChannels[i]));
            }

            for (var i = 0; i < aiMesh.VertexColorChannelCount; i++)
            {
                geom.Vertices.Channels.Add(VertexChannelNames.Color(i),
                                           ToXnaColors(aiMesh.VertexColorChannels[i]));
            }

            return(geom);
        }
Пример #11
0
        /// <summary>
        /// Recursive function adds vegetation billboards to all meshes.
        /// </summary>
        void GenerateVegetation(NodeContent node, ContentIdentity identity)
        {
            // First, recurse over any child nodes.
            foreach (NodeContent child in node.Children)
            {
                GenerateVegetation(child, identity);
            }

            // Check whether this node is in fact a mesh.
            MeshContent mesh = node as MeshContent;

            if (mesh != null)
            {
                // Create three new geometry objects, one for each type
                // of billboard that we are going to create. Set different
                // effect parameters to control the size and wind sensitivity
                // for each type of billboard.
                GeometryContent grass = CreateVegetationGeometry("grass.tga", 5, 5, 1, identity);
                GeometryContent trees = CreateVegetationGeometry("tree.tga", 12, 12, 0.5f, identity);
                GeometryContent cats  = CreateVegetationGeometry("cat.tga", 5, 5, 0, identity);

                MeshContent vegetationMesh = new MeshContent {
                    Name = "Billboards"
                };

                // Loop over all the existing geometry in this mesh.
                foreach (GeometryContent geometry in mesh.Geometry)
                {
                    IList <int>     indices   = geometry.Indices;
                    IList <Vector3> positions = geometry.Vertices.Positions;
                    IList <Vector3> normals   = geometry.Vertices.Channels.Get <Vector3>(VertexChannelNames.Normal());

                    // Loop over all the triangles in this piece of geometry.
                    for (int triangle = 0; triangle < indices.Count; triangle += 3)
                    {
                        // Look up the three indices for this triangle.
                        int i1 = indices[triangle];
                        int i2 = indices[triangle + 1];
                        int i3 = indices[triangle + 2];

                        // Create vegetation billboards to cover this triangle.
                        // A more sophisticated implementation would measure the
                        // size of the triangle to work out how many to create,
                        // but we don't bother since we happen to know that all
                        // our triangles are roughly the same size.
                        for (int count = 0; count < BillboardsPerTriangle; count++)
                        {
                            Vector3 position, normal;

                            // Choose a random location on the triangle.
                            PickRandomPoint(positions[i1], positions[i2], positions[i3],
                                            normals[i1], normals[i2], normals[i3],
                                            out position, out normal);

                            // Randomly choose what type of billboard to create.
                            GeometryContent billboardType;

                            if (random.NextDouble() < TreeProbability)
                            {
                                billboardType = trees;

                                // As a special case, force trees to point straight
                                // upward, even if they are growing on a slope.
                                // That's what trees do in real life, after all!
                                normal = Vector3.Up;
                            }
                            else if (random.NextDouble() < CatProbability)
                            {
                                billboardType = cats;
                            }
                            else
                            {
                                billboardType = grass;
                            }

                            // Add a new billboard to the output geometry.
                            GenerateBillboard(vegetationMesh, billboardType, position, normal);
                        }
                    }
                }

                // Add our new billboard geometry to the main mesh.
                vegetationMesh.Geometry.Add(grass);
                vegetationMesh.Geometry.Add(trees);
                vegetationMesh.Geometry.Add(cats);

                mesh.Children.Add(vegetationMesh);
            }
        }
Пример #12
0
        /// <summary>
        /// Helper function adds a single new billboard sprite to the output geometry.
        /// </summary>
        private void GenerateBillboard(MeshContent mesh, GeometryContent geometry,
                                       Vector3 position, Vector3 normal)
        {
            VertexContent           vertices = geometry.Vertices;
            VertexChannelCollection channels = vertices.Channels;

            // First, create a vertex position entry for this billboard. Each
            // billboard is going to be rendered a quad, so we need to create four
            // vertices, but at this point we only have a single position that is
            // shared by all the vertices. The real position of each vertex will be
            // computed on the fly in the vertex shader, thus allowing us to
            // implement effects like making the billboard rotate to always face the
            // camera, and sway in the wind. As input the vertex shader only wants to
            // know the center point of the billboard, and that is the same for all
            // the vertices, so only a single position is needed here.
            int positionIndex = mesh.Positions.Count;

            mesh.Positions.Add(position);

            // Second, create the four vertices, all referencing the same position.
            int index = vertices.PositionIndices.Count;

            for (int i = 0; i < 4; i++)
            {
                vertices.Add(positionIndex);
            }

            // Third, add normal data for each of the four vertices. A normal for a
            // billboard is kind of a silly thing to define, since we are using a
            // 2D sprite to fake a complex 3D object that would in reality have many
            // different normals across its surface. Here we are just using a copy
            // of the normal from the ground underneath the billboard, which can be
            // used in our lighting computation to make the vegetation darker or
            // lighter depending on the lighting of the underlying landscape.
            VertexChannel <Vector3> normals;

            normals = channels.Get <Vector3>(VertexChannelNames.Normal());

            for (int i = 0; i < 4; i++)
            {
                normals[index + i] = normal;
            }

            // Fourth, add texture coordinates.
            VertexChannel <Vector2> texCoords;

            texCoords = channels.Get <Vector2>(VertexChannelNames.TextureCoordinate(0));

            texCoords[index + 0] = new Vector2(0, 0);
            texCoords[index + 1] = new Vector2(1, 0);
            texCoords[index + 2] = new Vector2(1, 1);
            texCoords[index + 3] = new Vector2(0, 1);

            // Fifth, add a per-billboard random value, which is the same for
            // all four vertices. This is used in the vertex shader to make
            // each billboard a slightly different size, and to be affected
            // differently by the wind animation.
            float randomValue = (float)random.NextDouble() * 2 - 1;

            VertexChannel <float> randomValues;

            randomValues = channels.Get <float>(VertexChannelNames.TextureCoordinate(1));

            for (int i = 0; i < 4; i++)
            {
                randomValues[index + i] = randomValue;
            }

            // Sixth and finally, add indices defining the pair of
            // triangles that will be used to render the billboard.
            geometry.Indices.Add(index + 0);
            geometry.Indices.Add(index + 1);
            geometry.Indices.Add(index + 2);

            geometry.Indices.Add(index + 0);
            geometry.Indices.Add(index + 2);
            geometry.Indices.Add(index + 3);
        }
Пример #13
0
        private List <DRMorphTargetContent> BuildMorphTargets(GeometryContent geometry, List <MeshContent> inputMorphTargets, int index)
        {
            int[] vertexReorderMap = _vertexReorderMaps[index];

            var morphTargets = new List <DRMorphTargetContent>();

            foreach (var inputMorphTarget in inputMorphTargets)
            {
                int numberOfVertices = geometry.Vertices.VertexCount;
                var morphGeometry    = inputMorphTarget.Geometry[index];

                // Copy relative positions and normals into vertex buffer.
                var       positions = morphGeometry.Vertices.Positions;
                var       normals   = morphGeometry.Vertices.Channels.Get <Vector3>(VertexChannelNames.Normal());
                Vector3[] data      = new Vector3[numberOfVertices * 2];
                for (int i = 0; i < numberOfVertices; i++)
                {
                    int originalIndex = vertexReorderMap[i];
                    data[2 * i]     = positions[originalIndex];
                    data[2 * i + 1] = normals[originalIndex];
                }

                // Determine if morph target is empty.
                bool isEmpty = true;
                for (int i = 0; i < data.Length; i++)
                {
                    // File formats and preprocessing can introduce some inaccuracies.
                    // --> Use a relative large epsilon. (The default Numeric.EpsilonF is too small.)
                    const float epsilon = 1e-4f;
                    if (!Numeric.IsZero(data[i].LengthSquared(), epsilon * epsilon))
                    {
                        Debug.Write(string.Format(
                                        CultureInfo.InvariantCulture,
                                        "Morph target \"{0}\", submesh index {1}: Position/normal delta is {2}.",
                                        inputMorphTarget.Name, index, data[i].Length()));

                        isEmpty = false;
                        break;
                    }
                }

                if (!isEmpty)
                {
                    // (Note: VertexStride is set explicitly in CreateMorphTargetVertexBuffer().)
                    // ReSharper disable once PossibleInvalidOperationException
                    int vertexOffset = _morphTargetVertexBuffer.VertexData.Length / _morphTargetVertexBuffer.VertexDeclaration.VertexStride.Value;
                    _morphTargetVertexBuffer.Write(
                        _morphTargetVertexBuffer.VertexData.Length,
                        12, // The size of one Vector3 in data is 12.
                        data);

                    morphTargets.Add(new DRMorphTargetContent
                    {
                        Name         = inputMorphTarget.Name,
                        VertexBuffer = _morphTargetVertexBuffer,
                        StartVertex  = vertexOffset,
                    });
                }
            }

            return((morphTargets.Count > 0) ? morphTargets : null);
        }
Пример #14
0
        private ModelMeshContent ProcessMesh(MeshContent mesh, ModelBoneContent parent, ContentProcessorContext context)
        {
            var parts        = new List <ModelMeshPartContent>();
            var vertexBuffer = new VertexBufferContent();
            var indexBuffer  = new IndexCollection();

            if (GenerateTangentFrames)
            {
                context.Logger.LogMessage("Generating tangent frames.");
                foreach (GeometryContent geom in mesh.Geometry)
                {
                    if (!geom.Vertices.Channels.Contains(VertexChannelNames.Normal(0)))
                    {
                        MeshHelper.CalculateNormals(geom, true);
                    }

                    if (!geom.Vertices.Channels.Contains(VertexChannelNames.Tangent(0)) ||
                        !geom.Vertices.Channels.Contains(VertexChannelNames.Binormal(0)))
                    {
                        MeshHelper.CalculateTangentFrames(geom, VertexChannelNames.TextureCoordinate(0), VertexChannelNames.Tangent(0),
                                                          VertexChannelNames.Binormal(0));
                    }
                }
            }

            var startVertex = 0;

            foreach (var geometry in mesh.Geometry)
            {
                var vertices    = geometry.Vertices;
                var vertexCount = vertices.VertexCount;
                ModelMeshPartContent partContent;
                if (vertexCount == 0)
                {
                    partContent = new ModelMeshPartContent();
                }
                else
                {
                    var geomBuffer = geometry.Vertices.CreateVertexBuffer();
                    vertexBuffer.Write(vertexBuffer.VertexData.Length, 1, geomBuffer.VertexData);

                    var startIndex = indexBuffer.Count;
                    indexBuffer.AddRange(geometry.Indices);

                    partContent = new ModelMeshPartContent(vertexBuffer, indexBuffer, startVertex, vertexCount, startIndex, geometry.Indices.Count / 3);

                    // Geoms are supposed to all have the same decl, so just steal one of these
                    vertexBuffer.VertexDeclaration = geomBuffer.VertexDeclaration;

                    startVertex += vertexCount;
                }

                partContent.Material = geometry.Material;
                parts.Add(partContent);
            }

            var bounds = new BoundingSphere();

            if (mesh.Positions.Count > 0)
            {
                bounds = BoundingSphere.CreateFromPoints(mesh.Positions);
            }

            return(new ModelMeshContent(mesh.Name, mesh, parent, bounds, parts));
        }
Пример #15
0
        public override NodeContent Import(string filename, ContentImporterContext context)
        {
            var content = new NodeContent();

            var reader  = XmlReader.Create(filename);
            var xmlMesh = (XmlMesh) new XmlSerializer(typeof(XmlMesh)).Deserialize(reader);

            reader.Close();

            reader = XmlReader.Create(Path.Combine(Path.GetDirectoryName(filename), xmlMesh.SkeletonLink.Name + ".xml"));
            var xmlSkeleton = (XmlSkeleton) new XmlSerializer(typeof(XmlSkeleton)).Deserialize(reader);

            reader.Close();

            context.Logger.LogImportantMessage("Bones: " + xmlSkeleton.Bones.Length.ToString());

            var bones = new Dictionary <string, BoneContent>();

            foreach (var xmlBone in xmlSkeleton.Bones)
            {
                context.Logger.LogImportantMessage("{0}", "-- " + xmlBone.Name + ": " + xmlBone.Position.AsVector3().ToString() + ", " + xmlBone.Rotation.Angle.ToString() + "/" + xmlBone.Rotation.Axis.AsVector3().ToString());
                var boneContent = new BoneContent()
                {
                    Name      = xmlBone.Name,
                    Transform =
                        Matrix.CreateFromAxisAngle(xmlBone.Rotation.Axis.AsVector3(), xmlBone.Rotation.Angle) *
                        Matrix.CreateTranslation(xmlBone.Position.AsVector3())
                };
                bones.Add(xmlBone.Name, boneContent);
            }

            foreach (var boneParent in xmlSkeleton.BoneParents)
            {
                var parent = bones[boneParent.Parent];
                var bone   = bones[boneParent.Bone];
                parent.Children.Add(bone);
            }

            var rootBone = bones.Single(x => x.Value.Parent == null);

            content.Children.Add(rootBone.Value);

            context.Logger.LogImportantMessage("Submeshes: " + xmlMesh.SubMeshes.Length.ToString());

            //context.AddDependency(Path.GetFullPath("HUM_M.MATERIAL"));
            //var materialFile = File.ReadAllText("HUM_M.MATERIAL");
            ////context.Logger.LogImportantMessage("{0}", materialFile);

            foreach (var xmlSubMesh in xmlMesh.SubMeshes)
            {
                context.Logger.LogImportantMessage("Submesh: " + xmlSubMesh.Material);
                context.Logger.LogImportantMessage("-- Faces: " + xmlSubMesh.Faces.Length.ToString());
                if (xmlSubMesh.UseSharedGeometry)
                {
                    context.Logger.LogImportantMessage("-- Uses Shared Geometry");
                }
                else
                {
                    context.Logger.LogImportantMessage("-- Vertexbuffers: " + xmlSubMesh.Geometry.VertexBuffers.Length.ToString());
                    context.Logger.LogImportantMessage("-- Vertices (0): " + xmlSubMesh.Geometry.VertexBuffers[0].Vertices.Length.ToString());
                    context.Logger.LogImportantMessage("-- Vertices (1): " + xmlSubMesh.Geometry.VertexBuffers[1].Vertices.Length.ToString());
                }

                var builder = MeshBuilder.StartMesh(xmlSubMesh.Material);

                //if (xmlSubMesh.Material == "Hum_M/Chest")
                //    builder.SetMaterial(new SkinnedMaterialContent { Texture = new ExternalReference<TextureContent>("TL2_ARMORTEST_CHEST.png") });
                //else if (xmlSubMesh.Material == "Hum_M/MidLeg")
                //    builder.SetMaterial(new SkinnedMaterialContent { Texture = new ExternalReference<TextureContent>("TL2_ARMORTEST_PANTS.png") });
                //else
                builder.SetMaterial(new SkinnedMaterialContent {
                    Texture = new ExternalReference <TextureContent>("Fiend\\FIEND.dds")
                });

                var normalChannel  = builder.CreateVertexChannel <Vector3>(VertexChannelNames.Normal());
                var uvChannel      = builder.CreateVertexChannel <Vector2>(VertexChannelNames.TextureCoordinate(0));
                var weightsChannel = builder.CreateVertexChannel <BoneWeightCollection>(VertexChannelNames.Weights());

                var geometry = xmlSubMesh.Geometry;
                if (xmlSubMesh.UseSharedGeometry)
                {
                    geometry = xmlMesh.SharedGeometry;
                }

                foreach (var vertex in geometry.VertexBuffers[0].Vertices)
                {
                    builder.CreatePosition(vertex.Position.AsVector3());
                }

                foreach (var face in xmlSubMesh.Faces)
                {
                    AddTriangleVertex(builder, xmlMesh, xmlSubMesh, xmlSkeleton, face.Vertex1, normalChannel, uvChannel, weightsChannel);
                    AddTriangleVertex(builder, xmlMesh, xmlSubMesh, xmlSkeleton, face.Vertex2, normalChannel, uvChannel, weightsChannel);
                    AddTriangleVertex(builder, xmlMesh, xmlSubMesh, xmlSkeleton, face.Vertex3, normalChannel, uvChannel, weightsChannel);
                }

                content.Children.Add(builder.FinishMesh());
            }

            return(content);
        }
Пример #16
0
        internal Mesh(PopulatedStructure mesh, BlenderFile file)
        {
            FileBlock materialArray;

            int pointerSize = mesh["mat"].Size;

            Name = new string(mesh["id.name"].GetValueAsCharArray()).Split('\0')[0].Substring(2);
            ulong mat = mesh["mat"].GetValueAsPointer();

            if (mat == 0 || (materialArray = file.GetBlockByAddress(mat)).Size % pointerSize != 0)
            {
                Materials = new Material[0];
            }
            else
            {
                int count = materialArray.Size % pointerSize;
                Materials = new Material[count];
                for (int i = 0; i < count; i++)
                {
                    Materials[i] = Material.GetOrCreateMaterial(
                        file,
                        file.GetStructuresByAddress(
                            pointerSize == 4 ? BitConverter.ToUInt32(materialArray.Data, count * pointerSize) :
                            BitConverter.ToUInt64(materialArray.Data, count * pointerSize)
                            )[0]
                        );
                }
            }
            float[] vectorTemp = mesh["loc"].GetValueAsFloatArray();
            Location   = new Vector3(vectorTemp[0], vectorTemp[1], vectorTemp[2]);
            vectorTemp = mesh["rot"].GetValueAsFloatArray();
            Rotation   = new Vector3(vectorTemp[0], vectorTemp[1], vectorTemp[2]);
            vectorTemp = mesh["size"].GetValueAsFloatArray();
            Size       = new Vector3(vectorTemp[0], vectorTemp[1], vectorTemp[2]);

            MeshBuilder primordialMesh = MeshBuilder.StartMesh(Name);

            // both structures use the same vertex structure
            List <Vector3> verts = new List <Vector3>();
            List <short[]> unconvertedNormals = new List <short[]>();

            foreach (PopulatedStructure s in file.GetStructuresByAddress(mesh["mvert"].GetValueAsPointer()))
            {
                float[] vector = s["co"].GetValueAsFloatArray();
                unconvertedNormals.Add(s["no"].GetValueAsShortArray());
                verts.Add(new Vector3(vector[0], vector[1], vector[2]));
            }
            List <Vector3> normals = convertNormals(unconvertedNormals);

            VertexPositionNormalTexture[] vertices;
            BasicMaterialContent          bmc;

            // todo: not yet sure which format versions of Blender between 2.62 and 2.65 use.
            if (float.Parse(file.VersionNumber) >= 2.66f) // uses edges, loops, and polys (Blender 2.66+)
            {
                vertices = loadNewModel(file, mesh, verts, normals, out bmc);
            }
            else // uses MFace (Blender 2.49-2.61)
            {
                vertices = loadOldModel(file, mesh, verts, normals, out bmc);
            }

            MeshBuilder mb = MeshBuilder.StartMesh(Name);

            foreach (VertexPositionNormalTexture v in vertices)
            {
                mb.CreatePosition(v.Position);
            }
            int uvChannel     = mb.CreateVertexChannel <Vector2>(VertexChannelNames.TextureCoordinate(0));
            int normalChannel = mb.CreateVertexChannel <Vector3>(VertexChannelNames.Normal());
            int j             = 0;

            foreach (VertexPositionNormalTexture v in vertices)
            {
                mb.SetVertexChannelData(uvChannel, v.TextureCoordinate);
                mb.SetVertexChannelData(normalChannel, v.Normal);

                mb.AddTriangleVertex(j++);
            }
        }
        private MeshContent ExtractMesh(aiMesh aiMesh)
        {
            if (!String.IsNullOrEmpty(aiMesh.mName.Data))
            {
                log("modelname " + aiMesh.mName.Data);
                meshBuilder = MeshBuilder.StartMesh(aiMesh.mName.Data);
            }
            else
            {
                meshBuilder = MeshBuilder.StartMesh(Path.GetFileNameWithoutExtension(filename));
            }

            if (!aiMesh.HasPositions())
            {
                throw new Exception("MOdel does not have Position");
            }

            // Add additional vertex channels for texture coordinates and normals
            if (aiMesh.HasTextureCoords(0))
            {
                textureCoordinateDataIndex = meshBuilder.CreateVertexChannel <Vector2>(VertexChannelNames.TextureCoordinate(0));
            }
            else if (aiMesh.HasVertexColors(0))
            {
                colorCoordinateDataIndex = meshBuilder.CreateVertexChannel <Vector4>(VertexChannelNames.Color(0));
            }
            if (aiMesh.HasNormals())
            {
                normalDataIndex = meshBuilder.CreateVertexChannel <Vector3>(VertexChannelNames.Normal());
            }
            if (aiMesh.HasTangentsAndBitangents())
            {
                tangentDataIndex  = meshBuilder.CreateVertexChannel <Vector3>(VertexChannelNames.Tangent(0));
                binormalDataIndex = meshBuilder.CreateVertexChannel <Vector3>(VertexChannelNames.Binormal(0));
            }
            if (aiMesh.HasBones())
            {
                boneDataIndex = meshBuilder.CreateVertexChannel <BoneWeightCollection>(VertexChannelNames.Weights(0));
            }

            var numFaces           = (int)aiMesh.mNumFaces;
            var numVertices        = (int)aiMesh.mNumVertices;
            var aiPositions        = aiMesh.mVertices;
            var aiNormals          = aiMesh.mNormals;
            var aiTextureCoordsAll = aiMesh.mTextureCoords;
            var aiTextureCoords    = (aiTextureCoordsAll != null) ? aiTextureCoordsAll[0] : null;

            for (int j = 0; j < aiMesh.mNumVertices; j++)
            {
                meshBuilder.CreatePosition(aiMesh.mVertices[j].x, aiMesh.mVertices[j].y, aiMesh.mVertices[j].z);
            }

            meshBuilder.SetMaterial(GetMaterial(aiMesh));

            var aiFaces   = aiMesh.mFaces;
            var dxIndices = new uint[numFaces * 3];

            for (int k = 0; k < numFaces; ++k)
            {
                var aiFace    = aiFaces[k];
                var aiIndices = aiFace.mIndices;
                for (int j = 0; j < 3; ++j)
                {
                    int index = (int)aiIndices[j];
                    if (aiMesh.HasTextureCoords(0))
                    {
                        meshBuilder.SetVertexChannelData(textureCoordinateDataIndex, new Vector2(aiMesh.mTextureCoords[0][index].x, aiMesh.mTextureCoords[0][index].y));
                    }
                    else if (aiMesh.HasVertexColors(0))
                    {
                        meshBuilder.SetVertexChannelData(colorCoordinateDataIndex, new Vector4(aiMesh.mColors[0][index].r, aiMesh.mColors[0][index].g, aiMesh.mColors[0][index].b, aiMesh.mColors[0][index].a));
                    }
                    if (aiMesh.HasNormals())
                    {
                        meshBuilder.SetVertexChannelData(normalDataIndex, new Vector3(aiMesh.mNormals[index].x, aiMesh.mNormals[index].y, aiMesh.mNormals[index].z));
                    }

                    if (aiMesh.HasTangentsAndBitangents())
                    {
                        meshBuilder.SetVertexChannelData(tangentDataIndex, new Vector3(aiMesh.mTangents[index].x, aiMesh.mTangents[index].y, aiMesh.mTangents[index].z));
                        meshBuilder.SetVertexChannelData(binormalDataIndex, new Vector3(aiMesh.mBitangents[index].x, aiMesh.mBitangents[index].y, aiMesh.mBitangents[index].z));
                    }
                    if (aiMesh.HasBones())
                    {
                        BoneWeightCollection BoneWeightCollection = new BoneWeightCollection();
                        if (wbone.ContainsKey(index))
                        {
                            foreach (var item in wbone[index])
                            {
                                BoneWeightCollection.Add(new BoneWeight(item.Key, item.Value));
                            }
                        }
                        meshBuilder.SetVertexChannelData(boneDataIndex, BoneWeightCollection);
                    }

                    meshBuilder.AddTriangleVertex(index);
                }
            }

            MeshContent meshContent = meshBuilder.FinishMesh();

            return(meshContent);
        }
Пример #18
0
        public override NodeContent Import(string filename, ContentImporterContext context)
        {
            context.Logger.LogMessage("Importing H3D file: {0}", filename);

            _identity = new ContentIdentity(filename, GetType().Name);
            _rootNode = new NodeContent()
            {
                Identity = _identity, Name = "RootNode"
            };

            var scene = FormatIdentifier.IdentifyAndOpen(filename);
            var model = scene.Models[0];

            if (!scene.Textures.Any())
            {
                var path = Path.Combine(Path.GetDirectoryName(filename), $"{Path.GetFileNameWithoutExtension(filename)}@Textures{Path.GetExtension(filename)}");
                if (File.Exists(path))
                {
                    context.Logger.LogMessage($"Found texture file {path}. Loading data...");
                    scene.Merge(FormatIdentifier.IdentifyAndOpen(path, model.Skeleton));
                }
                else
                {
                    context.Logger.LogMessage($"Couldn't find texture file {path}!");
                }
            }

            // Textures
            var textures = new Dictionary <string, Texture2DContent>();

            foreach (var texture in scene.Textures)
            {
                var bitmapContent = new PixelBitmapContent <Color>(texture.Width, texture.Height)
                {
                    Identity = _identity,
                    Name     = texture.Name
                };
                bitmapContent.SetPixelData(texture.ToRGBA());

                var textureContent = new Texture2DContent()
                {
                    Identity = _identity,
                    Name     = texture.Name
                };
                textureContent.Faces[0].Add(bitmapContent);
                textures.Add(textureContent.Name, textureContent);
            }

            // Materials
            var materials = new Dictionary <string, H3DMaterialContent>();

            foreach (var material in model.Materials)
            {
#if DEBUG
                var hlslCode = new HLSLShaderGenerator(material.MaterialParams)
                {
                    BoneCount = model.Skeleton.Count
                }.GetShader();
                var glslCode = new GLSLFragmentShaderGenerator(material.MaterialParams).GetFragShader();
#endif
                var materialContent = new H3DMaterialContent()
                {
                    Identity = _identity,
                    Name     = material.Name,

                    Effect = new EffectContent
                    {
                        Identity   = _identity,
                        Name       = "H3DEffect",
                        EffectCode = new HLSLShaderGenerator(material.MaterialParams)
                        {
                            BoneCount = model.Skeleton.Count
                        }.GetShader()
                    },
                    Material = material.Name,

                    FaceCulling = (H3DFaceCulling?)material.MaterialParams.FaceCulling,

                    EmissionColor      = material.MaterialParams.EmissionColor.ToXNA(),
                    AmbientColor       = material.MaterialParams.AmbientColor.ToXNA(),
                    DiffuseColor       = material.MaterialParams.DiffuseColor.ToXNA(),
                    Specular0Color     = material.MaterialParams.Specular0Color.ToXNA(),
                    Specular1Color     = material.MaterialParams.Specular1Color.ToXNA(),
                    Constant0Color     = material.MaterialParams.Constant0Color.ToXNA(),
                    Constant1Color     = material.MaterialParams.Constant1Color.ToXNA(),
                    Constant2Color     = material.MaterialParams.Constant2Color.ToXNA(),
                    Constant3Color     = material.MaterialParams.Constant3Color.ToXNA(),
                    Constant4Color     = material.MaterialParams.Constant4Color.ToXNA(),
                    Constant5Color     = material.MaterialParams.Constant5Color.ToXNA(),
                    BlendColor         = material.MaterialParams.BlendColor.ToXNA(),
                    DepthBufferRead    = material.MaterialParams.DepthBufferRead,
                    DepthBufferWrite   = material.MaterialParams.DepthBufferWrite,
                    StencilBufferRead  = material.MaterialParams.StencilBufferRead,
                    StencilBufferWrite = material.MaterialParams.StencilBufferWrite,
                };

                var texCount = 0;
                if (material.EnabledTextures[0])
                {
                    texCount++;
                }
                if (material.EnabledTextures[1])
                {
                    texCount++;
                }
                if (material.EnabledTextures[2])
                {
                    texCount++;
                }
                materialContent.TextureList = new Texture2DContent[texCount];
                if (material.EnabledTextures[0])
                {
                    materialContent.TextureList[0] = textures[material.Texture0Name];
                }
                if (material.EnabledTextures[1])
                {
                    materialContent.TextureList[1] = textures[material.Texture1Name];
                }
                if (material.EnabledTextures[2])
                {
                    materialContent.TextureList[2] = textures[material.Texture2Name];
                }

                materialContent.TextureSamplerSettings = material.TextureMappers.Select(tm => new TextureSamplerSettings()
                {
                    WrapU     = tm.WrapU.ToXNAWrap(),
                    WrapV     = tm.WrapV.ToXNAWrap(),
                    MagFilter = (TextureSamplerSettings.TextureMagFilter)tm.MagFilter,
                    MinFilter = (TextureSamplerSettings.TextureMinFilter)tm.MinFilter
                }).ToArray();

                materials.Add(material.Name, materialContent);
            }

            // Geometry
            var meshes = new List <MeshContent>();
            for (var i = 0; i < model.Meshes.Count; i++)
            {
                var modelMesh = model.Meshes[i];

                if (modelMesh.Type == H3DMeshType.Silhouette)
                {
                    continue;
                }

                var mesh = new MeshContent()
                {
                    Identity = _identity,
                    Name     = $"{model.Materials[modelMesh.MaterialIndex].Name}_node{i}",
                };
                var geometry = new GeometryContent
                {
                    Identity = _identity,
                    Material = materials[model.Materials[modelMesh.MaterialIndex].Name]
                };
                var vertices   = GetWorldSpaceVertices(model.Skeleton, modelMesh);
                var baseVertex = mesh.Positions.Count;
                foreach (var vertex in vertices)
                {
                    mesh.Positions.Add(vertex.Position.ToVector3());
                }
                geometry.Vertices.AddRange(Enumerable.Range(baseVertex, vertices.Length));

                foreach (var attribute in modelMesh.Attributes)
                {
                    if (attribute.Name >= PICAAttributeName.BoneIndex)
                    {
                        continue;
                    }

                    switch (attribute.Name)
                    {
                    case PICAAttributeName.Position: break;     // Already added

                    case PICAAttributeName.Normal:
                        geometry.Vertices.Channels.Add(VertexChannelNames.Normal(0), vertices.Select(vertex => vertex.Normal.ToVector3()));
                        break;

                    case PICAAttributeName.Tangent:
                        geometry.Vertices.Channels.Add(VertexChannelNames.Tangent(0), vertices.Select(vertex => vertex.Tangent.ToVector3()));
                        break;

                    case PICAAttributeName.Color:
                        geometry.Vertices.Channels.Add(VertexChannelNames.Color(0), vertices.Select(vertex => vertex.Color.ToColor()));
                        break;

                    case PICAAttributeName.TexCoord0:
                        geometry.Vertices.Channels.Add(VertexChannelNames.TextureCoordinate(0), vertices.Select(vertex => vertex.TexCoord0.ToVector2().ToUV()));
                        break;

                    case PICAAttributeName.TexCoord1:
                        geometry.Vertices.Channels.Add(VertexChannelNames.TextureCoordinate(1), vertices.Select(vertex => vertex.TexCoord1.ToVector2().ToUV()));
                        break;

                    case PICAAttributeName.TexCoord2:
                        geometry.Vertices.Channels.Add(VertexChannelNames.TextureCoordinate(2), vertices.Select(vertex => vertex.TexCoord2.ToVector2().ToUV()));
                        break;
                    }
                }

                var vertexOffset = 0;
                var xnaWeights   = new List <BoneWeightCollection>();
                foreach (var modelSubMesh in modelMesh.SubMeshes)
                {
                    geometry.Indices.AddRange(modelSubMesh.Indices.Select(index => (int)index));

                    var vertexCount     = modelSubMesh.MaxIndex + 1 - vertexOffset;
                    var subMeshVertices = vertices.Skip(vertexOffset).Take(vertexCount).ToList();

                    if (modelSubMesh.Skinning == H3DSubMeshSkinning.Smooth)
                    {
                        foreach (var vertex in subMeshVertices)
                        {
                            var list = new BoneWeightCollection();
                            for (var index = 0; index < 4; index++)
                            {
                                var bIndex = vertex.Indices[index];
                                var weight = vertex.Weights[index];

                                if (weight == 0)
                                {
                                    break;
                                }

                                if (bIndex < modelSubMesh.BoneIndicesCount && bIndex > -1)
                                {
                                    bIndex = modelSubMesh.BoneIndices[bIndex];
                                }
                                else
                                {
                                    bIndex = 0;
                                }

                                list.Add(new BoneWeight(model.Skeleton[bIndex].Name, weight));
                            }
                            xnaWeights.Add(list);
                        }
                    }
                    else
                    {
                        foreach (var vertex in vertices)
                        {
                            var bIndex = vertex.Indices[0];

                            if (bIndex < modelSubMesh.BoneIndices.Length && bIndex > -1)
                            {
                                bIndex = modelSubMesh.BoneIndices[bIndex];
                            }
                            else
                            {
                                bIndex = 0;
                            }

                            xnaWeights.Add(new BoneWeightCollection()
                            {
                                new BoneWeight(model.Skeleton[bIndex].Name, 0)
                            });
                        }
                    }
                    vertexOffset += vertexCount;
                }
                geometry.Vertices.Channels.Add(VertexChannelNames.Weights(0), xnaWeights);
                mesh.Geometry.Add(geometry);
                meshes.Add(mesh);
            }

            foreach (var mesh in meshes)
            {
                _rootNode.Children.Add(mesh);
            }

            var rootBone = ImportBones(model);
            _rootNode.Children.Add(rootBone);

            if (!scene.SkeletalAnimations.Any())
            {
                var path = Path.Combine(Path.GetDirectoryName(filename), $"{Path.GetFileNameWithoutExtension(filename)}@Animations{Path.GetExtension(filename)}");
                if (File.Exists(path))
                {
                    context.Logger.LogMessage($"Found animation file {path}. Loading data...");
                    scene.Merge(FormatIdentifier.IdentifyAndOpen(path, model.Skeleton));
                }
                else
                {
                    context.Logger.LogMessage($"Couldn't find animation file {path}!");
                }
            }

            foreach (var animation in ImportSkeletalAnimations(scene))
            {
                rootBone.Animations.Add(animation.Name, animation);
            }

            foreach (var animation in ImportMaterialAnimations(scene))
            {
                _rootNode.Children.Add(animation);
            }

            return(_rootNode);
        }
Пример #19
0
        public override NodeContent Import(string filename, ContentImporterContext context)
        {
            ContentIdentity identity = new ContentIdentity(filename, GetType().Name);

            const int MAX_BONE_WEIGHTS             = 4;
            VertexBoneWeightLimitConfig boneConfig = new VertexBoneWeightLimitConfig(MAX_BONE_WEIGHTS);

            AssimpImporter importer = new AssimpImporter();

            importer.SetConfig(boneConfig);

            importer.AttachLogStream(new LogStream((msg, userData) => context.Logger.LogMessage(msg)));
            Scene scene = importer.ImportFile(filename,
                                              PostProcessSteps.FlipUVs |
                                              PostProcessSteps.JoinIdenticalVertices |
                                              PostProcessSteps.Triangulate |
                                              PostProcessSteps.SortByPrimitiveType |
                                              PostProcessSteps.FindInvalidData |
                                              PostProcessSteps.LimitBoneWeights |
                                              PostProcessSteps.FixInFacingNormals);


            // Root node
            NodeContent rootNode = new NodeContent
            {
                Name      = scene.RootNode.Name,
                Identity  = identity,
                Transform = Matrix.Transpose(ToXna(scene.RootNode.Transform))
            };


            // Materials
            MaterialContent[] materials = new MaterialContent[scene.MaterialCount];

            for (int m = 0; m < scene.MaterialCount; m++)
            {
                materials[m] = new BasicMaterialContent();

                materials[m].Identity = identity;
                // For some reason, there is all kinds of nasty junk in this string:
                materials[m].Name = CleanInput(scene.Materials[m].Name);

                for (int t = 0; t < scene.Materials[m].GetTextureCount(TextureType.Diffuse); t++)
                {
                    TextureSlot diffuseMap = scene.Materials[m].GetTexture(TextureType.Diffuse, t);
                    if (!String.IsNullOrEmpty(diffuseMap.FilePath))
                    {
                        materials[m].Textures.Add("Texture" + (t > 0 ? t.ToString() : ""),
                                                  new ExternalReference <TextureContent>(diffuseMap.FilePath, identity));
                    }
                }
            }


            // Bones

            // We find 'mesh container' nodes with the best names for those meshes while looking for the bones,
            // and will need them later when we create the MeshContents. I have a feeling that this won't work
            // in general, and may need to be made more robust.
            Dictionary <Mesh, string>      meshNames     = new Dictionary <Mesh, string>();
            Dictionary <Node, BoneContent> nodeToBoneMap = new Dictionary <Node, BoneContent>();
            BoneContent skeleton = null;    // The root bone for the model.

            List <Node> hierarchyNodes = scene.RootNode.Children.SelectDeep(n => n.Children).ToList();

            foreach (Node node in hierarchyNodes)
            {
                BoneContent bone = new BoneContent
                {
                    Name      = node.Name,
                    Transform = Matrix.Transpose(ToXna(node.Transform))
                };


                if (node.MeshIndices != null)
                {
                    // This node is a 'mesh container' instead of a bone, so we only care about extracting the name of the mesh.
                    foreach (int meshIndex in node.MeshIndices)
                    {
                        if (!meshNames.ContainsKey(scene.Meshes[meshIndex]))
                        {
                            meshNames.Add(scene.Meshes[meshIndex], node.Name);
                        }
                    }
                }
                else if (node.Parent == scene.RootNode)
                {
                    if (skeleton == null)
                    {
                        // This will be our skeleton so put the animations here:
                        if (scene.HasAnimations)
                        {
                            foreach (Animation assimpAnim in scene.Animations)
                            {
                                if (assimpAnim.HasNodeAnimations)
                                {
                                    AnimationContent newAnim = new AnimationContent();
                                    newAnim.Identity = identity;
                                    newAnim.Duration = TimeSpan.FromSeconds(assimpAnim.DurationInTicks / assimpAnim.TicksPerSecond);
                                    newAnim.Name     = assimpAnim.Name;

                                    foreach (NodeAnimationChannel nac in assimpAnim.NodeAnimationChannels)
                                    {
                                        Node animatedNode = hierarchyNodes.Find(n => n.Name == nac.NodeName);

                                        AnimationChannel newChan = BuildAnimtionChannel(animatedNode, nac);

                                        newAnim.Channels.Add(nac.NodeName, newChan);
                                    }

                                    if (String.IsNullOrEmpty(assimpAnim.Name))
                                    {
                                        bone.Animations.Add("SkelematorNoAnimationName", newAnim);
                                    }
                                    else
                                    {
                                        bone.Animations.Add(assimpAnim.Name, newAnim);
                                    }
                                }
                            }
                        }
                        rootNode.Children.Add(bone);
                        skeleton = bone;
                    }
                    else
                    {
                        context.Logger.LogWarning(null, identity, "Found multiple skeletons in the model, throwing extras away...");
                    }
                }
                else
                {
                    BoneContent parent = nodeToBoneMap[node.Parent];
                    parent.Children.Add(bone);
                }

                nodeToBoneMap.Add(node, bone);
            }


            // Meshes
            Dictionary <Mesh, MeshContent> meshes = new Dictionary <Mesh, MeshContent>();

            foreach (Mesh sceneMesh in scene.Meshes)
            {
                // See comment about meshNames at the beginning of the bone section.
                MeshBuilder mb = MeshBuilder.StartMesh(meshNames[sceneMesh]);

                mb.SwapWindingOrder = true; // Appears to require this...

                int positionIndex = -1;

                for (int v = 0; v < sceneMesh.VertexCount; v++)
                {
                    Vector3D vert = sceneMesh.Vertices[v];

                    // CreatePosition should just return a 0-based index of the newly added vertex.
                    positionIndex = mb.CreatePosition(new Vector3(vert.X, vert.Y, vert.Z));

                    if (positionIndex != v)
                    {
                        throw new InvalidContentException("Something unexpected happened while building a MeshContent from the Assimp scene mesh's vertices.  The scene mesh may contains duplicate vertices.");
                    }
                }

                if (positionIndex + 1 < 3)
                {
                    throw new InvalidContentException("There were not enough vertices in the Assimp scene mesh.");
                }



                // Create vertex channels
                int normalVertexChannelIndex = mb.CreateVertexChannel <Vector3>(VertexChannelNames.Normal());

                int[] texCoordVertexChannelIndex = new int[sceneMesh.TextureCoordsChannelCount];
                for (int x = 0; x < sceneMesh.TextureCoordsChannelCount; x++)
                {
                    texCoordVertexChannelIndex[x] = mb.CreateVertexChannel <Vector2>(VertexChannelNames.TextureCoordinate(x));
                }

                int boneWeightVertexChannelIndex = -1;

                if (sceneMesh.HasBones)
                {
                    boneWeightVertexChannelIndex = mb.CreateVertexChannel <BoneWeightCollection>(VertexChannelNames.Weights());
                }


                // Prepare vertex channel data
                BoneWeightCollection[] boneWeightData = null;
                if (sceneMesh.HasBones)
                {
                    boneWeightData = new BoneWeightCollection[sceneMesh.VertexCount];

                    for (int v = 0; v < sceneMesh.VertexCount; v++)
                    {
                        boneWeightData[v] = new BoneWeightCollection();
                    }

                    foreach (Bone sceneMeshBone in sceneMesh.Bones)
                    {
                        // We have to assume that the bone's name matches up with a node, and therefore one of our BoneContents.
                        foreach (VertexWeight sceneMeshBoneWeight in sceneMeshBone.VertexWeights)
                        {
                            boneWeightData[sceneMeshBoneWeight.VertexID].Add(new BoneWeight(sceneMeshBone.Name, sceneMeshBoneWeight.Weight));
                        }
                    }

                    for (int v = 0; v < sceneMesh.VertexCount; v++)
                    {
                        if (boneWeightData[v].Count <= 0)
                        {
                            throw new InvalidContentException("Encountered vertices without bone weights.");
                        }

                        boneWeightData[v].NormalizeWeights();
                    }
                }

                // Set the per-geometry data
                mb.SetMaterial(materials[sceneMesh.MaterialIndex]);
                mb.SetOpaqueData(new OpaqueDataDictionary());

                // Add each vertex
                for (int f = 0; f < sceneMesh.FaceCount; f++)
                {
                    if (sceneMesh.Faces[f].IndexCount != 3)
                    {
                        throw new InvalidContentException("Only triangular faces allowed.");
                    }

                    for (int t = 0; t < 3; t++)
                    {
                        mb.SetVertexChannelData(normalVertexChannelIndex, ToXna(sceneMesh.Normals[sceneMesh.Faces[f].Indices[t]]));

                        for (int x = 0; x < sceneMesh.TextureCoordsChannelCount; x++)
                        {
                            mb.SetVertexChannelData(texCoordVertexChannelIndex[x], ToXnaVector2((sceneMesh.GetTextureCoords(x))[sceneMesh.Faces[f].Indices[t]]));
                        }

                        if (sceneMesh.HasBones)
                        {
                            mb.SetVertexChannelData(boneWeightVertexChannelIndex, boneWeightData[sceneMesh.Faces[f].Indices[t]]);
                        }

                        mb.AddTriangleVertex((int)(sceneMesh.Faces[f].Indices[t]));
                    }
                }

                MeshContent mesh = mb.FinishMesh();
                rootNode.Children.Add(mesh);
                meshes.Add(sceneMesh, mesh);
            }

            return(rootNode);
        }
Пример #20
0
        /// <summary>
        /// The importer's entry point.
        /// Called by the framework when importing a game asset.
        /// </summary>
        /// <param name="filename">Name of a game asset file.</param>
        /// <param name="context">
        /// Contains information for importing a game asset, such as a logger interface.
        /// </param>
        /// <returns>Resulting game asset.</returns>
        public override NodeContent Import(string filename, ContentImporterContext context)
        {
            NodeContent rootNode = new NodeContent
            {
                Identity = new ContentIdentity(filename),
                Name     = Path.GetFileNameWithoutExtension(filename)
            };

            try
            {
                // Import file using Meshellator.
                Scene scene = MeshellatorLoader.ImportFromFile(filename);

                // Create materials.
                //System.Diagnostics.Debugger.Launch();
                Dictionary <Material, MaterialContent> materials = GetMaterials(scene);

                // Convert Meshellator scene to XNA mesh.
                foreach (Mesh mesh in scene.Meshes)
                {
                    MeshContent meshContent = new MeshContent
                    {
                        Name = mesh.Name
                    };
                    foreach (Point3D position in mesh.Positions)
                    {
                        meshContent.Positions.Add(ConvertPoint3D(position));
                    }

                    MaterialContent material = (mesh.Material != null)
                                                                        ? materials[mesh.Material]
                                                                        : new BasicMaterialContent
                    {
                        DiffuseColor       = new Vector3(0.5f),
                        VertexColorEnabled = false
                    };
                    GeometryContent geometryContent = new GeometryContent
                    {
                        Material = material
                    };
                    meshContent.Geometry.Add(geometryContent);

                    geometryContent.Indices.AddRange(mesh.Indices);

                    for (int i = 0; i < mesh.Positions.Count; ++i)
                    {
                        geometryContent.Vertices.Add(i);
                    }

                    List <Vector2> textureCoordinates = new List <Vector2>();
                    for (int i = 0; i < mesh.Positions.Count; ++i)
                    {
                        Vector2 textureCoordinate = (i < mesh.TextureCoordinates.Count)
                                                        ? ConvertTextureCoordinate(mesh.TextureCoordinates[i])
                                                        : Vector2.Zero;
                        textureCoordinates.Add(textureCoordinate);
                    }
                    geometryContent.Vertices.Channels.Add(VertexChannelNames.TextureCoordinate(0), textureCoordinates);

                    List <Vector3> normals = new List <Vector3>();
                    foreach (Vector3D normal in mesh.Normals)
                    {
                        normals.Add(ConvertVector3D(normal));
                    }
                    geometryContent.Vertices.Channels.Add(VertexChannelNames.Normal(), normals);

                    // Finish the mesh and set the transform.
                    if (SwapWindingOrder)
                    {
                        MeshHelper.SwapWindingOrder(meshContent);
                    }
                    meshContent.Transform = ConvertTransform(mesh.Transform);

                    // Add the mesh to the model
                    rootNode.Children.Add(meshContent);
                }

                return(rootNode);
            }
            catch (InvalidContentException)
            {
                // InvalidContentExceptions do not need further processing
                throw;
            }
            catch (Exception e)
            {
                // Wrap exception with content identity (includes line number)
                throw new InvalidContentException(
                          "Unable to parse file. Exception:\n" + e.ToString(),
                          rootNode.Identity, e);
            }
        }
        private void ValidateMorphTarget(MeshContent morphTarget)
        {
            // Check whether morph target is the child of the base mesh.
            var baseMesh = morphTarget.Parent as MeshContent;

            if (baseMesh == null)
            {
                string message = String.Format(
                    CultureInfo.InvariantCulture,
                    "Morph target \"{0}\" needs to be a child of (linked to) the base mesh.",
                    morphTarget.Name);
                throw new InvalidContentException(message, morphTarget.Identity);
            }

            // Check whether number of positions matches.
            if (baseMesh.Positions.Count != morphTarget.Positions.Count)
            {
                string message = String.Format(
                    CultureInfo.InvariantCulture,
                    "Number of positions in morph target \"{0}\" does not match base mesh. (Base mesh: {1}, morph target: {2})",
                    morphTarget.Name, baseMesh.Positions.Count, morphTarget.Positions.Count);
                throw new InvalidContentException(message, morphTarget.Identity);
            }

            // Check whether number of submeshes matches.
            int numberOfSubmeshes = baseMesh.Geometry.Count;

            if (numberOfSubmeshes != morphTarget.Geometry.Count)
            {
                string message = String.Format(
                    CultureInfo.InvariantCulture,
                    "Number of submeshes in morph target \"{0}\" does not match base mesh. (Base mesh: {1}, morph target: {2})",
                    morphTarget.Name, numberOfSubmeshes, morphTarget.Geometry.Count);
                throw new InvalidContentException(message, morphTarget.Identity);
            }

            for (int i = 0; i < numberOfSubmeshes; i++)
            {
                var baseGeometry  = baseMesh.Geometry[i];
                var morphGeometry = morphTarget.Geometry[i];

                // Check whether number of vertices matches.
                if (baseGeometry.Vertices.VertexCount != morphGeometry.Vertices.VertexCount)
                {
                    string message = String.Format(
                        CultureInfo.InvariantCulture,
                        "Number vertices in morph target \"{0}\" does not match base mesh.",
                        morphTarget.Name);
                    throw new InvalidContentException(message, morphTarget.Identity);
                }

                // Check for "Normal" channel.
                if (!baseGeometry.Vertices.Channels.Contains(VertexChannelNames.Normal()))
                {
                    string message = String.Format(
                        CultureInfo.InvariantCulture,
                        "Normal vectors missing in base mesh \"{0}\".",
                        morphTarget.Name);
                    throw new InvalidContentException(message, morphTarget.Identity);
                }

                if (!morphGeometry.Vertices.Channels.Contains(VertexChannelNames.Normal()))
                {
                    string message = String.Format(
                        CultureInfo.InvariantCulture,
                        "Normal vectors missing in morph target \"{0}\".",
                        morphTarget.Name);
                    throw new InvalidContentException(message, morphTarget.Identity);
                }

                int baseNormalsCount  = baseGeometry.Vertices.Channels[VertexChannelNames.Normal()].Count;
                int morphNormalsCount = morphGeometry.Vertices.Channels[VertexChannelNames.Normal()].Count;
                if (baseNormalsCount != morphNormalsCount)
                {
                    string message = String.Format(
                        CultureInfo.InvariantCulture,
                        "Number of vertices in morph target \"{0}\" (submesh: {1}) does not match base mesh. (Base mesh: {2}, morph target: {3})",
                        morphTarget.Name, i, baseNormalsCount, morphNormalsCount);
                    throw new InvalidContentException(message, morphTarget.Identity);
                }

                // Check whether number of indices matches.
                if (baseGeometry.Indices.Count != morphGeometry.Indices.Count)
                {
                    string message = String.Format(
                        CultureInfo.InvariantCulture,
                        "Indices in morph target \"{0}\" do not match base mesh.",
                        morphTarget.Name);
                    throw new InvalidContentException(message, morphTarget.Identity);
                }
            }

            // Check whether morph target has children.
            if (morphTarget.Children.Count > 0)
            {
                _context.Logger.LogWarning(
                    null, _input.Identity,
                    "The children of the morph target \"{0}\" will be ignored.",
                    morphTarget.Name);
            }
        }
Пример #22
0
        } // Process

        #endregion

        #region Process Vertex Channel

        /// <summary>
        /// Processes geometry content vertex channels at the specified index.
        /// </summary>
        protected override void ProcessVertexChannel(GeometryContent geometry, int vertexChannelIndex, ContentProcessorContext context)
        {
            // Compressed Vertex Data
            VertexChannelCollection channels = geometry.Vertices.Channels;
            string name = channels[vertexChannelIndex].Name;

            if (name == VertexChannelNames.Normal())
            {
                channels.ConvertChannelContent <NormalizedShort4>(vertexChannelIndex);
            }
            else if (name == VertexChannelNames.TextureCoordinate(0))
            {
                // If the resource has texture coordinates outside the range [-1, 1] the values will be clamped.
                channels.ConvertChannelContent <HalfVector2>(vertexChannelIndex);
            }
            else if (name == VertexChannelNames.TextureCoordinate(1))
            {
                channels.Remove(VertexChannelNames.TextureCoordinate(1));
            }
            else if (name == VertexChannelNames.TextureCoordinate(2))
            {
                channels.Remove(VertexChannelNames.TextureCoordinate(2));
            }
            else if (name == VertexChannelNames.TextureCoordinate(3))
            {
                channels.Remove(VertexChannelNames.TextureCoordinate(3));
            }
            else if (name == VertexChannelNames.TextureCoordinate(4))
            {
                channels.Remove(VertexChannelNames.TextureCoordinate(4));
            }
            else if (name == VertexChannelNames.TextureCoordinate(5))
            {
                channels.Remove(VertexChannelNames.TextureCoordinate(5));
            }
            else if (name == VertexChannelNames.TextureCoordinate(6))
            {
                channels.Remove(VertexChannelNames.TextureCoordinate(6));
            }
            else if (name == VertexChannelNames.TextureCoordinate(7))
            {
                channels.Remove(VertexChannelNames.TextureCoordinate(7));
            }
            else if (name == VertexChannelNames.Color(0))
            {
                channels.Remove(VertexChannelNames.Color(0));
            }
            else if (name == VertexChannelNames.Tangent(0))
            {
                channels.ConvertChannelContent <NormalizedShort4>(vertexChannelIndex);
            }
            else if (name == VertexChannelNames.Binormal(0))
            {
                // Not need to get rid of the binormal data because the model will use more than 32 bytes per vertex.
                // We can actually try to align the data to 64 bytes per vertex.
                channels.ConvertChannelContent <NormalizedShort4>(vertexChannelIndex);
            }
            else
            {
                // Blend indices, blend weights and everything else.
                // Don't use "BlendWeight0" as a name, nor weights0. Both names don't work.
                base.ProcessVertexChannel(geometry, vertexChannelIndex, context);
                channels.ConvertChannelContent <Byte4>("BlendIndices0");
                channels.ConvertChannelContent <NormalizedShort4>(VertexChannelNames.EncodeName(VertexElementUsage.BlendWeight, 0));
            }
        } // ProcessVertexChannel
Пример #23
0
        /// <summary>
        /// Import a VOX file as Model
        /// </summary>
        private NodeContent VoxelProcess(VoxelContent voxel, ContentImporterContext context)
        {
            XYZI[] voxels  = voxel.Voxels;
            uint[] palette = voxel.Palette;

            var     scale        = voxel.RealSize / voxel.GridSize;
            Vector3 centerOffset = new Vector3(1f, 0f, 1f) * (voxel.RealSize / -2f);

            var corner000 = new Point3(0, 0, 0);
            var corner100 = new Point3(1, 0, 0);
            var corner010 = new Point3(0, 1, 0);
            var corner110 = new Point3(1, 1, 0);
            var corner001 = new Point3(0, 0, 1);
            var corner101 = new Point3(1, 0, 1);
            var corner011 = new Point3(0, 1, 1);
            var corner111 = new Point3(1, 1, 1);


            var Forward  = Vector3.Forward;
            var Backward = Vector3.Backward;
            var Left     = Vector3.Left;
            var Right    = Vector3.Right;
            var Up       = Vector3.Up;
            var Down     = Vector3.Down;

            for (int i = 0; i < voxels.Length; i++)
            {
                var pt000 = voxels[i].Point.Add(ref corner000);
                var pt100 = voxels[i].Point.Add(ref corner100);
                var pt010 = voxels[i].Point.Add(ref corner010);
                var pt110 = voxels[i].Point.Add(ref corner110);
                var pt001 = voxels[i].Point.Add(ref corner001);
                var pt101 = voxels[i].Point.Add(ref corner101);
                var pt011 = voxels[i].Point.Add(ref corner011);
                var pt111 = voxels[i].Point.Add(ref corner111);

                // back
                var p0 = pt000.ToVector3();
                var p1 = pt100.ToVector3();
                var p2 = pt010.ToVector3();
                var p3 = pt110.ToVector3();

                // front
                var p4 = pt001.ToVector3();
                var p5 = pt101.ToVector3();
                var p6 = pt011.ToVector3();
                var p7 = pt111.ToVector3();

                Vector3.Multiply(ref p0, ref scale, out p0); Vector3.Add(ref p0, ref centerOffset, out p0);
                Vector3.Multiply(ref p1, ref scale, out p1); Vector3.Add(ref p1, ref centerOffset, out p1);
                Vector3.Multiply(ref p2, ref scale, out p2); Vector3.Add(ref p2, ref centerOffset, out p2);
                Vector3.Multiply(ref p3, ref scale, out p3); Vector3.Add(ref p3, ref centerOffset, out p3);
                Vector3.Multiply(ref p4, ref scale, out p4); Vector3.Add(ref p4, ref centerOffset, out p4);
                Vector3.Multiply(ref p5, ref scale, out p5); Vector3.Add(ref p5, ref centerOffset, out p5);
                Vector3.Multiply(ref p6, ref scale, out p6); Vector3.Add(ref p6, ref centerOffset, out p6);
                Vector3.Multiply(ref p7, ref scale, out p7); Vector3.Add(ref p7, ref centerOffset, out p7);

                vertex.Color.PackedValue = palette[voxels[i].ColorIndex];

                if ((voxels[i].SharedSides & Sides.Forward) == 0)
                {
                    vertex.Normal = Forward;
                    AddVertex(ref p1);
                    AddVertex(ref p3);
                    AddVertex(ref p0);

                    AddVertex(ref p0);
                    AddVertex(ref p3);
                    AddVertex(ref p2);
                }
                if ((voxels[i].SharedSides & Sides.Backward) == 0)
                {
                    vertex.Normal = Backward;
                    AddVertex(ref p4);
                    AddVertex(ref p6);
                    AddVertex(ref p5);

                    AddVertex(ref p5);
                    AddVertex(ref p6);
                    AddVertex(ref p7);
                }

                if ((voxels[i].SharedSides & Sides.Left) == 0)
                {
                    vertex.Normal = Left;
                    AddVertex(ref p2);
                    AddVertex(ref p6);
                    AddVertex(ref p0);

                    AddVertex(ref p0);
                    AddVertex(ref p6);
                    AddVertex(ref p4);
                }
                if ((voxels[i].SharedSides & Sides.Right) == 0)
                {
                    vertex.Normal = Right;
                    AddVertex(ref p1);
                    AddVertex(ref p5);
                    AddVertex(ref p3);

                    AddVertex(ref p3);
                    AddVertex(ref p5);
                    AddVertex(ref p7);
                }

                if ((voxels[i].SharedSides & Sides.Up) == 0)
                {
                    vertex.Normal = Up;
                    AddVertex(ref p7);
                    AddVertex(ref p6);
                    AddVertex(ref p3);

                    AddVertex(ref p3);
                    AddVertex(ref p6);
                    AddVertex(ref p2);
                }
                if ((voxels[i].SharedSides & Sides.Down) == 0)
                {
                    vertex.Normal = Down;
                    AddVertex(ref p5);
                    AddVertex(ref p1);
                    AddVertex(ref p4);

                    AddVertex(ref p4);
                    AddVertex(ref p1);
                    AddVertex(ref p0);
                }
            }

            MeshContent mesh = new MeshContent();

            mesh.Name = "voxel";

            for (int pi = 0; pi < this.vertices.Count; pi++)
            {
                mesh.Positions.Add(this.vertices[pi].Position);
            }

            var geom = new GeometryContent();

            mesh.Geometry.Add(geom);
            BasicMaterialContent material = new BasicMaterialContent();

            geom.Material = material;

            for (int pi = 0; pi < this.vertices.Count; pi++)
            {
                geom.Vertices.Add(pi);
            }

            for (int ii = 0; ii < this.indices.Count; ii++)
            {
                geom.Indices.Add(this.indices[ii]);
            }

            List <Vector3> normals = new List <Vector3>();
            List <Color>   colors  = new List <Color>();

            for (int vi = 0; vi < this.vertices.Count; vi++)
            {
                var vertex = vertices[vi];
                normals.Add(vertex.Normal);
                colors.Add(vertex.Color);
            }

            geom.Vertices.Channels.Add <Vector3>(VertexChannelNames.Normal(0), normals);
            geom.Vertices.Channels.Add <Color>(VertexChannelNames.Color(0), colors);

            return(mesh);
        }
Пример #24
0
        private static void MakeRelativeMorphTargets(MeshContent baseMesh, List <MeshContent> morphTargets)
        {
            foreach (var morphTarget in morphTargets)
            {
                // Make positions relative to base mesh.
                // (Positions are stored in MeshContent.Positions.)
                var basePositions     = baseMesh.Positions;
                var morphPositions    = morphTarget.Positions;
                int numberOfPositions = basePositions.Count;
                for (int i = 0; i < numberOfPositions; i++)
                {
                    morphPositions[i] -= basePositions[i];
                }

                // Make normals relative to base mesh.
                // (Normals are stored as a vertex channel per submesh.)
                int numberOfSubmeshes = baseMesh.Geometry.Count;
                for (int i = 0; i < numberOfSubmeshes; i++)
                {
                    var baseGeometry    = baseMesh.Geometry[i];
                    var morphGeometry   = morphTarget.Geometry[i];
                    var baseNormals     = baseGeometry.Vertices.Channels.Get <Vector3>(VertexChannelNames.Normal());
                    var morphNormals    = morphGeometry.Vertices.Channels.Get <Vector3>(VertexChannelNames.Normal());
                    int numberOfNormals = baseNormals.Count;
                    for (int j = 0; j < numberOfNormals; j++)
                    {
                        morphNormals[j] -= baseNormals[j];
                    }
                }
            }
        }
Пример #25
0
        private MeshContent CreateMesh(Mesh sceneMesh)
        {
            var mesh = new MeshContent {
                Name = sceneMesh.Name
            };

            // Position vertices are shared at the mesh level
            foreach (var vert in sceneMesh.Vertices)
            {
                mesh.Positions.Add(new Vector3(vert.X, vert.Y, vert.Z));
            }

            var geom = new GeometryContent
            {
                Material = _materials[sceneMesh.MaterialIndex]
            };

            // Geometry vertices reference 1:1 with the MeshContent parent,
            // no indirection is necessary.
            //geom.Vertices.Positions.AddRange(mesh.Positions);
            geom.Vertices.AddRange(Enumerable.Range(0, sceneMesh.VertexCount));
            geom.Indices.AddRange(sceneMesh.GetIndices());

            if (sceneMesh.HasBones)
            {
                var xnaWeights = new List <BoneWeightCollection>();
                for (var i = 0; i < geom.Indices.Count; i++)
                {
                    var list = new BoneWeightCollection();
                    for (var boneIndex = 0; boneIndex < sceneMesh.BoneCount; boneIndex++)
                    {
                        var bone = sceneMesh.Bones[boneIndex];
                        foreach (var weight in bone.VertexWeights)
                        {
                            if (weight.VertexID != i)
                            {
                                continue;
                            }

                            list.Add(new BoneWeight(bone.Name, weight.Weight));
                        }
                    }
                    if (list.Count > 0)
                    {
                        xnaWeights.Add(list);
                    }
                }

                geom.Vertices.Channels.Add(VertexChannelNames.Weights(0), xnaWeights);
            }

            // Individual channels go here
            if (sceneMesh.HasNormals)
            {
                geom.Vertices.Channels.Add(VertexChannelNames.Normal(), ToXna(sceneMesh.Normals));
            }

            for (var i = 0; i < sceneMesh.TextureCoordinateChannelCount; i++)
            {
                geom.Vertices.Channels.Add(VertexChannelNames.TextureCoordinate(i),
                                           ToXnaTexCoord(sceneMesh.TextureCoordinateChannels[i]));
            }

            mesh.Geometry.Add(geom);

            return(mesh);
        }
        } // Process

        #endregion

        #region Process Vertex Channel

        /// <summary>
        /// Processes geometry content vertex channels at the specified index.
        /// </summary>
        protected override void ProcessVertexChannel(GeometryContent geometry, int vertexChannelIndex, ContentProcessorContext context)
        {
            VertexChannelCollection channels = geometry.Vertices.Channels;

            // If the model has only position and normals a UV channel is added.
            // http://xnafinalengine.codeplex.com/wikipage?title=Compressed%20Vertex%20Data
            if (channels.Count == 1 && channels.Contains(VertexChannelNames.Normal()))
            {
                channels.Add<Vector2>(VertexChannelNames.TextureCoordinate(0), null);
            }

            // If the model has position, normal and UV then the data is packed on 32 bytes aliagned vertex data.
            if (channels.Count == 2 && channels.Contains(VertexChannelNames.Normal()) && channels.Contains(VertexChannelNames.TextureCoordinate(0)))
            {
                // No compressed Vertex Data
                base.ProcessVertexChannel(geometry, vertexChannelIndex, context);
            }
            else // If not then the data is compressed.
            {
                string name = channels[vertexChannelIndex].Name;

                if (name == VertexChannelNames.Normal())
                {
                    channels.ConvertChannelContent<NormalizedShort4>(vertexChannelIndex);
                }
                else if (name == VertexChannelNames.TextureCoordinate(0))
                {
                    // Clamp values.
                    /*for (int i = 0; i < channels[vertexChannelIndex].Count; i++)
                    {
                        Vector2 uv = (Vector2)channels[vertexChannelIndex][i];
                        if (uv.X < 0) 
                            uv.X *= -1;
                        if (uv.Y < 0) 
                            uv.Y *= -1;
                        Vector2 uvCampled = new Vector2(uv.X - (float)Math.Truncate(uv.X), uv.Y - (float)Math.Truncate(uv.Y));
                        channels[vertexChannelIndex][i] = uvCampled;
                    }
                    // If the resource has texture coordinates outside the range [-1, 1] the values will be clamped.
                    channels.ConvertChannelContent<NormalizedShort2>(vertexChannelIndex);*/
                    // Sometimes you can't just clamp values, because the distance between vertices surpass 1 uv unit.
                    // And given that I am not removing the binormals I won't normalize the UVs.
                    channels.ConvertChannelContent<HalfVector2>(vertexChannelIndex);
                }
                else if (name == VertexChannelNames.TextureCoordinate(1))
                    channels.Remove(VertexChannelNames.TextureCoordinate(1));
                else if (name == VertexChannelNames.TextureCoordinate(2))
                    channels.Remove(VertexChannelNames.TextureCoordinate(2));
                else if (name == VertexChannelNames.TextureCoordinate(3))
                    channels.Remove(VertexChannelNames.TextureCoordinate(3));
                else if (name == VertexChannelNames.TextureCoordinate(4))
                    channels.Remove(VertexChannelNames.TextureCoordinate(4));
                else if (name == VertexChannelNames.TextureCoordinate(5))
                    channels.Remove(VertexChannelNames.TextureCoordinate(5));
                else if (name == VertexChannelNames.TextureCoordinate(6))
                    channels.Remove(VertexChannelNames.TextureCoordinate(6));
                else if (name == VertexChannelNames.TextureCoordinate(7))
                    channels.Remove(VertexChannelNames.TextureCoordinate(7));
                else if (name == VertexChannelNames.Color(0))
                    channels.Remove(VertexChannelNames.Color(0));
                else if (name == VertexChannelNames.Tangent(0))
                {
                    channels.ConvertChannelContent<NormalizedShort4>(vertexChannelIndex);
                }
                else if (name == VertexChannelNames.Binormal(0))
                {
                    channels.ConvertChannelContent<NormalizedShort4>(vertexChannelIndex);
                    // If the binormal is removed then the position, the normal,
                    // the tangent and one texture coordinate can be fetched in one single block of 32 bytes.
                    // Still, it is more fast to just pass the value. At least on the test I made.
                    //channels.Remove(VertexChannelNames.Binormal(0));
                }
                else
                {
                    base.ProcessVertexChannel(geometry, vertexChannelIndex, context);
                }
            }
        } // ProcessVertexChannel
Пример #27
0
        public override NodeContent Import(string filename, ContentImporterContext context)
        {
            var identity = new ContentIdentity(filename, GetType().Name);
            var importer = new AssimpImporter();

            importer.AttachLogStream(new LogStream((msg, userData) => context.Logger.LogMessage(msg)));
            var scene = importer.ImportFile(filename,
                                            PostProcessSteps.FlipUVs | // So far appears necessary
                                            PostProcessSteps.JoinIdenticalVertices |
                                            PostProcessSteps.Triangulate |
                                            PostProcessSteps.SortByPrimitiveType |
                                            PostProcessSteps.FindInvalidData
                                            );

            var rootNode = new NodeContent
            {
                Name      = scene.RootNode.Name,
                Identity  = identity,
                Transform = ToXna(scene.RootNode.Transform)
            };

            // TODO: Materials
            var materials = new List <MaterialContent>();

            foreach (var sceneMaterial in scene.Materials)
            {
                var diffuse = sceneMaterial.GetTexture(TextureType.Diffuse, 0);

                materials.Add(new BasicMaterialContent()
                {
                    Name     = sceneMaterial.Name,
                    Identity = identity,
                    Texture  = new ExternalReference <TextureContent>(diffuse.FilePath, identity)
                });
            }

            // Meshes
            var meshes = new Dictionary <Mesh, MeshContent>();

            foreach (var sceneMesh in scene.Meshes)
            {
                if (!sceneMesh.HasVertices)
                {
                    continue;
                }

                var mesh = new MeshContent
                {
                    Name = sceneMesh.Name
                };

                // Position vertices are shared at the mesh level
                foreach (var vert in sceneMesh.Vertices)
                {
                    mesh.Positions.Add(new Vector3(vert.X, vert.Y, vert.Z));
                }

                var geom = new GeometryContent
                {
                    Name = string.Empty,
                    //Material = materials[sceneMesh.MaterialIndex]
                };

                // Geometry vertices reference 1:1 with the MeshContent parent,
                // no indirection is necessary.
                geom.Vertices.Positions.AddRange(mesh.Positions);
                geom.Vertices.AddRange(Enumerable.Range(0, sceneMesh.VertexCount));
                geom.Indices.AddRange(sceneMesh.GetIntIndices());

                // Individual channels go here
                if (sceneMesh.HasNormals)
                {
                    geom.Vertices.Channels.Add(VertexChannelNames.Normal(), ToXna(sceneMesh.Normals));
                }

                for (var i = 0; i < sceneMesh.TextureCoordsChannelCount; i++)
                {
                    geom.Vertices.Channels.Add(VertexChannelNames.TextureCoordinate(i),
                                               ToXnaVector2(sceneMesh.GetTextureCoords(i)));
                }

                mesh.Geometry.Add(geom);
                rootNode.Children.Add(mesh);
                meshes.Add(sceneMesh, mesh);
            }

            // Bones
            var bones          = new Dictionary <Node, BoneContent>();
            var hierarchyNodes = scene.RootNode.Children.SelectDeep(n => n.Children).ToList();

            foreach (var node in hierarchyNodes)
            {
                var bone = new BoneContent
                {
                    Name      = node.Name,
                    Transform = Matrix.Transpose(ToXna(node.Transform))
                };

                if (node.Parent == scene.RootNode)
                {
                    rootNode.Children.Add(bone);
                }
                else
                {
                    var parent = bones[node.Parent];
                    parent.Children.Add(bone);
                }

                // Copy the bone's name to the MeshContent - this appears to be
                // the way it comes out of XNA's FBXImporter.
                foreach (var meshIndex in node.MeshIndices)
                {
                    meshes[scene.Meshes[meshIndex]].Name = node.Name;
                }

                bones.Add(node, bone);
            }

            return(rootNode);
        }
Пример #28
0
        /// <summary>
        /// Breaks the input mesh into separate un-indexed triangles.
        /// </summary>
        /// <param name="input">Input MeshContent node.</param>
        /// <returns>Broken MeshContent</returns>
        private MeshContent ProcessMesh(NodeContent input)
        {
            MeshBuilder builder = MeshBuilder.StartMesh("model");

            MeshContent    mesh         = input as MeshContent;
            List <Vector3> normalList   = new List <Vector3>();
            List <Vector2> texCoordList = new List <Vector2>();

            if (mesh != null)
            {
                int normalChannel = builder.CreateVertexChannel <Vector3>(
                    VertexChannelNames.Normal());
                int texChannel = builder.CreateVertexChannel <Vector2>(
                    VertexChannelNames.TextureCoordinate(0));

                foreach (GeometryContent geometry in mesh.Geometry)
                {
                    IndirectPositionCollection positions = geometry.Vertices.Positions;

                    VertexChannel <Vector3> normals =
                        geometry.Vertices.Channels.Get <Vector3>(
                            VertexChannelNames.Normal());

                    VertexChannel <Vector2> texCoords =
                        geometry.Vertices.Channels.Get <Vector2>(
                            VertexChannelNames.TextureCoordinate(0));

                    // Copy the positions over
                    // To do that, we traverse the indices and grab the indexed
                    // position  and add it to the new mesh. This in effect will
                    // duplicate positions in the mesh reversing the compacting
                    // effect of using index buffers.
                    foreach (int i in geometry.Indices)
                    {
                        builder.CreatePosition(positions[i]);

                        // Save the normals and the texture coordinates for additon to
                        // the mesh later.
                        normalList.Add(normals[i]);
                        texCoordList.Add(texCoords[i]);
                    }
                }

                int index = 0;

                foreach (GeometryContent geometry in mesh.Geometry)
                {
                    // Save the material to the new mesh.
                    builder.SetMaterial(geometry.Material);

                    // Now we create the Triangles.
                    // To do that, we simply generate an index list that is sequential
                    // from 0 to geometry.Indices.Count
                    // This will create an index buffer that looks like: 0,1,2,3,4,5,...
                    for (int i = 0; i < geometry.Indices.Count; i++)
                    {
                        // Set the normal for the current vertex
                        builder.SetVertexChannelData(normalChannel, normalList[index]);
                        // Set the texture coordinates for the current vertex
                        builder.SetVertexChannelData(texChannel, texCoordList[index]);
                        builder.AddTriangleVertex(index);
                        index++;
                    }
                }
            }

            MeshContent finalMesh = builder.FinishMesh();

            // Copy the transform over from the source mesh to retain parent/child
            // relative transforms.
            finalMesh.Transform = input.Transform;

            // Now we take the new MeshContent and calculate the centers of all the
            // triangles. The centers are needed so that we can rotate the triangles
            // around them as we shatter the model.
            foreach (GeometryContent geometry in finalMesh.Geometry)
            {
                Vector3[] triangleCenters = new Vector3[geometry.Indices.Count / 3];
                Vector3[] trianglePoints  = new Vector3[2];

                IndirectPositionCollection positions = geometry.Vertices.Positions;

                for (int i = 0; i < positions.Count; i++)
                {
                    Vector3 position = positions[i];

                    if (i % 3 == 2)
                    {
                        // Calculate the center of the triangle.
                        triangleCenters[i / 3] = (trianglePoints[0] + trianglePoints[1]
                                                  + position) / 3;
                    }
                    else
                    {
                        trianglePoints[i % 3] = position;
                    }
                }

                // Add two new channels to the MeshContent:
                // triangleCenterChannel: This is the channel that will store the center
                // of the triangle that this vertex belongs to.
                // rotationalVelocityChannel: This channel has randomly generated values
                // for x,y and z rotational angles. This information will be used to
                // randomly rotate the triangles as they shatter from the model.
                geometry.Vertices.Channels.Add <Vector3>(
                    triangleCenterChannel,
                    new ReplicateTriangleDataToEachVertex <Vector3>(triangleCenters));
                geometry.Vertices.Channels.Add <Vector3>(
                    rotationalVelocityChannel,
                    new ReplicateTriangleDataToEachVertex <Vector3>(
                        new RandomVectorEnumerable(triangleCenters.Length)));
            }

            foreach (NodeContent child in input.Children)
            {
                finalMesh.Children.Add(ProcessMesh(child));
            }

            return(finalMesh);
        }