indexbuffercompression.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. /*
  2. Copyright (c) 2014, Conor Stokes
  3. All rights reserved.
  4. Redistribution and use in source and binary forms, with or without
  5. modification, are permitted provided that the following conditions are met:
  6. 1. Redistributions of source code must retain the above copyright notice, this
  7. list of conditions and the following disclaimer.
  8. 2. Redistributions in binary form must reproduce the above copyright notice,
  9. this list of conditions and the following disclaimer in the documentation
  10. and/or other materials provided with the distribution.
  11. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  12. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  13. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  14. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  15. ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  16. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  17. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  18. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  19. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  20. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  21. */
  22. #include "IndexBufferCompression.h"
  23. #include "WriteBitstream.h"
  24. #include "IndexCompressionConstants.h"
  25. #include <assert.h>
  26. #ifdef _MSC_VER
  27. #define IBC_INLINE __forceinline
  28. #else
  29. #define IBC_INLINE __attribute__((always_inline))
  30. #endif
  31. const uint32_t VERTEX_NOT_MAPPED = 0xFFFFFFFF;
  32. // Output the compression information for a single vertex, remapping any new vertices and updating the vertex fifo where needed.
  33. static IBC_INLINE void OutputVertex( uint32_t vertex,
  34. uint32_t* vertexRemap,
  35. uint32_t& newVertexCount,
  36. uint32_t* vertexFifo,
  37. uint32_t& verticesRead,
  38. WriteBitstream& output )
  39. {
  40. // Check if a vertex hasn't been remapped,
  41. if ( vertexRemap[ vertex ] == VERTEX_NOT_MAPPED )
  42. {
  43. // no remap, so remap to the current high watermark and output a new vertex code.
  44. vertexRemap[ vertex ] = newVertexCount;
  45. output.Write( IB_NEW_VERTEX, IB_CODE_BITS );
  46. ++newVertexCount;
  47. // new vertices go into the vertex FIFO
  48. vertexFifo[ verticesRead & VERTEX_FIFO_MASK ] = vertex;
  49. ++verticesRead;
  50. }
  51. else
  52. {
  53. int32_t lowestVertexCursor = verticesRead >= VERTEX_FIFO_SIZE ? verticesRead - VERTEX_FIFO_SIZE : 0;
  54. // Probe backwards in the vertex FIFO for a cached vertex
  55. for ( int32_t vertexCursor = verticesRead - 1; vertexCursor >= lowestVertexCursor; --vertexCursor )
  56. {
  57. if ( vertexFifo[ vertexCursor & VERTEX_FIFO_MASK ] == vertex )
  58. {
  59. // found a cached vertex, so write out the code for a cached vertex, as the relative index into the fifo.
  60. output.Write( IB_CACHED_VERTEX, IB_CODE_BITS );
  61. output.Write( ( verticesRead - 1 ) - vertexCursor, CACHED_VERTEX_BITS );
  62. return;
  63. }
  64. }
  65. // no cached vertex found, so write out a free vertex
  66. output.Write( IB_FREE_VERTEX, IB_CODE_BITS );
  67. // free vertices are relative to the latest new vertex.
  68. uint32_t vertexOutput = ( newVertexCount - 1 ) - vertexRemap[ vertex ];
  69. // v-int encode the free vertex index.
  70. do
  71. {
  72. uint32_t lower7 = vertexOutput & 0x7F;
  73. vertexOutput >>= 7;
  74. output.Write( lower7 | ( vertexOutput > 0 ? 0x80 : 0 ), 8 );
  75. } while ( vertexOutput > 0 );
  76. // free vertices go back into the vertex cache.
  77. vertexFifo[ verticesRead & VERTEX_FIFO_MASK ] = vertex;
  78. ++verticesRead;
  79. }
  80. }
  81. template <typename Ty>
  82. void CompressIndexBuffer( const Ty* triangles,
  83. uint32_t triangleCount,
  84. uint32_t* vertexRemap,
  85. uint32_t vertexCount,
  86. WriteBitstream& output )
  87. {
  88. Edge edgeFifo[ EDGE_FIFO_SIZE ];
  89. uint32_t vertexFifo[ VERTEX_FIFO_SIZE ];
  90. uint32_t edgesRead = 0;
  91. uint32_t verticesRead = 0;
  92. uint32_t newVertices = 0;
  93. const Ty* triangleEnd = triangles + ( triangleCount * 3 );
  94. assert( vertexCount < 0xFFFFFFFF );
  95. uint32_t* vertexRemapEnd = vertexRemap + vertexCount;
  96. // clear the vertex remapping to "not found" value of 0xFFFFFFFF - dirty, but low overhead.
  97. for (uint32_t* remappedVertex = vertexRemap; remappedVertex < vertexRemapEnd; ++remappedVertex )
  98. {
  99. *remappedVertex = VERTEX_NOT_MAPPED;
  100. }
  101. // iterate through the triangles
  102. for (const Ty* triangle = triangles; triangle < triangleEnd; triangle += 3 )
  103. {
  104. int32_t lowestEdgeCursor = edgesRead >= EDGE_FIFO_SIZE ? edgesRead - EDGE_FIFO_SIZE : 0;
  105. int32_t edgeCursor = edgesRead - 1;
  106. bool foundEdge = false;
  107. int32_t freeVertex;
  108. // Probe back through the edge fifo to see if one of the triangle edges is in the FIFO
  109. for ( ; edgeCursor >= lowestEdgeCursor; --edgeCursor )
  110. {
  111. const Edge& edge = edgeFifo[ edgeCursor & VERTEX_FIFO_MASK ];
  112. // check all the edges in order and save the free vertex.
  113. if ( edge.second == triangle[ 0 ] && edge.first == triangle[ 1 ] )
  114. {
  115. foundEdge = true;
  116. freeVertex = 2;
  117. break;
  118. }
  119. else if ( edge.second == triangle[ 1 ] && edge.first == triangle[ 2 ] )
  120. {
  121. foundEdge = true;
  122. freeVertex = 0;
  123. break;
  124. }
  125. else if ( edge.second == triangle[ 2 ] && edge.first == triangle[ 0 ] )
  126. {
  127. foundEdge = true;
  128. freeVertex = 1;
  129. break;
  130. }
  131. }
  132. // we found an edge so write it out, then output the vertex
  133. if ( foundEdge )
  134. {
  135. output.Write( IB_CACHED_EDGE, IB_CODE_BITS );
  136. output.Write( ( edgesRead - 1 ) - edgeCursor, CACHED_EDGE_BITS );
  137. const Edge& edge = edgeFifo[ edgeCursor & EDGE_FIFO_MASK ];
  138. OutputVertex( triangle[ freeVertex ], vertexRemap, newVertices, vertexFifo, verticesRead, output );
  139. // edge is in reverse order to last triangle it occured on (and it will only be a match if this is the case).
  140. // so put the vertices into the fifo in that order.
  141. vertexFifo[ verticesRead & VERTEX_FIFO_MASK ] = edge.second;
  142. ++verticesRead;
  143. vertexFifo[ verticesRead & VERTEX_FIFO_MASK ] = edge.first;
  144. ++verticesRead;
  145. // Populate the edge fifo with the the remaining edges
  146. // Note - the winding order is important as we'll need to re-produce this on decompression.
  147. // The edges are put in as if the found edge is the first edge in the triangle (which it will be when we
  148. // reconstruct).
  149. switch ( freeVertex )
  150. {
  151. case 0:
  152. edgeFifo[ edgesRead & EDGE_FIFO_MASK ].set(triangle[ 2 ], triangle[ 0 ]);
  153. ++edgesRead;
  154. edgeFifo[ edgesRead & EDGE_FIFO_MASK ].set(triangle[ 0 ], triangle[ 1 ]);
  155. ++edgesRead;
  156. break;
  157. case 1:
  158. edgeFifo[ edgesRead & EDGE_FIFO_MASK ].set(triangle[ 0 ], triangle[ 1 ]);
  159. ++edgesRead;
  160. edgeFifo[ edgesRead & EDGE_FIFO_MASK ].set(triangle[ 1 ], triangle[ 2 ]);
  161. ++edgesRead;
  162. break;
  163. case 2:
  164. edgeFifo[ edgesRead & EDGE_FIFO_MASK ].set(triangle[ 1 ], triangle[ 2 ]);
  165. ++edgesRead;
  166. edgeFifo[ edgesRead & EDGE_FIFO_MASK ].set(triangle[ 2 ], triangle[ 0 ]);
  167. ++edgesRead;
  168. break;
  169. }
  170. }
  171. else
  172. {
  173. // no edge, so we need to output all the vertices.
  174. OutputVertex( triangle[ 0 ], vertexRemap, newVertices, vertexFifo, verticesRead, output );
  175. OutputVertex( triangle[ 1 ], vertexRemap, newVertices, vertexFifo, verticesRead, output );
  176. OutputVertex( triangle[ 2 ], vertexRemap, newVertices, vertexFifo, verticesRead, output );
  177. // populate the edge fifo with the 3 most recent edges
  178. edgeFifo[ edgesRead & EDGE_FIFO_MASK ].set(triangle[ 0 ], triangle[ 1 ]);
  179. ++edgesRead;
  180. edgeFifo[ edgesRead & EDGE_FIFO_MASK ].set(triangle[ 1 ], triangle[ 2 ]);
  181. ++edgesRead;
  182. edgeFifo[ edgesRead & EDGE_FIFO_MASK ].set(triangle[ 2 ], triangle[ 0 ]);
  183. ++edgesRead;
  184. }
  185. }
  186. }
  187. void CompressIndexBuffer ( const uint16_t* triangles,
  188. uint32_t triangleCount,
  189. uint32_t* vertexRemap,
  190. uint32_t vertexCount,
  191. WriteBitstream& output )
  192. {
  193. CompressIndexBuffer<uint16_t>(triangles, triangleCount, vertexRemap, vertexCount, output);
  194. }
  195. void CompressIndexBuffer ( const uint32_t* triangles,
  196. uint32_t triangleCount,
  197. uint32_t* vertexRemap,
  198. uint32_t vertexCount,
  199. WriteBitstream& output )
  200. {
  201. CompressIndexBuffer<uint32_t>(triangles, triangleCount, vertexRemap, vertexCount, output);
  202. }