RoundedBoxGeometry.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. ( function () {
  2. const _tempNormal = new THREE.Vector3();
  3. function getUv( faceDirVector, normal, uvAxis, projectionAxis, radius, sideLength ) {
  4. const totArcLength = 2 * Math.PI * radius / 4; // length of the planes between the arcs on each axis
  5. const centerLength = Math.max( sideLength - 2 * radius, 0 );
  6. const halfArc = Math.PI / 4; // Get the vector projected onto the Y plane
  7. _tempNormal.copy( normal );
  8. _tempNormal[ projectionAxis ] = 0;
  9. _tempNormal.normalize(); // total amount of UV space alloted to a single arc
  10. const arcUvRatio = 0.5 * totArcLength / ( totArcLength + centerLength ); // the distance along one arc the point is at
  11. const arcAngleRatio = 1.0 - _tempNormal.angleTo( faceDirVector ) / halfArc;
  12. if ( Math.sign( _tempNormal[ uvAxis ] ) === 1 ) {
  13. return arcAngleRatio * arcUvRatio;
  14. } else {
  15. // total amount of UV space alloted to the plane between the arcs
  16. const lenUv = centerLength / ( totArcLength + centerLength );
  17. return lenUv + arcUvRatio + arcUvRatio * ( 1.0 - arcAngleRatio );
  18. }
  19. }
  20. class RoundedBoxGeometry extends THREE.BoxGeometry {
  21. constructor( width = 1, height = 1, depth = 1, segments = 2, radius = 0.1 ) {
  22. // ensure segments is odd so we have a plane connecting the rounded corners
  23. segments = segments * 2 + 1; // ensure radius isn't bigger than shortest side
  24. radius = Math.min( width / 2, height / 2, depth / 2, radius );
  25. super( 1, 1, 1, segments, segments, segments ); // if we just have one segment we're the same as a regular box
  26. if ( segments === 1 ) return;
  27. const geometry2 = this.toNonIndexed();
  28. this.index = null;
  29. this.attributes.position = geometry2.attributes.position;
  30. this.attributes.normal = geometry2.attributes.normal;
  31. this.attributes.uv = geometry2.attributes.uv; //
  32. const position = new THREE.Vector3();
  33. const normal = new THREE.Vector3();
  34. const box = new THREE.Vector3( width, height, depth ).divideScalar( 2 ).subScalar( radius );
  35. const positions = this.attributes.position.array;
  36. const normals = this.attributes.normal.array;
  37. const uvs = this.attributes.uv.array;
  38. const faceTris = positions.length / 6;
  39. const faceDirVector = new THREE.Vector3();
  40. const halfSegmentSize = 0.5 / segments;
  41. for ( let i = 0, j = 0; i < positions.length; i += 3, j += 2 ) {
  42. position.fromArray( positions, i );
  43. normal.copy( position );
  44. normal.x -= Math.sign( normal.x ) * halfSegmentSize;
  45. normal.y -= Math.sign( normal.y ) * halfSegmentSize;
  46. normal.z -= Math.sign( normal.z ) * halfSegmentSize;
  47. normal.normalize();
  48. positions[ i + 0 ] = box.x * Math.sign( position.x ) + normal.x * radius;
  49. positions[ i + 1 ] = box.y * Math.sign( position.y ) + normal.y * radius;
  50. positions[ i + 2 ] = box.z * Math.sign( position.z ) + normal.z * radius;
  51. normals[ i + 0 ] = normal.x;
  52. normals[ i + 1 ] = normal.y;
  53. normals[ i + 2 ] = normal.z;
  54. const side = Math.floor( i / faceTris );
  55. switch ( side ) {
  56. case 0:
  57. // right
  58. // generate UVs along Z then Y
  59. faceDirVector.set( 1, 0, 0 );
  60. uvs[ j + 0 ] = getUv( faceDirVector, normal, 'z', 'y', radius, depth );
  61. uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'y', 'z', radius, height );
  62. break;
  63. case 1:
  64. // left
  65. // generate UVs along Z then Y
  66. faceDirVector.set( - 1, 0, 0 );
  67. uvs[ j + 0 ] = 1.0 - getUv( faceDirVector, normal, 'z', 'y', radius, depth );
  68. uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'y', 'z', radius, height );
  69. break;
  70. case 2:
  71. // top
  72. // generate UVs along X then Z
  73. faceDirVector.set( 0, 1, 0 );
  74. uvs[ j + 0 ] = 1.0 - getUv( faceDirVector, normal, 'x', 'z', radius, width );
  75. uvs[ j + 1 ] = getUv( faceDirVector, normal, 'z', 'x', radius, depth );
  76. break;
  77. case 3:
  78. // bottom
  79. // generate UVs along X then Z
  80. faceDirVector.set( 0, - 1, 0 );
  81. uvs[ j + 0 ] = 1.0 - getUv( faceDirVector, normal, 'x', 'z', radius, width );
  82. uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'z', 'x', radius, depth );
  83. break;
  84. case 4:
  85. // front
  86. // generate UVs along X then Y
  87. faceDirVector.set( 0, 0, 1 );
  88. uvs[ j + 0 ] = 1.0 - getUv( faceDirVector, normal, 'x', 'y', radius, width );
  89. uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'y', 'x', radius, height );
  90. break;
  91. case 5:
  92. // back
  93. // generate UVs along X then Y
  94. faceDirVector.set( 0, 0, - 1 );
  95. uvs[ j + 0 ] = getUv( faceDirVector, normal, 'x', 'y', radius, width );
  96. uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'y', 'x', radius, height );
  97. break;
  98. }
  99. }
  100. }
  101. }
  102. THREE.RoundedBoxGeometry = RoundedBoxGeometry;
  103. } )();