babylon.groundMesh.ts 11 KB

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