decalBuilder.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. import { Nullable, IndicesArray } from "../../types";
  2. import { Vector3, Matrix, Vector2 } from "../../Maths/math.vector";
  3. import { Mesh, _CreationDataStorage } from "../mesh";
  4. import { VertexBuffer } from "../buffer";
  5. import { VertexData } from "../mesh.vertexData";
  6. import { AbstractMesh } from "../abstractMesh";
  7. import { Camera } from "../../Cameras/camera";
  8. import { PositionNormalTextureVertex } from '../../Maths/math.vertexFormat';
  9. Mesh.CreateDecal = (name: string, sourceMesh: AbstractMesh, position: Vector3, normal: Vector3, size: Vector3, angle: number): Mesh => {
  10. var options = {
  11. position: position,
  12. normal: normal,
  13. size: size,
  14. angle: angle
  15. };
  16. return DecalBuilder.CreateDecal(name, sourceMesh, options);
  17. };
  18. /**
  19. * Class containing static functions to help procedurally build meshes
  20. */
  21. export class DecalBuilder {
  22. /**
  23. * Creates a decal mesh.
  24. * A decal is a mesh usually applied as a model onto the surface of another mesh. So don't forget the parameter `sourceMesh` depicting the decal
  25. * * The parameter `position` (Vector3, default `(0, 0, 0)`) sets the position of the decal in World coordinates
  26. * * The parameter `normal` (Vector3, default `Vector3.Up`) sets the normal of the mesh where the decal is applied onto in World coordinates
  27. * * The parameter `size` (Vector3, default `(1, 1, 1)`) sets the decal scaling
  28. * * The parameter `angle` (float in radian, default 0) sets the angle to rotate the decal
  29. * * The parameter `captureUVS` defines if we need to capture the uvs or compute them
  30. * @param name defines the name of the mesh
  31. * @param sourceMesh defines the mesh where the decal must be applied
  32. * @param options defines the options used to create the mesh
  33. * @param scene defines the hosting scene
  34. * @returns the decal mesh
  35. * @see https://doc.babylonjs.com/how_to/decals
  36. */
  37. public static CreateDecal(name: string, sourceMesh: AbstractMesh, options: { position?: Vector3, normal?: Vector3, size?: Vector3, angle?: number, captureUVS?: boolean }): Mesh {
  38. var indices = <IndicesArray>sourceMesh.getIndices();
  39. var positions = sourceMesh.getVerticesData(VertexBuffer.PositionKind);
  40. var normals = sourceMesh.getVerticesData(VertexBuffer.NormalKind);
  41. var uvs = sourceMesh.getVerticesData(VertexBuffer.UVKind);
  42. var position = options.position || Vector3.Zero();
  43. var normal = options.normal || Vector3.Up();
  44. var size = options.size || Vector3.One();
  45. var angle = options.angle || 0;
  46. // Getting correct rotation
  47. if (!normal) {
  48. var target = new Vector3(0, 0, 1);
  49. var camera = <Camera>sourceMesh.getScene().activeCamera;
  50. var cameraWorldTarget = Vector3.TransformCoordinates(target, camera.getWorldMatrix());
  51. normal = camera.globalPosition.subtract(cameraWorldTarget);
  52. }
  53. var yaw = -Math.atan2(normal.z, normal.x) - Math.PI / 2;
  54. var len = Math.sqrt(normal.x * normal.x + normal.z * normal.z);
  55. var pitch = Math.atan2(normal.y, len);
  56. // Matrix
  57. var decalWorldMatrix = Matrix.RotationYawPitchRoll(yaw, pitch, angle).multiply(Matrix.Translation(position.x, position.y, position.z));
  58. var inverseDecalWorldMatrix = Matrix.Invert(decalWorldMatrix);
  59. var meshWorldMatrix = sourceMesh.getWorldMatrix();
  60. var transformMatrix = meshWorldMatrix.multiply(inverseDecalWorldMatrix);
  61. var vertexData = new VertexData();
  62. vertexData.indices = [];
  63. vertexData.positions = [];
  64. vertexData.normals = [];
  65. vertexData.uvs = [];
  66. var currentVertexDataIndex = 0;
  67. var extractDecalVector3 = (indexId: number): PositionNormalTextureVertex => {
  68. var result = new PositionNormalTextureVertex();
  69. if (!indices || !positions || !normals) {
  70. return result;
  71. }
  72. var vertexId = indices[indexId];
  73. result.position = new Vector3(positions[vertexId * 3], positions[vertexId * 3 + 1], positions[vertexId * 3 + 2]);
  74. // Send vector to decal local world
  75. result.position = Vector3.TransformCoordinates(result.position, transformMatrix);
  76. // Get normal
  77. result.normal = new Vector3(normals[vertexId * 3], normals[vertexId * 3 + 1], normals[vertexId * 3 + 2]);
  78. result.normal = Vector3.TransformNormal(result.normal, transformMatrix);
  79. if (options.captureUVS && uvs) {
  80. result.uv = new Vector2(uvs[vertexId * 2], uvs[vertexId * 2 + 1]);
  81. }
  82. return result;
  83. }; // Inspired by https://github.com/mrdoob/three.js/blob/eee231960882f6f3b6113405f524956145148146/examples/js/geometries/DecalGeometry.js
  84. var clip = (vertices: PositionNormalTextureVertex[], axis: Vector3): PositionNormalTextureVertex[] => {
  85. if (vertices.length === 0) {
  86. return vertices;
  87. }
  88. var clipSize = 0.5 * Math.abs(Vector3.Dot(size, axis));
  89. var clipVertices = (v0: PositionNormalTextureVertex, v1: PositionNormalTextureVertex): PositionNormalTextureVertex => {
  90. var clipFactor = Vector3.GetClipFactor(v0.position, v1.position, axis, clipSize);
  91. return new PositionNormalTextureVertex(
  92. Vector3.Lerp(v0.position, v1.position, clipFactor),
  93. Vector3.Lerp(v0.normal, v1.normal, clipFactor)
  94. );
  95. };
  96. var result = new Array<PositionNormalTextureVertex>();
  97. for (var index = 0; index < vertices.length; index += 3) {
  98. var v1Out: boolean;
  99. var v2Out: boolean;
  100. var v3Out: boolean;
  101. var total = 0;
  102. let nV1: Nullable<PositionNormalTextureVertex> = null;
  103. let nV2: Nullable<PositionNormalTextureVertex> = null;
  104. let nV3: Nullable<PositionNormalTextureVertex> = null;
  105. let nV4: Nullable<PositionNormalTextureVertex> = null;
  106. var d1 = Vector3.Dot(vertices[index].position, axis) - clipSize;
  107. var d2 = Vector3.Dot(vertices[index + 1].position, axis) - clipSize;
  108. var d3 = Vector3.Dot(vertices[index + 2].position, axis) - clipSize;
  109. v1Out = d1 > 0;
  110. v2Out = d2 > 0;
  111. v3Out = d3 > 0;
  112. total = (v1Out ? 1 : 0) + (v2Out ? 1 : 0) + (v3Out ? 1 : 0);
  113. switch (total) {
  114. case 0:
  115. result.push(vertices[index]);
  116. result.push(vertices[index + 1]);
  117. result.push(vertices[index + 2]);
  118. break;
  119. case 1:
  120. if (v1Out) {
  121. nV1 = vertices[index + 1];
  122. nV2 = vertices[index + 2];
  123. nV3 = clipVertices(vertices[index], nV1);
  124. nV4 = clipVertices(vertices[index], nV2);
  125. }
  126. if (v2Out) {
  127. nV1 = vertices[index];
  128. nV2 = vertices[index + 2];
  129. nV3 = clipVertices(vertices[index + 1], nV1);
  130. nV4 = clipVertices(vertices[index + 1], nV2);
  131. result.push(nV3);
  132. result.push(nV2.clone());
  133. result.push(nV1.clone());
  134. result.push(nV2.clone());
  135. result.push(nV3.clone());
  136. result.push(nV4);
  137. break;
  138. }
  139. if (v3Out) {
  140. nV1 = vertices[index];
  141. nV2 = vertices[index + 1];
  142. nV3 = clipVertices(vertices[index + 2], nV1);
  143. nV4 = clipVertices(vertices[index + 2], nV2);
  144. }
  145. if (nV1 && nV2 && nV3 && nV4) {
  146. result.push(nV1.clone());
  147. result.push(nV2.clone());
  148. result.push(nV3);
  149. result.push(nV4);
  150. result.push(nV3.clone());
  151. result.push(nV2.clone());
  152. }
  153. break;
  154. case 2:
  155. if (!v1Out) {
  156. nV1 = vertices[index].clone();
  157. nV2 = clipVertices(nV1, vertices[index + 1]);
  158. nV3 = clipVertices(nV1, vertices[index + 2]);
  159. result.push(nV1);
  160. result.push(nV2);
  161. result.push(nV3);
  162. }
  163. if (!v2Out) {
  164. nV1 = vertices[index + 1].clone();
  165. nV2 = clipVertices(nV1, vertices[index + 2]);
  166. nV3 = clipVertices(nV1, vertices[index]);
  167. result.push(nV1);
  168. result.push(nV2);
  169. result.push(nV3);
  170. }
  171. if (!v3Out) {
  172. nV1 = vertices[index + 2].clone();
  173. nV2 = clipVertices(nV1, vertices[index]);
  174. nV3 = clipVertices(nV1, vertices[index + 1]);
  175. result.push(nV1);
  176. result.push(nV2);
  177. result.push(nV3);
  178. }
  179. break;
  180. case 3:
  181. break;
  182. }
  183. }
  184. return result;
  185. };
  186. for (var index = 0; index < indices.length; index += 3) {
  187. var faceVertices = new Array<PositionNormalTextureVertex>();
  188. faceVertices.push(extractDecalVector3(index));
  189. faceVertices.push(extractDecalVector3(index + 1));
  190. faceVertices.push(extractDecalVector3(index + 2));
  191. // Clip
  192. faceVertices = clip(faceVertices, new Vector3(1, 0, 0));
  193. faceVertices = clip(faceVertices, new Vector3(-1, 0, 0));
  194. faceVertices = clip(faceVertices, new Vector3(0, 1, 0));
  195. faceVertices = clip(faceVertices, new Vector3(0, -1, 0));
  196. faceVertices = clip(faceVertices, new Vector3(0, 0, 1));
  197. faceVertices = clip(faceVertices, new Vector3(0, 0, -1));
  198. if (faceVertices.length === 0) {
  199. continue;
  200. }
  201. // Add UVs and get back to world
  202. for (var vIndex = 0; vIndex < faceVertices.length; vIndex++) {
  203. var vertex = faceVertices[vIndex];
  204. //TODO check for Int32Array | Uint32Array | Uint16Array
  205. (<number[]>vertexData.indices).push(currentVertexDataIndex);
  206. vertex.position.toArray(vertexData.positions, currentVertexDataIndex * 3);
  207. vertex.normal.toArray(vertexData.normals, currentVertexDataIndex * 3);
  208. if (!options.captureUVS) {
  209. (<number[]>vertexData.uvs).push(0.5 + vertex.position.x / size.x);
  210. (<number[]>vertexData.uvs).push(0.5 + vertex.position.y / size.y);
  211. } else {
  212. vertex.uv.toArray(vertexData.uvs, currentVertexDataIndex * 2);
  213. }
  214. currentVertexDataIndex++;
  215. }
  216. }
  217. // Return mesh
  218. var decal = new Mesh(name, sourceMesh.getScene());
  219. vertexData.applyToMesh(decal);
  220. decal.position = position.clone();
  221. decal.rotation = new Vector3(pitch, yaw, angle);
  222. return decal;
  223. }
  224. }