babylon.groundMesh.ts 12 KB

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