Пример #1
0
        static void ProcessWeightsChannel(GeometryContent geometry, int vertexChannelIndex)
        {
            // create a map of Name->Index of the bones
            BoneContent skeleton = MeshHelper.FindSkeleton(geometry.Parent);
            Dictionary <string, int> boneIndices    = new Dictionary <string, int>();
            IList <BoneContent>      flattenedBones = MeshHelper.FlattenSkeleton(skeleton);

            for (int i = 0; i < flattenedBones.Count; i++)
            {
                boneIndices.Add(flattenedBones[i].Name, i);
            }

            // convert all of our bone weights into the correct indices and weight values
            VertexChannel <BoneWeightCollection> inputWeights = geometry.Vertices.Channels[vertexChannelIndex] as VertexChannel <BoneWeightCollection>;

            Vector4[] outputIndices = new Vector4[inputWeights.Count];
            Vector4[] outputWeights = new Vector4[inputWeights.Count];
            for (int i = 0; i < inputWeights.Count; i++)
            {
                ConvertWeights(inputWeights[i], boneIndices, outputIndices, outputWeights, i, geometry);
            }

            // create our new channel names
            int    usageIndex  = VertexChannelNames.DecodeUsageIndex(inputWeights.Name);
            string indicesName = VertexChannelNames.EncodeName(VertexElementUsage.BlendIndices, usageIndex);
            string weightsName = VertexChannelNames.EncodeName(VertexElementUsage.BlendWeight, usageIndex);

            // add in the index and weight channels
            geometry.Vertices.Channels.Insert(vertexChannelIndex + 1, indicesName, outputIndices);
            geometry.Vertices.Channels.Insert(vertexChannelIndex + 2, weightsName, outputWeights);

            // remove the original weights channel
            geometry.Vertices.Channels.RemoveAt(vertexChannelIndex);
        }
Пример #2
0
        private int[] ComputeBoneSet(GeometryContent geometry, ContentProcessorContext context, string asset, Dictionary <string, int> boneIndices)
        {
            SortedDictionary <int, bool> indicesInUse = new SortedDictionary <int, bool>();

            foreach (VertexChannel vc in geometry.Vertices.Channels)
            {
                string str = VertexChannelNames.DecodeBaseName(vc.Name);
                if (str == "Weights")
                {
                    VertexChannel <BoneWeightCollection> channel = vc as VertexChannel <BoneWeightCollection>;
                    if (vc == null)
                    {
                        continue;
                    }
                    for (int n = 0; n < channel.Count; n++)
                    {
                        VertexWeightsInUse(context, asset, channel[n], boneIndices, indicesInUse);
                    }
                }
            }
            int[] values = new int[indicesInUse.Count];
            int   i      = 0;

            foreach (int index in indicesInUse.Keys)
            {
                values[i++] = index;
            }
            return(values);
        }
Пример #3
0
        private static void ProcessWeightsChannel(ContentProcessorContext context, string asset, GeometryContent geometry, int vertexChannelIndex, Dictionary <string, int> boneIndices, Dictionary <int, int> boneRemap)
        {
            if (boneIndices == null)
            {
                throw new InvalidContentException("Mesh has bone weights with no skeleton");
            }

            VertexChannelCollection channels             = geometry.Vertices.Channels;
            VertexChannel           channel2             = channels[vertexChannelIndex];
            VertexChannel <BoneWeightCollection> channel = channel2 as VertexChannel <BoneWeightCollection>;

            Byte4[]   outputIndices = new Byte4[channel.Count];
            Vector4[] outputWeights = new Vector4[channel.Count];
            for (int i = 0; i < channel.Count; i++)
            {
                BoneWeightCollection inputWeights = channel[i];
                ConvertVertexWeights(context, asset, inputWeights, boneIndices, outputIndices, outputWeights, i, geometry, boneRemap);
            }
            int    usageIndex = VertexChannelNames.DecodeUsageIndex(channel.Name);
            string name       = VertexChannelNames.EncodeName(VertexElementUsage.BlendIndices, usageIndex);
            string str        = VertexChannelNames.EncodeName(VertexElementUsage.BlendWeight, usageIndex);

            channels.Insert <Byte4>(vertexChannelIndex + 1, name, outputIndices);
            channels.Insert <Vector4>(vertexChannelIndex + 2, str, outputWeights);
            channels.RemoveAt(vertexChannelIndex);
        }
