Vertex key to identify an unique vertex by the combination of its used position, normal, tangent and texture coordinate
Beispiel #1
0
        /// <summary>
        /// Determines whether two vertex keys are the same, that is they are both
        /// referring to the exact same vertex through their indices.
        /// </summary>
        /// <param name="obj">Another vertex key</param>
        /// <returns>True if both vertex keys refer to the same vertex</returns>
        public override bool Equals(object obj)
        {
            if (obj is VertexKey)
            {
                VertexKey other   = (VertexKey)obj;
                var       a       = this.VertexChannels;
                var       b       = other.VertexChannels;
                int       i       = SuperIndex;
                int       j       = other.SuperIndex;
                var       pos     = VertexElementUsage.Position;
                var       normal  = VertexElementUsage.Normal;
                var       tex     = VertexElementUsage.TextureCoordinate;
                var       tangent = VertexElementUsage.Tangent;
                var       color   = VertexElementUsage.Color;

                return(a == b &&
                       a[color].Indices[i] == b[color].Indices[j] &&
                       a[pos].Indices[i] == b[pos].Indices[j] &&
                       a[normal].Indices[i] == b[normal].Indices[j] &&
                       a[tex].Indices[i] == b[tex].Indices[j] &&
                       a[tangent].Indices[i] == b[tangent].Indices[j]);

                // Note: Joint Indices/Weights are always the same for one position,
                // no need to compare
            }

            return(base.Equals(obj));
        }
        /// <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;
            }
        }
Beispiel #3
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;
            }
        }