babylon.edgesRenderer.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. module BABYLON {
  2. class FaceAdjacencies {
  3. public edges = new Array<number>();
  4. public p0: Vector3;
  5. public p1: Vector3;
  6. public p2: Vector3;
  7. public edgesConnectedCount = 0;
  8. }
  9. export class EdgesRenderer {
  10. public edgesWidthScalerForOrthographic = 1000.0;
  11. public edgesWidthScalerForPerspective = 50.0;
  12. private _source: AbstractMesh;
  13. private _linesPositions = new Array<number>();
  14. private _linesNormals = new Array<number>();
  15. private _linesIndices = new Array<number>();
  16. private _epsilon: number;
  17. private _indicesCount: number;
  18. private _lineShader: ShaderMaterial;
  19. private _ib: WebGLBuffer;
  20. private _buffers: { [key: string]: VertexBuffer; } = {};
  21. private _checkVerticesInsteadOfIndices = false;
  22. // Beware when you use this class with complex objects as the adjacencies computation can be really long
  23. constructor(source: AbstractMesh, epsilon = 0.95, checkVerticesInsteadOfIndices = false) {
  24. this._source = source;
  25. this._checkVerticesInsteadOfIndices = checkVerticesInsteadOfIndices;
  26. this._epsilon = epsilon;
  27. this._prepareRessources();
  28. this._generateEdgesLines();
  29. }
  30. private _prepareRessources(): void {
  31. if (this._lineShader) {
  32. return;
  33. }
  34. this._lineShader = new ShaderMaterial("lineShader", this._source.getScene(), "line",
  35. {
  36. attributes: ["position", "normal"],
  37. uniforms: ["worldViewProjection", "color", "width", "aspectRatio"]
  38. });
  39. this._lineShader.disableDepthWrite = true;
  40. this._lineShader.backFaceCulling = false;
  41. }
  42. public dispose(): void {
  43. var buffer = this._buffers[VertexBuffer.PositionKind];
  44. if (buffer) {
  45. buffer.dispose();
  46. this._buffers[VertexBuffer.PositionKind] = null;
  47. }
  48. buffer = this._buffers[VertexBuffer.NormalKind];
  49. if (buffer) {
  50. buffer.dispose();
  51. this._buffers[VertexBuffer.NormalKind] = null;
  52. }
  53. this._source.getScene().getEngine()._releaseBuffer(this._ib);
  54. this._lineShader.dispose();
  55. }
  56. private _processEdgeForAdjacencies(pa: number, pb: number, p0: number, p1: number, p2: number): number {
  57. if (pa === p0 && pb === p1 || pa === p1 && pb === p0) {
  58. return 0;
  59. }
  60. if (pa === p1 && pb === p2 || pa === p2 && pb === p1) {
  61. return 1;
  62. }
  63. if (pa === p2 && pb === p0 || pa === p0 && pb === p2) {
  64. return 2;
  65. }
  66. return -1;
  67. }
  68. private _processEdgeForAdjacenciesWithVertices(pa: Vector3, pb: Vector3, p0: Vector3, p1: Vector3, p2: Vector3): number {
  69. if (pa.equalsWithEpsilon(p0) && pb.equalsWithEpsilon(p1) || pa.equalsWithEpsilon(p1) && pb.equalsWithEpsilon(p0)) {
  70. return 0;
  71. }
  72. if (pa.equalsWithEpsilon(p1) && pb.equalsWithEpsilon(p2) || pa.equalsWithEpsilon(p2) && pb.equalsWithEpsilon(p1)) {
  73. return 1;
  74. }
  75. if (pa.equalsWithEpsilon(p2) && pb.equalsWithEpsilon(p0) || pa.equalsWithEpsilon(p0) && pb.equalsWithEpsilon(p2)) {
  76. return 2;
  77. }
  78. return -1;
  79. }
  80. private _checkEdge(faceIndex: number, edge: number, faceNormals: Array<Vector3>, p0: Vector3, p1: Vector3): void {
  81. var needToCreateLine;
  82. if (edge === undefined) {
  83. needToCreateLine = true;
  84. } else {
  85. var dotProduct = Vector3.Dot(faceNormals[faceIndex], faceNormals[edge]);
  86. needToCreateLine = dotProduct < this._epsilon;
  87. }
  88. if (needToCreateLine) {
  89. var offset = this._linesPositions.length / 3;
  90. var normal = p0.subtract(p1);
  91. normal.normalize();
  92. // Positions
  93. this._linesPositions.push(p0.x);
  94. this._linesPositions.push(p0.y);
  95. this._linesPositions.push(p0.z);
  96. this._linesPositions.push(p0.x);
  97. this._linesPositions.push(p0.y);
  98. this._linesPositions.push(p0.z);
  99. this._linesPositions.push(p1.x);
  100. this._linesPositions.push(p1.y);
  101. this._linesPositions.push(p1.z);
  102. this._linesPositions.push(p1.x);
  103. this._linesPositions.push(p1.y);
  104. this._linesPositions.push(p1.z);
  105. // Normals
  106. this._linesNormals.push(p1.x);
  107. this._linesNormals.push(p1.y);
  108. this._linesNormals.push(p1.z);
  109. this._linesNormals.push(-1);
  110. this._linesNormals.push(p1.x);
  111. this._linesNormals.push(p1.y);
  112. this._linesNormals.push(p1.z);
  113. this._linesNormals.push(1);
  114. this._linesNormals.push(p0.x);
  115. this._linesNormals.push(p0.y);
  116. this._linesNormals.push(p0.z);
  117. this._linesNormals.push(-1);
  118. this._linesNormals.push(p0.x);
  119. this._linesNormals.push(p0.y);
  120. this._linesNormals.push(p0.z);
  121. this._linesNormals.push(1);
  122. // Indices
  123. this._linesIndices.push(offset);
  124. this._linesIndices.push(offset + 1);
  125. this._linesIndices.push(offset + 2);
  126. this._linesIndices.push(offset);
  127. this._linesIndices.push(offset + 2);
  128. this._linesIndices.push(offset + 3);
  129. }
  130. }
  131. _generateEdgesLines(): void {
  132. var positions = this._source.getVerticesData(VertexBuffer.PositionKind);
  133. var indices = this._source.getIndices();
  134. // First let's find adjacencies
  135. var adjacencies = new Array<FaceAdjacencies>();
  136. var faceNormals = new Array<Vector3>();
  137. var index: number;
  138. var faceAdjacencies: FaceAdjacencies;
  139. // Prepare faces
  140. for (index = 0; index < indices.length; index += 3) {
  141. faceAdjacencies = new FaceAdjacencies();
  142. var p0Index = indices[index];
  143. var p1Index = indices[index + 1];
  144. var p2Index = indices[index + 2];
  145. faceAdjacencies.p0 = new Vector3(positions[p0Index * 3], positions[p0Index * 3 + 1], positions[p0Index * 3 + 2]);
  146. faceAdjacencies.p1 = new Vector3(positions[p1Index * 3], positions[p1Index * 3 + 1], positions[p1Index * 3 + 2]);
  147. faceAdjacencies.p2 = new Vector3(positions[p2Index * 3], positions[p2Index * 3 + 1], positions[p2Index * 3 + 2]);
  148. var faceNormal = Vector3.Cross(faceAdjacencies.p1.subtract(faceAdjacencies.p0), faceAdjacencies.p2.subtract(faceAdjacencies.p1));
  149. faceNormal.normalize();
  150. faceNormals.push(faceNormal);
  151. adjacencies.push(faceAdjacencies);
  152. }
  153. // Scan
  154. for (index = 0; index < adjacencies.length; index++) {
  155. faceAdjacencies = adjacencies[index];
  156. for (var otherIndex = index + 1; otherIndex < adjacencies.length; otherIndex++) {
  157. var otherFaceAdjacencies = adjacencies[otherIndex];
  158. if (faceAdjacencies.edgesConnectedCount === 3) { // Full
  159. break;
  160. }
  161. if (otherFaceAdjacencies.edgesConnectedCount === 3) { // Full
  162. continue;
  163. }
  164. var otherP0 = indices[otherIndex * 3];
  165. var otherP1 = indices[otherIndex * 3 + 1];
  166. var otherP2 = indices[otherIndex * 3 + 2];
  167. for (var edgeIndex = 0; edgeIndex < 3; edgeIndex++) {
  168. var otherEdgeIndex: number;
  169. if (faceAdjacencies.edges[edgeIndex] !== undefined) {
  170. continue;
  171. }
  172. switch (edgeIndex) {
  173. case 0:
  174. if (this._checkVerticesInsteadOfIndices) {
  175. otherEdgeIndex = this._processEdgeForAdjacenciesWithVertices(faceAdjacencies.p0, faceAdjacencies.p1, otherFaceAdjacencies.p0, otherFaceAdjacencies.p1, otherFaceAdjacencies.p2);
  176. } else {
  177. otherEdgeIndex = this._processEdgeForAdjacencies(indices[index * 3], indices[index * 3 + 1], otherP0, otherP1, otherP2);
  178. }
  179. break;
  180. case 1:
  181. if (this._checkVerticesInsteadOfIndices) {
  182. otherEdgeIndex = this._processEdgeForAdjacenciesWithVertices(faceAdjacencies.p1, faceAdjacencies.p2, otherFaceAdjacencies.p0, otherFaceAdjacencies.p1, otherFaceAdjacencies.p2);
  183. } else {
  184. otherEdgeIndex = this._processEdgeForAdjacencies(indices[index * 3 + 1], indices[index * 3 + 2], otherP0, otherP1, otherP2);
  185. }
  186. break;
  187. case 2:
  188. if (this._checkVerticesInsteadOfIndices) {
  189. otherEdgeIndex = this._processEdgeForAdjacenciesWithVertices(faceAdjacencies.p2, faceAdjacencies.p0, otherFaceAdjacencies.p0, otherFaceAdjacencies.p1, otherFaceAdjacencies.p2);
  190. } else {
  191. otherEdgeIndex = this._processEdgeForAdjacencies(indices[index * 3 + 2], indices[index * 3], otherP0, otherP1, otherP2);
  192. }
  193. break;
  194. }
  195. if (otherEdgeIndex === -1) {
  196. continue;
  197. }
  198. faceAdjacencies.edges[edgeIndex] = otherIndex;
  199. otherFaceAdjacencies.edges[otherEdgeIndex] = index;
  200. faceAdjacencies.edgesConnectedCount++;
  201. otherFaceAdjacencies.edgesConnectedCount++;
  202. if (faceAdjacencies.edgesConnectedCount === 3) {
  203. break;
  204. }
  205. }
  206. }
  207. }
  208. // Create lines
  209. for (index = 0; index < adjacencies.length; index++) {
  210. // We need a line when a face has no adjacency on a specific edge or if all the adjacencies has an angle greater than epsilon
  211. var current = adjacencies[index];
  212. this._checkEdge(index, current.edges[0], faceNormals, current.p0, current.p1);
  213. this._checkEdge(index, current.edges[1], faceNormals, current.p1, current.p2);
  214. this._checkEdge(index, current.edges[2], faceNormals, current.p2, current.p0);
  215. }
  216. // Merge into a single mesh
  217. var engine = this._source.getScene().getEngine();
  218. this._buffers[VertexBuffer.PositionKind] = new VertexBuffer(engine, this._linesPositions, VertexBuffer.PositionKind, false);
  219. this._buffers[VertexBuffer.NormalKind] = new VertexBuffer(engine, this._linesNormals, VertexBuffer.NormalKind, false, false, 4);
  220. this._ib = engine.createIndexBuffer(this._linesIndices);
  221. this._indicesCount = this._linesIndices.length;
  222. }
  223. public render(): void {
  224. if (!this._lineShader.isReady()) {
  225. return;
  226. }
  227. var scene = this._source.getScene();
  228. var engine = scene.getEngine();
  229. this._lineShader._preBind();
  230. // VBOs
  231. engine.bindBuffers(this._buffers, this._ib, this._lineShader.getEffect());
  232. scene.resetCachedMaterial();
  233. this._lineShader.setColor4("color", this._source.edgesColor);
  234. if (scene.activeCamera.mode === Camera.ORTHOGRAPHIC_CAMERA) {
  235. this._lineShader.setFloat("width", this._source.edgesWidth / this.edgesWidthScalerForOrthographic);
  236. } else {
  237. this._lineShader.setFloat("width", this._source.edgesWidth / this.edgesWidthScalerForPerspective);
  238. }
  239. this._lineShader.setFloat("aspectRatio", engine.getAspectRatio(scene.activeCamera));
  240. this._lineShader.bind(this._source.getWorldMatrix());
  241. // Draw order
  242. engine.draw(true, 0, this._indicesCount);
  243. this._lineShader.unbind();
  244. engine.setDepthWrite(true);
  245. }
  246. }
  247. }