Пример #4
0
        static void ProcessWeightsChannel(GeometryContent geometry, int vertexChannelIndex, SerializableSkeleton skeleton)
        {
            Dictionary <string, int> boneIndices = skeleton.boneIndexByName;

            // convert all of our bone weights into the correct indices and weight values
            VertexChannel <BoneWeightCollection> inputWeights = geometry.Vertices.Channels[vertexChannelIndex] as VertexChannel <BoneWeightCollection>;

            Vector4[] outputIndices = new Vector4[inputWeights.Count];
            Vector4[] outputWeights = new Vector4[inputWeights.Count];
            for (int i = 0; i < inputWeights.Count; i++)
            {
                ConvertWeights(inputWeights[i], boneIndices, outputIndices, outputWeights, i, geometry);
            }

            // create our new channel names
            int    usageIndex  = VertexChannelNames.DecodeUsageIndex(inputWeights.Name);
            string indicesName = VertexChannelNames.EncodeName(Microsoft.Xna.Framework.Graphics.VertexElementUsage.BlendIndices, usageIndex);
            string weightsName = VertexChannelNames.EncodeName(Microsoft.Xna.Framework.Graphics.VertexElementUsage.BlendWeight, usageIndex);

            // add in the index and weight channels
            geometry.Vertices.Channels.Insert(vertexChannelIndex + 1, indicesName, outputIndices);
            geometry.Vertices.Channels.Insert(vertexChannelIndex + 2, weightsName, outputWeights);

            // remove the original weights channel
            geometry.Vertices.Channels.RemoveAt(vertexChannelIndex);
        }
Пример #5
0
        private static void ColorMaterials(MeshContent mesh)
        {
            foreach (GeometryContent meshPart in mesh.Geometry)
            {
                Vector4 constColor = MatColor(meshPart);

                bool foundColor = false;
                foreach (VertexChannel channel in meshPart.Vertices.Channels)
                {
                    if (channel.Name == VertexChannelNames.Color(0))
                    {
                        foundColor = true;
                        VertexChannel <Vector4> colorChannel = (VertexChannel <Vector4>)channel;
                        for (int i = 0; i < colorChannel.Count; ++i)
                        {
                            colorChannel[i] = constColor;
                        }
                    }
                }
                if (!foundColor)
                {
                    int       cnt    = meshPart.Vertices.VertexCount;
                    Vector4[] colors = new Vector4[cnt];
                    for (int i = 0; i < cnt; ++i)
                    {
                        colors[i] = constColor;
                    }

                    meshPart.Vertices.Channels.Add <Vector4>(
                        VertexChannelNames.Color(0).ToString(), colors);
                }
            }
        }
Пример #6
0
        public static bool NeedsSplitting(MeshContent mesh, int maxBones)
        {
            SortedDictionary <string, object> skinnedBones = new SortedDictionary <string, object>();

            foreach (GeometryContent geom in mesh.Geometry)
            {
                VertexChannel <BoneWeightCollection> weightChannel = null;
                foreach (VertexChannel channel in geom.Vertices.Channels)
                {
                    if (channel.Name == VertexChannelNames.Weights())
                    {
                        weightChannel = (VertexChannel <BoneWeightCollection>)channel;
                        break;
                    }
                }
                if (weightChannel != null)
                {
                    foreach (BoneWeightCollection weights in weightChannel)
                    {
                        foreach (BoneWeight weight in weights)
                        {
                            if (!skinnedBones.ContainsKey(weight.BoneName))
                            {
                                skinnedBones.Add(weight.BoneName, null);
                            }
                        }
                    }
                }
            }
            return(skinnedBones.Keys.Count > maxBones);
        }
Пример #7
0
        /// <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);
            }
        }
Пример #8
0
        private void ProcessGeometry(GeometryContent xnaGeometry)
        {
            // find and process the geometry's bone weights
            for (int i = 0; i < xnaGeometry.Vertices.Channels.Count; i++)
            {
                string channelName = xnaGeometry.Vertices.Channels[i].Name;
                string baseName    = VertexChannelNames.DecodeBaseName(channelName);

                if (baseName == "Weights")
                {
                    ProcessWeightsChannel(xnaGeometry, i, outputModel.skeleton);
                }
            }


            // retrieve the four vertex channels we require for CPU skinning. we ignore any
            // other channels the model might have.
            string normalName      = VertexChannelNames.EncodeName(Microsoft.Xna.Framework.Graphics.VertexElementUsage.Normal, 0);
            string texCoordName    = VertexChannelNames.EncodeName(Microsoft.Xna.Framework.Graphics.VertexElementUsage.TextureCoordinate, 0);
            string blendWeightName = VertexChannelNames.EncodeName(Microsoft.Xna.Framework.Graphics.VertexElementUsage.BlendWeight, 0);
            string blendIndexName  = VertexChannelNames.EncodeName(Microsoft.Xna.Framework.Graphics.VertexElementUsage.BlendIndices, 0);

            VertexChannel <Vector3> normals      = xnaGeometry.Vertices.Channels[normalName] as VertexChannel <Vector3>;
            VertexChannel <Vector2> texCoords    = xnaGeometry.Vertices.Channels[texCoordName] as VertexChannel <Vector2>;
            VertexChannel <Vector4> blendWeights = xnaGeometry.Vertices.Channels[blendWeightName] as VertexChannel <Vector4>;
            VertexChannel <Vector4> blendIndices = xnaGeometry.Vertices.Channels[blendIndexName] as VertexChannel <Vector4>;

            // create our array of vertices
            int triangleCount = xnaGeometry.Indices.Count / 3;

            SerializableVertex[] vertices = new SerializableVertex[xnaGeometry.Vertices.VertexCount];
            for (int i = 0; i < vertices.Length; i++)
            {
                vertices[i] = new SerializableVertex
                {
                    position     = xnaGeometry.Vertices.Positions[i],
                    normal       = normals[i],
                    texture      = texCoords[i],
                    blendweights = blendWeights[i],
                    blendindices = blendIndices[i]
                };
            }

            int[] indices = new int[xnaGeometry.Indices.Count];
            for (int i = 0; i < xnaGeometry.Indices.Count; i++)
            {
                indices[i] = xnaGeometry.Indices[i];
            }

            SerializableMesh mesh = new SerializableMesh();

            mesh.name        = string.Format("mesh_{0}_{1}", outputModel.meshList.Count, xnaGeometry.Name);
            mesh.textureName = GetTextureName(xnaGeometry);
            mesh.vertices    = vertices;
            mesh.indices     = indices;
            outputModel.meshList.Add(mesh);
        }
