babylon.groundMesh.ts 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. module BABYLON {
  2. export class GroundMesh extends Mesh {
  3. public generateOctree = false;
  4. private _worldInverse = new Matrix();
  5. private _heightQuads: { slope: Vector2; facet1: Vector4; facet2: Vector4 }[];
  6. public _subdivisionsX: number;
  7. public _subdivisionsY: number;
  8. public _width: number;
  9. public _height: number;
  10. public _minX: number;
  11. public _maxX: number;
  12. public _minZ: number;
  13. public _maxZ: number;
  14. constructor(name: string, scene: Scene) {
  15. super(name, scene);
  16. }
  17. public getClassName(): string {
  18. return "GroundMesh";
  19. }
  20. public get subdivisions(): number {
  21. return Math.min(this._subdivisionsX, this._subdivisionsY);
  22. }
  23. public get subdivisionsX(): number {
  24. return this._subdivisionsX;
  25. }
  26. public get subdivisionsY(): number {
  27. return this._subdivisionsY;
  28. }
  29. public optimize(chunksCount: number, octreeBlocksSize = 32): void {
  30. this._subdivisionsX = chunksCount;
  31. this._subdivisionsY = chunksCount;
  32. this.subdivide(chunksCount);
  33. this.createOrUpdateSubmeshesOctree(octreeBlocksSize);
  34. }
  35. /**
  36. * Returns a height (y) value in the Worl system :
  37. * the ground altitude at the coordinates (x, z) expressed in the World system.
  38. * Returns the ground y position if (x, z) are outside the ground surface.
  39. * Not pertinent if the ground is rotated.
  40. */
  41. public getHeightAtCoordinates(x: number, z: number): number {
  42. // express x and y in the ground local system
  43. x -= this.position.x;
  44. z -= this.position.z;
  45. x /= this.scaling.x;
  46. z /= this.scaling.z;
  47. if (x < this._minX || x > this._maxX || z < this._minZ || z > this._maxZ) {
  48. return this.position.y;
  49. }
  50. if (!this._heightQuads || this._heightQuads.length == 0) {
  51. this._initHeightQuads();
  52. this._computeHeightQuads();
  53. }
  54. var facet = this._getFacetAt(x, z);
  55. var y = -(facet.x * x + facet.z * z + facet.w) / facet.y;
  56. // return y in the World system
  57. return y * this.scaling.y + this.position.y;
  58. }
  59. /**
  60. * Returns a normalized vector (Vector3) orthogonal to the ground
  61. * at the ground coordinates (x, z) expressed in the World system.
  62. * Returns Vector3(0, 1, 0) if (x, z) are outside the ground surface.
  63. * Not pertinent if the ground is rotated.
  64. */
  65. public getNormalAtCoordinates(x: number, z: number): Vector3 {
  66. var normal = new Vector3(0, 1, 0);
  67. this.getNormalAtCoordinatesToRef(x, z, normal);
  68. return normal;
  69. }
  70. /**
  71. * Updates the Vector3 passed a reference with a normalized vector orthogonal to the ground
  72. * at the ground coordinates (x, z) expressed in the World system.
  73. * Doesn't uptade the reference Vector3 if (x, z) are outside the ground surface.
  74. * Not pertinent if the ground is rotated.
  75. */
  76. public getNormalAtCoordinatesToRef(x: number, z: number, ref: Vector3): void {
  77. // express x and y in the ground local system
  78. x -= this.position.x;
  79. z -= this.position.z;
  80. x /= this.scaling.x;
  81. z /= this.scaling.z;
  82. if (x < this._minX || x > this._maxX || z < this._minZ || z > this._maxZ) {
  83. return;
  84. }
  85. if (!this._heightQuads || this._heightQuads.length == 0) {
  86. this._initHeightQuads();
  87. this._computeHeightQuads();
  88. }
  89. var facet = this._getFacetAt(x, z);
  90. ref.x = facet.x;
  91. ref.y = facet.y;
  92. ref.z = facet.z;
  93. }
  94. /**
  95. * Force the heights to be recomputed for getHeightAtCoordinates() or getNormalAtCoordinates()
  96. * if the ground has been updated.
  97. * This can be used in the render loop
  98. */
  99. public updateCoordinateHeights(): void {
  100. if (!this._heightQuads || this._heightQuads.length == 0) {
  101. this._initHeightQuads();
  102. }
  103. this._computeHeightQuads();
  104. }
  105. // Returns the element "facet" from the heightQuads array relative to (x, z) local coordinates
  106. private _getFacetAt(x: number, z: number): Vector4 {
  107. // retrieve col and row from x, z coordinates in the ground local system
  108. var subdivisionsX = this._subdivisionsX;
  109. var subdivisionsY = this._subdivisionsY;
  110. var col = Math.floor((x + this._maxX) * this._subdivisionsX / this._width);
  111. var row = Math.floor(-(z + this._maxZ) * this._subdivisionsY / this._height + this._subdivisionsY);
  112. var quad = this._heightQuads[row * this._subdivisionsX + col];
  113. var facet;
  114. if (z < quad.slope.x * x + quad.slope.y) {
  115. facet = quad.facet1;
  116. } else {
  117. facet = quad.facet2;
  118. }
  119. return facet;
  120. }
  121. // Creates and populates the heightMap array with "facet" elements :
  122. // a quad is two triangular facets separated by a slope, so a "facet" element is 1 slope + 2 facets
  123. // slope : Vector2(c, h) = 2D diagonal line equation setting appart two triangular facets in a quad : z = cx + h
  124. // facet1 : Vector4(a, b, c, d) = first facet 3D plane equation : ax + by + cz + d = 0
  125. // facet2 : Vector4(a, b, c, d) = second facet 3D plane equation : ax + by + cz + d = 0
  126. private _initHeightQuads(): void {
  127. var subdivisionsX = this._subdivisionsX;
  128. var subdivisionsY = this._subdivisionsY;
  129. this._heightQuads = new Array();
  130. for (var row = 0; row < subdivisionsY; row++) {
  131. for (var col = 0; col < subdivisionsX; col++) {
  132. var quad = { slope: BABYLON.Vector2.Zero(), facet1: new BABYLON.Vector4(0, 0, 0, 0), facet2: new BABYLON.Vector4(0, 0, 0, 0) };
  133. this._heightQuads[row * subdivisionsX + col] = quad;
  134. }
  135. }
  136. }
  137. // Compute each quad element values and update the the heightMap array :
  138. // slope : Vector2(c, h) = 2D diagonal line equation setting appart two triangular facets in a quad : z = cx + h
  139. // facet1 : Vector4(a, b, c, d) = first facet 3D plane equation : ax + by + cz + d = 0
  140. // facet2 : Vector4(a, b, c, d) = second facet 3D plane equation : ax + by + cz + d = 0
  141. private _computeHeightQuads(): void {
  142. var positions = this.getVerticesData(VertexBuffer.PositionKind);
  143. var v1 = Tmp.Vector3[3];
  144. var v2 = Tmp.Vector3[2];
  145. var v3 = Tmp.Vector3[1];
  146. var v4 = Tmp.Vector3[0];
  147. var v1v2 = Tmp.Vector3[4];
  148. var v1v3 = Tmp.Vector3[5];
  149. var v1v4 = Tmp.Vector3[6];
  150. var norm1 = Tmp.Vector3[7];
  151. var norm2 = Tmp.Vector3[8];
  152. var i = 0;
  153. var j = 0;
  154. var k = 0;
  155. var cd = 0; // 2D slope coefficient : z = cd * x + h
  156. var h = 0;
  157. var d1 = 0; // facet plane equation : ax + by + cz + d = 0
  158. var d2 = 0;
  159. var subdivisionsX = this._subdivisionsX;
  160. var subdivisionsY = this._subdivisionsY;
  161. for (var row = 0; row < subdivisionsY; row++) {
  162. for (var col = 0; col < subdivisionsX; col++) {
  163. i = col * 3;
  164. j = row * (subdivisionsX + 1) * 3;
  165. k = (row + 1) * (subdivisionsX + 1) * 3;
  166. v1.x = positions[j + i];
  167. v1.y = positions[j + i + 1];
  168. v1.z = positions[j + i + 2];
  169. v2.x = positions[j + i + 3];
  170. v2.y = positions[j + i + 4];
  171. v2.z = positions[j + i + 5];
  172. v3.x = positions[k + i];
  173. v3.y = positions[k + i + 1];
  174. v3.z = positions[k + i + 2];
  175. v4.x = positions[k + i + 3];
  176. v4.y = positions[k + i + 4];
  177. v4.z = positions[k + i + 5];
  178. // 2D slope V1V4
  179. cd = (v4.z - v1.z) / (v4.x - v1.x);
  180. h = v1.z - cd * v1.x; // v1 belongs to the slope
  181. // facet equations :
  182. // we compute each facet normal vector
  183. // the equation of the facet plane is : norm.x * x + norm.y * y + norm.z * z + d = 0
  184. // we compute the value d by applying the equation to v1 which belongs to the plane
  185. // then we store the facet equation in a Vector4
  186. v2.subtractToRef(v1, v1v2);
  187. v3.subtractToRef(v1, v1v3);
  188. v4.subtractToRef(v1, v1v4);
  189. Vector3.CrossToRef(v1v4, v1v3, norm1); // caution : CrossToRef uses the Tmp class
  190. Vector3.CrossToRef(v1v2, v1v4, norm2);
  191. norm1.normalize();
  192. norm2.normalize();
  193. d1 = -(norm1.x * v1.x + norm1.y * v1.y + norm1.z * v1.z);
  194. d2 = -(norm2.x * v2.x + norm2.y * v2.y + norm2.z * v2.z);
  195. var quad = this._heightQuads[row * subdivisionsX + col];
  196. quad.slope.copyFromFloats(cd, h);
  197. quad.facet1.copyFromFloats(norm1.x, norm1.y, norm1.z, d1);
  198. quad.facet2.copyFromFloats(norm2.x, norm2.y, norm2.z, d2);
  199. }
  200. }
  201. }
  202. }
  203. }