/// <summary>
        /// This function removes geometry that contains no bone weights, because the ModelProcessor
        /// will throw an exception if we give it geometry content like that.
        /// </summary>
        /// <param name="node"></param>
        /// <param name="context"></param>
        static void RemoveInvalidGeometry(NodeContent node, ContentProcessorContext context)
        {
            MeshContent meshContent = node as MeshContent;

            if (meshContent != null)
            {
                // Maintain a list of all the geometry that was invalid that we will be removing
                List <GeometryContent> removeGeometry = new List <GeometryContent>();
                foreach (GeometryContent geometry in meshContent.Geometry)
                {
                    VertexChannelCollection channels = geometry.Vertices.Channels;

                    // Does this geometry contain bone weight information?
                    if (geometry.Vertices.Channels.Contains(VertexChannelNames.Weights(0)))
                    {
                        bool removed = false;

                        VertexChannel <BoneWeightCollection> weights = geometry.Vertices.Channels.Get <BoneWeightCollection>(VertexChannelNames.Weights(0));
                        foreach (BoneWeightCollection collection in weights)
                        {
                            // If we don't have any weights, then this isn't going to be good. The geometry has no bone weights,
                            // so lets just remove it.
                            if (collection.Count <= 0)
                            {
                                removeGeometry.Add(geometry);
                                removed = true;
                                break;
                            }
                            else
                            {
                                // Otherwise, normalize the weights. This call is probably unnecessary.
                                collection.NormalizeWeights(4);
                            }
                        }

                        //If we removed something from this geometry, just remove the whole geometry - there's no point in going farther
                        if (removed)
                        {
                            break;
                        }
                    }
                }

                // Remove all the invalid geometry we found, and log a warning.
                foreach (GeometryContent geometry in removeGeometry)
                {
                    meshContent.Geometry.Remove(geometry);
                    context.Logger.LogWarning(null, null,
                                              "Mesh part {0} has been removed because it has no bone weights associated with it.",
                                              geometry.Name);
                }
            }

            // Recursively call this function for each child
            foreach (NodeContent child in node.Children)
            {
                RemoveInvalidGeometry(child, context);
            }
        }
        /// <summary>
        /// Converts a single piece of input geometry into our instanced format.
        /// </summary>
        void ProcessGeometry(GeometryContent geometry)
        {
            int indexCount  = geometry.Indices.Count;
            int vertexCount = geometry.Vertices.VertexCount;

            // Validate that the number of vertices is suitable for instancing.
            if (vertexCount > ushort.MaxValue)
            {
                throw new InvalidContentException(
                          string.Format("Geometry contains {0} vertices: " +
                                        "this is too many to be instanced.", vertexCount));
            }

            if (vertexCount > ushort.MaxValue / 8)
            {
                context.Logger.LogWarning(null, rootNode.Identity,
                                          "Geometry contains {0} vertices: " +
                                          "this will only allow it to be instanced " +
                                          "{1} times per batch. A model with fewer " +
                                          "vertices would be more efficient.",
                                          vertexCount, ushort.MaxValue / vertexCount);
            }

            // Validate that the vertex channels we are going to use to pass
            // through our instancing data aren't already in use.
            VertexChannelCollection vertexChannels = geometry.Vertices.Channels;

            for (int i = 1; i <= 4; i++)
            {
                if (vertexChannels.Contains(VertexChannelNames.TextureCoordinate(i)))
                {
                    throw new InvalidContentException(
                              string.Format("Model already contains data for texture " +
                                            "coordinate channel {0}, but instancing " +
                                            "requires this channel for its own use.", i));
                }
            }

            // Flatten the flexible input vertex channel data into
            // a simple GPU style vertex buffer byte array.
            VertexBufferContent vertexBufferContent;

            VertexElement[] vertexElements;

            geometry.Vertices.CreateVertexBuffer(out vertexBufferContent,
                                                 out vertexElements,
                                                 context.TargetPlatform);

            int vertexStride = VertexDeclaration.GetVertexStrideSize(vertexElements, 0);

            // Convert the input material.
            MaterialContent material = ProcessMaterial(geometry.Material);

            // Add the new piece of geometry to our output model.
            outputModel.AddModelPart(indexCount, vertexCount, vertexStride,
                                     vertexElements, vertexBufferContent,
                                     geometry.Indices, material);
        }
Ejemplo n.º 3
0
        private static void ProcessColorChannel(GeometryContent geometry, int vertexChannelIndex)
        {
            VertexChannelCollection channels = geometry.Vertices.Channels;

            try
            {
                channels.ConvertChannelContent <Color>(vertexChannelIndex);
            }
            catch (NotSupportedException)
            {
                throw new InvalidCastException("Unable to convert mesh embedded colour channel to Vector4");
            }
        }
Ejemplo n.º 4
0
        public VertexData()
        {
            Positions = new VertexChannel<Vector3>();
            Normals = new VertexChannelCollection<Vector3>();
            Tangents = new VertexChannelCollection<Vector3>();
            Binormals = new VertexChannelCollection<Vector3>();
            Colors = new VertexChannelCollection<Color4>();
            TextureCoordinates = new VertexChannelCollection<Vector2>();

            Positions.Changed += ChannelChanged;
            Normals.Changed += ChannelChanged;
            Tangents.Changed += ChannelChanged;
            Binormals.Changed += ChannelChanged;
            Colors.Changed += ChannelChanged;
            TextureCoordinates.Changed += ChannelChanged;
        }
Ejemplo n.º 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);
        }
Ejemplo n.º 6
0
        //funnily enough, Weights and colours screw up VertexContent.CreateVertexBuffer()
        //so the normal XNA model importer hacks around this... which is wonderful.
        //this is an exact copy of the hack
        private void ProcessVertexChannels(GeometryContent geometry, ContentProcessorContext context, string asset, Dictionary <string, int> boneIndices, Dictionary <int, int> boneRemap)
        {
            VertexChannelCollection collection = geometry.Vertices.Channels;
            List <VertexChannel>    list       = new List <VertexChannel>(collection);
            int vertexChannelIndex             = 0;

            foreach (VertexChannel channel in list)
            {
                if (((vertexChannelIndex < 0) || (vertexChannelIndex >= collection.Count)) || (collection[vertexChannelIndex] != channel))
                {
                    vertexChannelIndex = collection.IndexOf(channel);
                    if (vertexChannelIndex < 0)
                    {
                        continue;
                    }
                }
                this.ProcessVertexChannel(geometry, vertexChannelIndex, context, asset, boneIndices, boneRemap);
                vertexChannelIndex++;
            }
        }
Ejemplo n.º 7
0
 /// <summary>
 /// Constructs a VertexContent instance.
 /// </summary>
 internal VertexContent(GeometryContent geom)
 {
     positionIndices = new VertexChannel<int>("PositionIndices");
     positions = new IndirectPositionCollection(geom, positionIndices);
     channels = new VertexChannelCollection(this);
 }
Ejemplo n.º 8
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);
        }
Ejemplo n.º 9
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
        } // 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
Ejemplo n.º 11
0
 /// <summary>
 /// Constructs a VertexContent instance.
 /// </summary>
 internal VertexContent()
 {
     channels = new VertexChannelCollection(this);
     positionIndices = new VertexChannel<int>("PositionIndices");
     positions = new IndirectPositionCollection();
 }