Пример #9
0
        Tuple <VertexChannel, VertexChannel> GenerateJointChannels(Vector4[] jointIndices, Vector3[] jointWeights, int[] positionIndices)
        {
            if (jointIndices.Length == 0)
            {
                return(null);
            }

            VertexSource indexSource  = new VertexSource();
            VertexSource weightSource = new VertexSource();

            indexSource.Stride = 4;
            indexSource.Data   = new float[jointIndices.Length * 4];

            weightSource.Stride = 3;
            weightSource.Data   = new float[jointWeights.Length * 3];

            for (int i = 0; i < jointIndices.Length; i++)
            {
                indexSource.Data[i * 4 + 0] = jointIndices[i].X;
                indexSource.Data[i * 4 + 1] = jointIndices[i].Y;
                indexSource.Data[i * 4 + 2] = jointIndices[i].Z;
                indexSource.Data[i * 4 + 3] = jointIndices[i].W;
            }

            for (int i = 0; i < jointWeights.Length; i++)
            {
                weightSource.Data[i * 3 + 0] = jointWeights[i].X;
                weightSource.Data[i * 3 + 1] = jointWeights[i].Y;
                weightSource.Data[i * 3 + 2] = jointWeights[i].Z;
            }

            VertexElement indexDesc = new VertexElement();

            indexDesc.Offset              = 0;
            indexDesc.UsageIndex          = 0;
            indexDesc.VertexElementFormat = VertexElementFormat.Vector4;
            indexDesc.VertexElementUsage  = VertexElementUsage.BlendIndices;

            VertexElement weightDesc = new VertexElement();

            weightDesc.Offset              = 0;
            weightDesc.UsageIndex          = 0;
            weightDesc.VertexElementFormat = VertexElementFormat.Vector3;
            weightDesc.VertexElementUsage  = VertexElementUsage.BlendWeight;

            VertexChannel indexChannel  = new VertexChannel(indexSource, indexDesc);
            VertexChannel weightChannel = new VertexChannel(weightSource, weightDesc);

            indexChannel.Indices  = new int[positionIndices.Length];
            weightChannel.Indices = new int[positionIndices.Length];

            Array.Copy(positionIndices, indexChannel.Indices, positionIndices.Length);
            Array.Copy(positionIndices, weightChannel.Indices, positionIndices.Length);

            return(new Tuple <VertexChannel, VertexChannel>(indexChannel, weightChannel));
        }
Пример #10
0
 private void GetWeightChannel()
 {
     foreach (VertexChannel channel in geom.Vertices.Channels)
     {
         if (channel.Name == VertexChannelNames.Weights())
         {
             weightChannel = (VertexChannel <BoneWeightCollection>)channel;
             break;
         }
     }
 }
