/// <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; } }
/// <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; } }