/// <summary> /// Function to remap your indices to improve spatial locality in your vertex buffer. /// </summary> /// <remarks> /// Note that, according to the remapping handed back to you, you must reorder your /// vertex buffer. /// </remarks> /// <param name="primitiveGroups">Array of PrimitiveGroups you want remapped.</param> /// <param name="vertexCount"> /// Number of vertices in your vertex buffer, also can be thought of as the range of acceptable /// values for indices in your primitive groups. /// </param> /// <param name="remappedPrimitiveGroups">Array of remapped PrimitiveGroups.</param> public void RemapIndices(PrimitiveGroup[] primitiveGroups, ushort vertexCount, out PrimitiveGroup[] remappedPrimitiveGroups) { remappedPrimitiveGroups = new PrimitiveGroup[primitiveGroups.Length]; //caches oldIndex --> newIndex conversion var indexCache = new int[vertexCount]; for (int i = 0; i < indexCache.Length; ++i) { indexCache[i] = -1; } //loop over primitive groups uint indexCtr = 0; for (int i = 0; i < primitiveGroups.Length; i++) { int numIndices = primitiveGroups[i].Indices.Length; //init remapped group remappedPrimitiveGroups[i] = new PrimitiveGroup(primitiveGroups[i].Type, new ushort[numIndices]); for (int j = 0; j < numIndices; j++) { int cachedIndex = indexCache[primitiveGroups[i].Indices[j]]; if (cachedIndex == -1) //we haven't seen this index before { //point to "last" vertex in VB remappedPrimitiveGroups[i].Indices[j] = ( ushort )indexCtr; //add to index cache, increment indexCache[primitiveGroups[i].Indices[j]] = ( ushort )indexCtr++; } else { //we've seen this index before remappedPrimitiveGroups[i].Indices[j] = ( ushort )cachedIndex; } } } }
/// <summary> /// Generates strips from the given input indices. /// </summary> /// <param name="indices">Input index list, the indices you would use to render.</param> /// <param name="primitiveGroups">Array of optimized/stripified PrimitiveGroups</param> /// <param name="validateEnabled">Whether to validate the output or not.</param> /// <returns>A boolean indicating whether the operation completed successfully.</returns> public bool GenerateStrips(ushort[] indices, out PrimitiveGroup[] primitiveGroups, bool validateEnabled = false) { var numGroups = 0; //put data in format that the stripifier likes var tempIndices = new List <ushort>(indices.Length); ushort maxIndex = 0; ushort minIndex = 0xFFFF; for (var i = 0; i < indices.Length; i++) { tempIndices.Add(indices[i]); if (indices[i] > maxIndex) { maxIndex = indices[i]; } if (indices[i] < minIndex) { minIndex = indices[i]; } } var tempStrips = new List <StripInfo>(); var tempFaces = new List <FaceInfo>(); var stripifier = new Stripifier(); //do actual stripification stripifier.Stripify(tempIndices, CacheSize, MinStripSize, maxIndex, tempStrips, tempFaces); //stitch strips together var stripIndices = new List <int>(); uint numSeparateStrips = 0; if (ListsOnly) { //if we're outputting only lists, we're done numGroups = 1; primitiveGroups = new PrimitiveGroup[numGroups]; var primGroupArray = primitiveGroups; //count the total number of indices uint numIndices = 0; for (var i = 0; i < tempStrips.Count; i++) { numIndices += ( uint )(tempStrips[i].Faces.Count * 3); } //add in the list numIndices += ( uint )(tempFaces.Count * 3); primGroupArray[0] = new PrimitiveGroup(PrimitiveType.TriangleList, new ushort[numIndices]); //do strips uint indexCtr = 0; for (var i = 0; i < tempStrips.Count; i++) { for (var j = 0; j < tempStrips[i].Faces.Count; j++) { //degenerates are of no use with lists if (!Stripifier.IsDegenerate(tempStrips[i].Faces[j])) { primGroupArray[0].Indices[indexCtr++] = ( ushort )tempStrips[i].Faces[j].V0; primGroupArray[0].Indices[indexCtr++] = ( ushort )tempStrips[i].Faces[j].V1; primGroupArray[0].Indices[indexCtr++] = ( ushort )tempStrips[i].Faces[j].V2; } else { //we've removed a tri, reduce the number of indices var resizedIndices = primGroupArray[0].Indices; Array.Resize(ref resizedIndices, primGroupArray[0].Indices.Length - 3); primGroupArray[0].Indices = resizedIndices; } } } //do lists for (var i = 0; i < tempFaces.Count; i++) { primGroupArray[0].Indices[indexCtr++] = ( ushort )tempFaces[i].V0; primGroupArray[0].Indices[indexCtr++] = ( ushort )tempFaces[i].V1; primGroupArray[0].Indices[indexCtr++] = ( ushort )tempFaces[i].V2; } } else { stripifier.CreateStrips(tempStrips, stripIndices, StitchStrips, ref numSeparateStrips, UseRestart, ( uint )RestartValue); //if we're stitching strips together, we better get back only one strip from CreateStrips() Debug.Assert(StitchStrips && numSeparateStrips == 1 || !StitchStrips); //convert to output format numGroups = ( ushort )numSeparateStrips; //for the strips if (tempFaces.Count != 0) { numGroups++; //we've got a list as well, increment } primitiveGroups = new PrimitiveGroup[numGroups]; var primGroupArray = primitiveGroups; //first, the strips var startingLoc = 0; for (var stripCtr = 0; stripCtr < numSeparateStrips; stripCtr++) { var stripLength = 0; if (!StitchStrips) { //if we've got multiple strips, we need to figure out the correct length int i; for (i = startingLoc; i < stripIndices.Count; i++) { if (stripIndices[i] == -1) { break; } } stripLength = i - startingLoc; } else { stripLength = stripIndices.Count; } primGroupArray[stripCtr] = new PrimitiveGroup(PrimitiveType.TriangleStrip, new ushort[stripLength]); var indexCtr = 0; for (int i = startingLoc; i < stripLength + startingLoc; i++) { primGroupArray[stripCtr].Indices[indexCtr++] = ( ushort )stripIndices[i]; } //we add 1 to account for the -1 separating strips //this doesn't break the stitched case since we'll exit the loop startingLoc += stripLength + 1; } //next, the list if (tempFaces.Count != 0) { int faceGroupLoc = numGroups - 1; //the face group is the last one primGroupArray[faceGroupLoc] = new PrimitiveGroup(PrimitiveType.TriangleList, new ushort[tempFaces.Count * 3]); var indexCtr = 0; for (var i = 0; i < tempFaces.Count; i++) { primGroupArray[faceGroupLoc].Indices[indexCtr++] = ( ushort )tempFaces[i].V0; primGroupArray[faceGroupLoc].Indices[indexCtr++] = ( ushort )tempFaces[i].V1; primGroupArray[faceGroupLoc].Indices[indexCtr++] = ( ushort )tempFaces[i].V2; } } } //validate generated data against input if (validateEnabled) { var numbins = 100; var inBins = new List <FaceInfo> [numbins]; for (var i = 0; i < inBins.Length; ++i) { inBins[i] = new List <FaceInfo>(); } //hash input indices on first index for (var i = 0; i < indices.Length; i += 3) { var faceInfo = new FaceInfo(indices[i], indices[i + 1], indices[i + 2]); inBins[indices[i] % numbins].Add(faceInfo); } for (var i = 0; i < numGroups; ++i) { switch (primitiveGroups[i].Type) { case PrimitiveType.TriangleList: { for (var j = 0; j < primitiveGroups[i].Indices.Length; j += 3) { ushort v0 = primitiveGroups[i].Indices[j]; ushort v1 = primitiveGroups[i].Indices[j + 1]; ushort v2 = primitiveGroups[i].Indices[j + 2]; //ignore degenerates if (Stripifier.IsDegenerate(v0, v1, v2)) { continue; } if (!TestTriangle(v0, v1, v2, inBins, numbins)) { Cleanup(tempStrips, tempFaces); return(false); } } break; } case PrimitiveType.TriangleStrip: { var flip = false; for (var j = 2; j < primitiveGroups[i].Indices.Length; ++j) { ushort v0 = primitiveGroups[i].Indices[j - 2]; ushort v1 = primitiveGroups[i].Indices[j - 1]; ushort v2 = primitiveGroups[i].Indices[j]; if (flip) { //swap v1 and v2 ushort swap = v1; v1 = v2; v2 = swap; } //ignore degenerates if (Stripifier.IsDegenerate(v0, v1, v2)) { flip = !flip; continue; } if (!TestTriangle(v0, v1, v2, inBins, numbins)) { Cleanup(tempStrips, tempFaces); return(false); } flip = !flip; } break; } case PrimitiveType.TriangleFan: default: break; } } } //clean up everything Cleanup(tempStrips, tempFaces); return(true); }