Пример #11
0
        void ProcessGeometry(GeometryContent geometry)
        {
            // find and process the geometry's bone weights
            for (int i = 0; i < geometry.Vertices.Channels.Count; i++)
            {
                string channelName = geometry.Vertices.Channels[i].Name;
                string baseName    = VertexChannelNames.DecodeBaseName(channelName);

                if (baseName == "Weights")
                {
                    ProcessWeightsChannel(geometry, i);
                }
            }

            // retrieve the four vertex channels we require for CPU skinning. we ignore any
            // other channels the model might have.
            string normalName      = VertexChannelNames.EncodeName(VertexElementUsage.Normal, 0);
            string texCoordName    = VertexChannelNames.EncodeName(VertexElementUsage.TextureCoordinate, 0);
            string blendWeightName = VertexChannelNames.EncodeName(VertexElementUsage.BlendWeight, 0);
            string blendIndexName  = VertexChannelNames.EncodeName(VertexElementUsage.BlendIndices, 0);

            VertexChannel <Vector3> normals      = geometry.Vertices.Channels[normalName] as VertexChannel <Vector3>;
            VertexChannel <Vector2> texCoords    = geometry.Vertices.Channels[texCoordName] as VertexChannel <Vector2>;
            VertexChannel <Vector4> blendWeights = geometry.Vertices.Channels[blendWeightName] as VertexChannel <Vector4>;
            VertexChannel <Vector4> blendIndices = geometry.Vertices.Channels[blendIndexName] as VertexChannel <Vector4>;

            // create our array of vertices
            int triangleCount = geometry.Indices.Count / 3;

            CpuVertex[] vertices = new CpuVertex[geometry.Vertices.VertexCount];
            for (int i = 0; i < vertices.Length; i++)
            {
                vertices[i] = new CpuVertex
                {
                    Position          = geometry.Vertices.Positions[i],
                    Normal            = normals[i],
                    TextureCoordinate = texCoords[i],
                    BlendWeights      = blendWeights[i],
                    BlendIndices      = blendIndices[i]
                };
            }

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

            // Add the new piece of geometry to our output model.
            outputModel.AddModelPart(triangleCount, geometry.Indices, vertices, material as BasicMaterialContent);
        }
Пример #12
0
        /// <summary>
        /// Processes a single node in the node hierarchy.  Recursively processes children.
        /// </summary>
        /// <param name="node">The node to process.</param>
        private void ProcessNode(NodeContent node, StaticModelContent staticModel, ContentProcessorContext context, ref int indexOffset, ref int vertexOffset)
        {
            MeshContent mesh = node as MeshContent;

            if (mesh != null)
            {
                GenerateNTBData(mesh);

                foreach (GeometryContent geometry in mesh.Geometry)
                {
                    if (!geometry.Vertices.Channels.Contains("Normal0") || !geometry.Vertices.Channels.Contains("TextureCoordinate0") || !geometry.Vertices.Channels.Contains("Binormal0") || !geometry.Vertices.Channels.Contains("Tangent0"))
                    {
                        // Only complain about normals/texture coordinates, since binormal and tangent data should have been computed for us and would only have failed if normal/texture coordinate data did not exist.
                        throw new Exception("Geometry must contain Normal/Texture Coordinate channels.");
                    }

                    VertexChannel <Vector3> normals   = geometry.Vertices.Channels.Get <Vector3>("Normal0");
                    VertexChannel <Vector3> binormals = geometry.Vertices.Channels.Get <Vector3>("Binormal0");
                    VertexChannel <Vector3> tangents  = geometry.Vertices.Channels.Get <Vector3>("Tangent0");
                    VertexChannel <Vector2> texCoord0 = geometry.Vertices.Channels.Get <Vector2>("TextureCoordinate0");

                    staticModel.VertexContent.Write <Vector3>(sizeof(float) * 0 + vertexOffset, sizeof(float) * 14, geometry.Vertices.Positions);
                    staticModel.VertexContent.Write <Vector2>(sizeof(float) * 3 + vertexOffset, sizeof(float) * 14, texCoord0);
                    staticModel.VertexContent.Write <Vector3>(sizeof(float) * 5 + vertexOffset, sizeof(float) * 14, normals);
                    staticModel.VertexContent.Write <Vector3>(sizeof(float) * 8 + vertexOffset, sizeof(float) * 14, binormals);
                    staticModel.VertexContent.Write <Vector3>(sizeof(float) * 11 + vertexOffset, sizeof(float) * 14, tangents);

                    vertexOffset += geometry.Vertices.Positions.Count * sizeof(float) * 14;

                    int[] indices = new int[geometry.Indices.Count];
                    geometry.Indices.CopyTo(indices, 0);

                    for (int i = 0; i < indices.Length; ++i)
                    {
                        indices[i] += indexOffset;
                    }

                    indexOffset += geometry.Vertices.Positions.Count;

                    staticModel.IndexContent.AddRange(indices);
                }
            }

            foreach (NodeContent child in node.Children)
            {
                ProcessNode(child, staticModel, context, ref indexOffset, ref vertexOffset);
            }
        }
Пример #13
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;
        }
