PolyhedronBufferGeometry.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. import { BufferGeometry } from '../core/BufferGeometry.js';
  2. import { Float32BufferAttribute } from '../core/BufferAttribute.js';
  3. import { Vector3 } from '../math/Vector3.js';
  4. import { Vector2 } from '../math/Vector2.js';
  5. class PolyhedronBufferGeometry extends BufferGeometry {
  6. constructor( vertices, indices, radius, detail ) {
  7. super();
  8. this.type = 'PolyhedronBufferGeometry';
  9. this.parameters = {
  10. vertices: vertices,
  11. indices: indices,
  12. radius: radius,
  13. detail: detail
  14. };
  15. radius = radius || 1;
  16. detail = detail || 0;
  17. // default buffer data
  18. const vertexBuffer = [];
  19. const uvBuffer = [];
  20. // the subdivision creates the vertex buffer data
  21. subdivide( detail );
  22. // all vertices should lie on a conceptual sphere with a given radius
  23. applyRadius( radius );
  24. // finally, create the uv data
  25. generateUVs();
  26. // build non-indexed geometry
  27. this.setAttribute( 'position', new Float32BufferAttribute( vertexBuffer, 3 ) );
  28. this.setAttribute( 'normal', new Float32BufferAttribute( vertexBuffer.slice(), 3 ) );
  29. this.setAttribute( 'uv', new Float32BufferAttribute( uvBuffer, 2 ) );
  30. if ( detail === 0 ) {
  31. this.computeVertexNormals(); // flat normals
  32. } else {
  33. this.normalizeNormals(); // smooth normals
  34. }
  35. // helper functions
  36. function subdivide( detail ) {
  37. const a = new Vector3();
  38. const b = new Vector3();
  39. const c = new Vector3();
  40. // iterate over all faces and apply a subdivison with the given detail value
  41. for ( let i = 0; i < indices.length; i += 3 ) {
  42. // get the vertices of the face
  43. getVertexByIndex( indices[ i + 0 ], a );
  44. getVertexByIndex( indices[ i + 1 ], b );
  45. getVertexByIndex( indices[ i + 2 ], c );
  46. // perform subdivision
  47. subdivideFace( a, b, c, detail );
  48. }
  49. }
  50. function subdivideFace( a, b, c, detail ) {
  51. const cols = detail + 1;
  52. // we use this multidimensional array as a data structure for creating the subdivision
  53. const v = [];
  54. // construct all of the vertices for this subdivision
  55. for ( let i = 0; i <= cols; i ++ ) {
  56. v[ i ] = [];
  57. const aj = a.clone().lerp( c, i / cols );
  58. const bj = b.clone().lerp( c, i / cols );
  59. const rows = cols - i;
  60. for ( let j = 0; j <= rows; j ++ ) {
  61. if ( j === 0 && i === cols ) {
  62. v[ i ][ j ] = aj;
  63. } else {
  64. v[ i ][ j ] = aj.clone().lerp( bj, j / rows );
  65. }
  66. }
  67. }
  68. // construct all of the faces
  69. for ( let i = 0; i < cols; i ++ ) {
  70. for ( let j = 0; j < 2 * ( cols - i ) - 1; j ++ ) {
  71. const k = Math.floor( j / 2 );
  72. if ( j % 2 === 0 ) {
  73. pushVertex( v[ i ][ k + 1 ] );
  74. pushVertex( v[ i + 1 ][ k ] );
  75. pushVertex( v[ i ][ k ] );
  76. } else {
  77. pushVertex( v[ i ][ k + 1 ] );
  78. pushVertex( v[ i + 1 ][ k + 1 ] );
  79. pushVertex( v[ i + 1 ][ k ] );
  80. }
  81. }
  82. }
  83. }
  84. function applyRadius( radius ) {
  85. const vertex = new Vector3();
  86. // iterate over the entire buffer and apply the radius to each vertex
  87. for ( let i = 0; i < vertexBuffer.length; i += 3 ) {
  88. vertex.x = vertexBuffer[ i + 0 ];
  89. vertex.y = vertexBuffer[ i + 1 ];
  90. vertex.z = vertexBuffer[ i + 2 ];
  91. vertex.normalize().multiplyScalar( radius );
  92. vertexBuffer[ i + 0 ] = vertex.x;
  93. vertexBuffer[ i + 1 ] = vertex.y;
  94. vertexBuffer[ i + 2 ] = vertex.z;
  95. }
  96. }
  97. function generateUVs() {
  98. const vertex = new Vector3();
  99. for ( let i = 0; i < vertexBuffer.length; i += 3 ) {
  100. vertex.x = vertexBuffer[ i + 0 ];
  101. vertex.y = vertexBuffer[ i + 1 ];
  102. vertex.z = vertexBuffer[ i + 2 ];
  103. const u = azimuth( vertex ) / 2 / Math.PI + 0.5;
  104. const v = inclination( vertex ) / Math.PI + 0.5;
  105. uvBuffer.push( u, 1 - v );
  106. }
  107. correctUVs();
  108. correctSeam();
  109. }
  110. function correctSeam() {
  111. // handle case when face straddles the seam, see #3269
  112. for ( let i = 0; i < uvBuffer.length; i += 6 ) {
  113. // uv data of a single face
  114. const x0 = uvBuffer[ i + 0 ];
  115. const x1 = uvBuffer[ i + 2 ];
  116. const x2 = uvBuffer[ i + 4 ];
  117. const max = Math.max( x0, x1, x2 );
  118. const min = Math.min( x0, x1, x2 );
  119. // 0.9 is somewhat arbitrary
  120. if ( max > 0.9 && min < 0.1 ) {
  121. if ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1;
  122. if ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1;
  123. if ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1;
  124. }
  125. }
  126. }
  127. function pushVertex( vertex ) {
  128. vertexBuffer.push( vertex.x, vertex.y, vertex.z );
  129. }
  130. function getVertexByIndex( index, vertex ) {
  131. const stride = index * 3;
  132. vertex.x = vertices[ stride + 0 ];
  133. vertex.y = vertices[ stride + 1 ];
  134. vertex.z = vertices[ stride + 2 ];
  135. }
  136. function correctUVs() {
  137. const a = new Vector3();
  138. const b = new Vector3();
  139. const c = new Vector3();
  140. const centroid = new Vector3();
  141. const uvA = new Vector2();
  142. const uvB = new Vector2();
  143. const uvC = new Vector2();
  144. for ( let i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) {
  145. a.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] );
  146. b.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] );
  147. c.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] );
  148. uvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] );
  149. uvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] );
  150. uvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] );
  151. centroid.copy( a ).add( b ).add( c ).divideScalar( 3 );
  152. const azi = azimuth( centroid );
  153. correctUV( uvA, j + 0, a, azi );
  154. correctUV( uvB, j + 2, b, azi );
  155. correctUV( uvC, j + 4, c, azi );
  156. }
  157. }
  158. function correctUV( uv, stride, vector, azimuth ) {
  159. if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) {
  160. uvBuffer[ stride ] = uv.x - 1;
  161. }
  162. if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) {
  163. uvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5;
  164. }
  165. }
  166. // Angle around the Y axis, counter-clockwise when looking from above.
  167. function azimuth( vector ) {
  168. return Math.atan2( vector.z, - vector.x );
  169. }
  170. // Angle above the XZ plane.
  171. function inclination( vector ) {
  172. return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) );
  173. }
  174. }
  175. }
  176. export { PolyhedronBufferGeometry };