ParametricGeometry.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import {
  2. BufferGeometry,
  3. Float32BufferAttribute,
  4. Vector3
  5. } from 'three';
  6. /**
  7. * This class can be used to generate a geometry based on a parametric surface.
  8. *
  9. * Reference: [Mesh Generation with Python]{@link https://prideout.net/blog/old/blog/index.html@p=44.html}
  10. *
  11. * ```js
  12. * const geometry = new THREE.ParametricGeometry( klein, 25, 25 );
  13. * const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
  14. * const klein = new THREE.Mesh( geometry, material );
  15. * scene.add( klein );
  16. * ```
  17. *
  18. * @augments BufferGeometry
  19. * @three_import import { ParametricGeometry } from 'three/addons/geometries/ParametricGeometry.js';
  20. */
  21. class ParametricGeometry extends BufferGeometry {
  22. /**
  23. * Constructs a new parametric geometry.
  24. *
  25. * @param {ParametricGeometry~Func} func - The parametric function. Default is a function that generates a curved plane surface.
  26. * @param {number} [slices=8] - The number of slices to use for the parametric function.
  27. * @param {number} [stacks=8] - The stacks of slices to use for the parametric function.
  28. */
  29. constructor( func = ( u, v, target ) => target.set( u, v, Math.cos( u ) * Math.sin( v ) ), slices = 8, stacks = 8 ) {
  30. super();
  31. this.type = 'ParametricGeometry';
  32. /**
  33. * Holds the constructor parameters that have been
  34. * used to generate the geometry. Any modification
  35. * after instantiation does not change the geometry.
  36. *
  37. * @type {Object}
  38. */
  39. this.parameters = {
  40. func: func,
  41. slices: slices,
  42. stacks: stacks
  43. };
  44. // buffers
  45. const indices = [];
  46. const vertices = [];
  47. const normals = [];
  48. const uvs = [];
  49. const EPS = 0.00001;
  50. const normal = new Vector3();
  51. const p0 = new Vector3(), p1 = new Vector3();
  52. const pu = new Vector3(), pv = new Vector3();
  53. // generate vertices, normals and uvs
  54. const sliceCount = slices + 1;
  55. for ( let i = 0; i <= stacks; i ++ ) {
  56. const v = i / stacks;
  57. for ( let j = 0; j <= slices; j ++ ) {
  58. const u = j / slices;
  59. // vertex
  60. func( u, v, p0 );
  61. vertices.push( p0.x, p0.y, p0.z );
  62. // normal
  63. // approximate tangent vectors via finite differences
  64. if ( u - EPS >= 0 ) {
  65. func( u - EPS, v, p1 );
  66. pu.subVectors( p0, p1 );
  67. } else {
  68. func( u + EPS, v, p1 );
  69. pu.subVectors( p1, p0 );
  70. }
  71. if ( v - EPS >= 0 ) {
  72. func( u, v - EPS, p1 );
  73. pv.subVectors( p0, p1 );
  74. } else {
  75. func( u, v + EPS, p1 );
  76. pv.subVectors( p1, p0 );
  77. }
  78. // cross product of tangent vectors returns surface normal
  79. normal.crossVectors( pu, pv ).normalize();
  80. normals.push( normal.x, normal.y, normal.z );
  81. // uv
  82. uvs.push( u, v );
  83. }
  84. }
  85. // generate indices
  86. for ( let i = 0; i < stacks; i ++ ) {
  87. for ( let j = 0; j < slices; j ++ ) {
  88. const a = i * sliceCount + j;
  89. const b = i * sliceCount + j + 1;
  90. const c = ( i + 1 ) * sliceCount + j + 1;
  91. const d = ( i + 1 ) * sliceCount + j;
  92. // faces one and two
  93. indices.push( a, b, d );
  94. indices.push( b, c, d );
  95. }
  96. }
  97. // build geometry
  98. this.setIndex( indices );
  99. this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
  100. this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
  101. this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
  102. }
  103. copy( source ) {
  104. super.copy( source );
  105. this.parameters = Object.assign( {}, source.parameters );
  106. return this;
  107. }
  108. }
  109. /**
  110. * Parametric function definition of `ParametricGeometry`.
  111. *
  112. * @callback ParametricGeometry~Func
  113. * @param {number} u - The `u` coordinate on the surface in the range `[0,1]`.
  114. * @param {number} v - The `v` coordinate on the surface in the range `[0,1]`.
  115. * @param {Vector3} target - The target vector that is used to store the method's result.
  116. */
  117. export { ParametricGeometry };