Пример #14
0
 private void CreatePaletteIndices(MeshContent mesh)
 {
     foreach (GeometryContent meshPart in mesh.Geometry)
     {
         int         meshIndex = (int)mesh.OpaqueData["MeshIndex"];
         BoneIndexer indexer   = indexers[meshIndex];
         foreach (VertexChannel channel in meshPart.Vertices.Channels)
         {
             if (channel.Name == VertexChannelNames.Weights())
             {
                 VertexChannel <BoneWeightCollection> vc =
                     (VertexChannel <BoneWeightCollection>)channel;
                 foreach (BoneWeightCollection boneWeights in vc)
                 {
                     foreach (BoneWeight weight in boneWeights)
                     {
                         indexer.GetBoneIndex(weight.BoneName);
                     }
                 }
             }
         }
     }
 }
Пример #15
0
        /// <summary>
        /// This is worth some comments.
        /// The PositionIndices are a mapping from the indices in a GeometryContent
        /// to the Positions in its parent MeshContent.
        /// Note that the GeometryContent also has Positions, but these are extracted
        /// from the MeshContent automatically using the PositionIndices.
        /// All other Channels are directly indexed by the Indices.
        /// So:
        /// pos = MeshContent.Positions[GeoCont.Verts.PosIndices[GeoCont.Indices[i]]]
        /// but
        /// pos = GeoCont.Verts.Positions[GeoCont.Indices[i]]
        ///
        /// When we add some position indices to the GeoCont.Vertices, the positions
        /// are automatically set, because we are giving PositionIndices which map
        /// into the parent MeshContent. This assumes the positions in question are
        /// already in the MeshContent.Positions (which is the case here, because src
        /// and dst are both children of the same MeshContent).
        /// The additional channels are initialized to some unspecified default values,
        /// but they are there now, so all we need to do is copy them from src into dst.
        /// Since they are directly indexed by Indices, we know that their indices
        /// are simply offset by the number of verts that were in dst before we added
        /// in src. Similarly, we add in the src Indices offset by dst's original
        /// number of verts.
        /// </summary>
        /// <param name="dst"></param>
        /// <param name="src"></param>
        private static void AddGeometry(GeometryContent dst, GeometryContent src)
        {
            int baseIndex = dst.Vertices.VertexCount;

            int[] posindices = new int[src.Vertices.VertexCount];
            for (int i = 0; i < src.Vertices.VertexCount; ++i)
            {
                posindices[i] = src.Vertices.PositionIndices[i];
            }
            dst.Vertices.AddRange(posindices);

            /// Append src channel data to dst
            for (int iChan = 0; iChan < src.Vertices.Channels.Count; ++iChan)
            {
                VertexChannel srcChannel = src.Vertices.Channels[iChan];
                VertexChannel dstChannel = dst.Vertices.Channels[iChan];
                for (int i = 0; i < src.Vertices.VertexCount; ++i)
                {
                    dstChannel[i + baseIndex] = srcChannel[i];
                }
            }

            for (int i = 0; i < src.Indices.Count; ++i)
            {
                dst.Indices.Add(src.Indices[i] + baseIndex);
            }

            /// Copy over any opaqueData
            foreach (KeyValuePair <string, object> k in src.OpaqueData)
            {
                if (!dst.OpaqueData.ContainsKey(k.Key))
                {
                    dst.OpaqueData.Add(k.Key, k.Value);
                }
            }
        }
Пример #16
0
        protected virtual void ProcessGeometry(GeometryContent geometry, ContentProcessorContext context)
        {
            if (bakeMeshTransform)
            {
                Matrix vertexTransform = geometry.Parent.AbsoluteTransform;
                Matrix vectorTransform = Matrix.Transpose(Matrix.Invert(vertexTransform));

                foreach (VertexChannel vertexChannel in geometry.Vertices.Channels)
                {
                    if (IsVectorChannel(vertexChannel) && vertexChannel.ElementType == typeof(Vector3))
                    {
                        // Cast for a channel of type Vector3
                        VertexChannel <Vector3> vectorChannel = (VertexChannel <Vector3>)vertexChannel;

                        // Transform all the vectors
                        for (int i = 0; i < vectorChannel.Count; i++)
                        {
                            vectorChannel[i] = Vector3.Transform(vectorChannel[i], vectorTransform);
                            vectorChannel[i].Normalize();
                        }
                    }
                }
            }
        }
Пример #17
0
 /// <summary>
 /// Constructs a VertexContent instance.
 /// </summary>
 internal VertexContent()
 {
     channels = new VertexChannelCollection(this);
     positionIndices = new VertexChannel<int>("PositionIndices");
     positions = new IndirectPositionCollection();
 }
Пример #18
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);
 }
Пример #19
0
        /// <summary>
        /// Creates a vertex container from a set of "raw" vertex channels as read from the COLLADA file.
        /// Hence, it is assumed that each channel uses its own source (rather than every channel using
        /// the same single source). 
        /// </summary>
        /// <param name="inputChannels">Original Input Channels from COLLADA file</param>
        public VertexContainer(List<VertexChannel> inputChannels)
        {
            // Check for basic requirements
            if (inputChannels.Any(c => c.Description.VertexElementUsage == VertexElementUsage.Position) == false)
                throw new ArgumentException("Geometry has not all needed information. At least Positions are necessary!");

            // Convert Colors to single values, if necessary
            ConvertColorChannels(inputChannels);

            // Number of floats per vertex
            _vertexSize = CalculateVertexSize(inputChannels);

            // Expected number of indices
            int numIndices = inputChannels.First().Indices.Length;

            // vertex buffer with an expected number of 3/4 of the number of indices
            List<float> vbuffer = new List<float>(numIndices * 3 / 4);

            // Remember the position of distinct vertices to avoid duplicates
            Dictionary<VertexKey, int> usedVertices = new Dictionary<VertexKey, int>(numIndices * 3 / 4);

            // Indices referencing the new vertex buffer (vbuffer)
            List<int> indexList = new List<int>(numIndices);

            // Go through all indices to create vertices
            for (int i = 0; i < numIndices; i++)
            {
                VertexKey key = new VertexKey(inputChannels, i);
                int usedIndex = 0;

                if (usedVertices.TryGetValue(key, out usedIndex))
                {
                    // This vertex was already used, its index is "usedIndex"
                    indexList.Add(usedIndex);
                }
                else
                {
                    // If the vertex is unknown, add it to the vertex container (channel-wise)
                    // and remember that is has been used and the corresponding index
                    int index = vbuffer.Count / _vertexSize;

                    // Add all elements of the current vertex to the vertex buffer
                    foreach (VertexChannel channel in inputChannels)
                    {
                        float[] elementData = new float[channel.Source.Stride];
                        channel.GetValue(i, ref elementData, 0);

                        // origin of texture coordinates in XNA is top left, while
                        // in COLLADA it is bottom left. Therefore they need to be
                        // converted here
                        if (channel.Description.VertexElementUsage == VertexElementUsage.TextureCoordinate)
                        {
                            elementData[1] = 1 - elementData[1];
                        }

                        vbuffer.AddRange(elementData);
                    }

                    // Remember that this vertex combination was used before
                    // and store the index where it can be found in the
                    // vertex container
                    usedVertices.Add(key, index);

                    // Add reference to the just created vertex to the index list / buffer
                    indexList.Add(index);
                }
            }

            // Create adequate vertex channels
            int offset = 0;

            foreach (VertexChannel inputChannel in inputChannels)
            {
                VertexSource newSource = new VertexSource()
                {
                    Offset = offset // the element-offset within the vertex buffer
                };

                VertexElement desc = new VertexElement(offset, inputChannel.Description.VertexElementFormat,
                    inputChannel.Description.VertexElementUsage, inputChannel.Description.UsageIndex);

                VertexChannel newChannel = new VertexChannel(newSource, desc);
                _vertexChannels.Add(newChannel);

                offset += inputChannel.Source.Stride;
            }

            // Swap winding order
            for (int i = 0; i < indexList.Count; i += 3)
            {
                int swap = indexList[i + 1];
                indexList[i + 1] = indexList[i + 2];
                indexList[i + 2] = swap;
            }

            _data = vbuffer.ToArray();
            _indices = indexList.ToArray();

            // Update Source Data reference off all vertex channels
            foreach (VertexChannel channel in _vertexChannels)
            {
                // Every channel uses the same source now (global vertex buffer)
                channel.Source.Data = _data;

                // Every channel also uses the same indices
                channel.Indices = _indices;

                // The stride of one entry containing all elements for one vertex
                channel.Source.Stride = _vertexSize;
            }
        }
Пример #20
0
        /// <summary>
        /// Imports a piece of geometry, but does only support meshes, no NURBS.
        /// </summary>
        /// <param name="xmlGeometryNode">XML node of the geometry</param>
        /// <exception cref="Exception">Only works with meshes</exception>
        Mesh ImportGeometry(XmlNode xmlGeometryNode, ColladaModel model)
        {
            // Find the mesh node
            XmlNode xmlMeshNode = xmlGeometryNode.SelectSingleNode(".//mesh");

            if (xmlMeshNode == null)
            {
                throw new Exception("No supported geometry (Mesh) found");
            }

            // Determine number of mesh parts
            XmlNodeList xmlTriangles = xmlMeshNode.SelectNodes("triangles|polygons|polylist");

            if (xmlTriangles.Count == 0)
            {
                throw new Exception("No triangles found in mesh. Only triangles are supported");
            }

            if (xmlTriangles[0].Name != "triangles")
            {
                // If there are polygons or a polylist, check that all of them are triangles
                if (xmlTriangles[0].Attributes["vcount"] != null)
                {
                    var vcounts      = XmlUtil.ParseInts(xmlTriangles[0].Attributes["vcount"].Value);
                    var nonTriangles = vcounts.Where(count => count != 3);
                    if (nonTriangles.Any())
                    {
                        throw new Exception("Found polygon with " + nonTriangles.First() +
                                            " elements. Only triangles are supported");
                    }
                }
            }

            // Source data for this mesh used by all mesh parts.
            List <VertexSource> sources = ReadSources(xmlMeshNode);

            Vector4[] jointIndices;
            Vector3[] jointWeights;

            // Skinning Information, if available
            GetJointWeightsAndIndices(xmlMeshNode, model, out jointIndices, out jointWeights);

            if (sources.Count == 0)
            {
                throw new Exception("No data found");
            }

            //-------------------------------------------------------
            // Create Mesh
            //-------------------------------------------------------
            Mesh mesh = new Mesh();

            mesh.Name      = XmlUtil.GetName(xmlGeometryNode);
            mesh.MeshParts = new MeshPart[xmlTriangles.Count];

            // A mesh container for every mesh part, since every mesh part may use different
            // vertex types. This can be optimized in the content processor, if needed
            mesh.VertexContainers = new VertexContainer[xmlTriangles.Count];

            string[] semantics = new string[] { "VERTEX", "COLOR", "NORMAL", "TEXCOORD", "TEXTANGENT", "TEXBINORMAL" };

            //-------------------------------------------------------
            // Create Mesh Parts
            //-------------------------------------------------------
            for (int i = 0; i < xmlTriangles.Count; i++)
            {
                XmlNode    xmlPart      = xmlTriangles[i];
                int        numTriangles = int.Parse(xmlPart.Attributes["count"].Value);
                List <int> indexStream  = new List <int>(numTriangles * 3);

                var pNodes = xmlPart.SelectNodes("p");
                if (pNodes.Count > 1)
                {
                    // Indices are scattered among numTriangles <p> tags
                    foreach (XmlNode p in pNodes)
                    {
                        indexStream.AddRange(XmlUtil.ParseInts(p.InnerText));
                    }
                }
                else
                {
                    // Indices are contained in one <p> tag
                    indexStream.AddRange(XmlUtil.ParseInts(pNodes[0].InnerText));
                }

                int[]    indices = indexStream.ToArray();
                MeshPart part    = new MeshPart();

                try
                {
                    if (xmlPart.Attributes["material"] == null)
                    {
                        throw new Exception("no material attribute found");
                    }
                    part.MaterialName = FindMaterial(xmlGeometryNode, xmlPart.Attributes["material"].Value);
                }
                catch (Exception)
                {
                    // No Material found
                    part.MaterialName = null;
                }

                // Read Vertex Channels
                List <VertexChannel> vertexChannels = new List <VertexChannel>();

                foreach (String semantic in semantics)
                {
                    XmlNode input = xmlPart.SelectSingleNode(".//input[@semantic='" + semantic + "']");
                    if (input == null)
                    {
                        continue;                // no such vertex channel defined
                    }
                    int    offset;
                    String sourceId;

                    if (!input.TryGetAttribute("source", out sourceId))
                    {
                        throw new Exception("Referenced source of input with '" + semantic + "' semantic not found");
                    }
                    if (!input.TryGetAttribute("offset", out offset))
                    {
                        throw new Exception("No offset attribute of input with '" + semantic + "' semantic found");
                    }

                    sourceId = sourceId.Replace("#", "");
                    VertexSource source = sources.Where(s => s.GlobalID.Equals(sourceId)).FirstOrDefault();
                    if (source == null)
                    {
                        if (semantic.Equals("VERTEX"))
                        {
                            sourceId = xmlGeometryNode.SelectSingleNode(".//input[@semantic='POSITION']/@source").InnerText.Substring(1);
                            source   = sources.Where(s => s.GlobalID.Equals(sourceId)).FirstOrDefault();
                        }

                        if (source == null)
                        {
                            throw new Exception("Source '" + sourceId + "' not found");
                        }
                    }

                    VertexElement desc = new VertexElement();
                    desc.Offset              = offset;
                    desc.UsageIndex          = 0;
                    desc.VertexElementFormat = GetVertexElementFormat(source, semantic);
                    desc.VertexElementUsage  = GetVertexElementUsage(semantic);

                    VertexChannel channel = new VertexChannel(source, desc);
                    channel.CopyIndices(indices, offset, numTriangles * 3);
                    vertexChannels.Add(channel);
                }

                var jointChannels = GenerateJointChannels(jointIndices, jointWeights,
                                                          vertexChannels.Where(c => c.Description.VertexElementUsage ==
                                                                               VertexElementUsage.Position).First().Indices);

                if (jointChannels != null)
                {
                    vertexChannels.Add(jointChannels.Item1);
                    vertexChannels.Add(jointChannels.Item2);
                }

                part.Vertices = new VertexContainer(vertexChannels);
                part.Indices  = part.Vertices.Indices;

                mesh.VertexContainers[i] = part.Vertices;
                mesh.MeshParts[i]        = part;
            }

            return(mesh);
        }
Пример #21
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);
        }
Пример #22
0
 private bool IsVectorChannel(VertexChannel vertexChannel)
 {
     return(vertexChannel.Name.StartsWith("Normal") ||
            vertexChannel.Name.StartsWith("Tangent") ||
            vertexChannel.Name.StartsWith("Binormal"));
 }
Пример #23
0
 private void GetWeightChannel()
 {
     foreach (VertexChannel channel in geom.Vertices.Channels)
     {
         if (channel.Name == VertexChannelNames.Weights())
         {
             weightChannel = (VertexChannel<BoneWeightCollection>)channel;
             break;
         }
     }
 }
Пример #24
0
        /// <summary>
        /// Go through the vertex channels in the geometry and replace the
        /// BoneWeightCollection objects with weight and index channels.
        /// </summary>
        /// <param name="geometry">The geometry to process.</param>
        /// <param name="vertexChannelIndex">The index of the vertex channel to process.</param>
        /// <param name="context">The processor context.</param>
        protected override void ProcessVertexChannel(GeometryContent geometry, int vertexChannelIndex, ContentProcessorContext context)
        {
            bool boneCollectionsWithZeroWeights = false;

            if (geometry.Vertices.Channels[vertexChannelIndex].Name == VertexChannelNames.Weights())
            {
                int         meshIndex = (int)geometry.Parent.OpaqueData["MeshIndex"];
                BoneIndexer indexer   = indexers[meshIndex];
                // Skin channels are passed in from importers as BoneWeightCollection objects
                VertexChannel <BoneWeightCollection> vc =
                    (VertexChannel <BoneWeightCollection>)
                    geometry.Vertices.Channels[vertexChannelIndex];
                int maxBonesPerVertex = 0;
                for (int i = 0; i < vc.Count; i++)
                {
                    int count = vc[i].Count;
                    if (count > maxBonesPerVertex)
                    {
                        maxBonesPerVertex = count;
                    }
                }

                // Add weights as colors (Converts well to 4 floats)
                // and indices as packed 4byte vectors.
                Color[] weightsToAdd = new Color[vc.Count];
                Byte4[] indicesToAdd = new Byte4[vc.Count];

                // Go through the BoneWeightCollections and create a new
                // weightsToAdd and indicesToAdd array for each BoneWeightCollection.
                for (int i = 0; i < vc.Count; i++)
                {
                    BoneWeightCollection bwc = vc[i];

                    if (bwc.Count == 0)
                    {
                        boneCollectionsWithZeroWeights = true;
                        continue;
                    }

                    bwc.NormalizeWeights(4);
                    int count = bwc.Count;
                    if (count > maxBonesPerVertex)
                    {
                        maxBonesPerVertex = count;
                    }

                    // Add the appropriate bone indices based on the bone names in the
                    // BoneWeightCollection
                    Vector4 bi = new Vector4();
                    bi.X = count > 0 ? indexer.GetBoneIndex(bwc[0].BoneName) : (byte)0;
                    bi.Y = count > 1 ? indexer.GetBoneIndex(bwc[1].BoneName) : (byte)0;
                    bi.Z = count > 2 ? indexer.GetBoneIndex(bwc[2].BoneName) : (byte)0;
                    bi.W = count > 3 ? indexer.GetBoneIndex(bwc[3].BoneName) : (byte)0;


                    indicesToAdd[i] = new Byte4(bi);
                    Vector4 bw = new Vector4();
                    bw.X            = count > 0 ? bwc[0].Weight : 0;
                    bw.Y            = count > 1 ? bwc[1].Weight : 0;
                    bw.Z            = count > 2 ? bwc[2].Weight : 0;
                    bw.W            = count > 3 ? bwc[3].Weight : 0;
                    weightsToAdd[i] = new Color(bw);
                }

                // Remove the old BoneWeightCollection channel
                geometry.Vertices.Channels.Remove(vc);
                // Add the new channels
                geometry.Vertices.Channels.Add <Byte4>(VertexElementUsage.BlendIndices.ToString(), indicesToAdd);
                geometry.Vertices.Channels.Add <Color>(VertexElementUsage.BlendWeight.ToString(), weightsToAdd);
            }
            else
            {
                // No skinning info, so we let the base class process the channel
                base.ProcessVertexChannel(geometry, vertexChannelIndex, context);
            }
            if (boneCollectionsWithZeroWeights)
            {
                context.Logger.LogWarning("", geometry.Identity,
                                          "BonesWeightCollections with zero weights found in geometry.");
            }
        }