babylon.edgesRenderer.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  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]: Nullable<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 _rebuild(): void {
  43. var buffer = this._buffers[VertexBuffer.PositionKind];
  44. if (buffer) {
  45. buffer._rebuild();
  46. }
  47. buffer = this._buffers[VertexBuffer.NormalKind];
  48. if (buffer) {
  49. buffer._rebuild();
  50. }
  51. var scene = this._source.getScene();
  52. var engine = scene.getEngine();
  53. this._ib = engine.createIndexBuffer(this._linesIndices);
  54. }
  55. public dispose(): void {
  56. var buffer = this._buffers[VertexBuffer.PositionKind];
  57. if (buffer) {
  58. buffer.dispose();
  59. this._buffers[VertexBuffer.PositionKind] = null;
  60. }
  61. buffer = this._buffers[VertexBuffer.NormalKind];
  62. if (buffer) {
  63. buffer.dispose();
  64. this._buffers[VertexBuffer.NormalKind] = null;
  65. }
  66. this._source.getScene().getEngine()._releaseBuffer(this._ib);
  67. this._lineShader.dispose();
  68. }
  69. private _processEdgeForAdjacencies(pa: number, pb: number, p0: number, p1: number, p2: number): number {
  70. if (pa === p0 && pb === p1 || pa === p1 && pb === p0) {
  71. return 0;
  72. }
  73. if (pa === p1 && pb === p2 || pa === p2 && pb === p1) {
  74. return 1;
  75. }
  76. if (pa === p2 && pb === p0 || pa === p0 && pb === p2) {
  77. return 2;
  78. }
  79. return -1;
  80. }
  81. private _processEdgeForAdjacenciesWithVertices(pa: Vector3, pb: Vector3, p0: Vector3, p1: Vector3, p2: Vector3): number {
  82. if (pa.equalsWithEpsilon(p0) && pb.equalsWithEpsilon(p1) || pa.equalsWithEpsilon(p1) && pb.equalsWithEpsilon(p0)) {
  83. return 0;
  84. }
  85. if (pa.equalsWithEpsilon(p1) && pb.equalsWithEpsilon(p2) || pa.equalsWithEpsilon(p2) && pb.equalsWithEpsilon(p1)) {
  86. return 1;
  87. }
  88. if (pa.equalsWithEpsilon(p2) && pb.equalsWithEpsilon(p0) || pa.equalsWithEpsilon(p0) && pb.equalsWithEpsilon(p2)) {
  89. return 2;
  90. }
  91. return -1;
  92. }
  93. private _checkEdge(faceIndex: number, edge: number, faceNormals: Array<Vector3>, p0: Vector3, p1: Vector3): void {
  94. var needToCreateLine;
  95. if (edge === undefined) {
  96. needToCreateLine = true;
  97. } else {
  98. var dotProduct = Vector3.Dot(faceNormals[faceIndex], faceNormals[edge]);
  99. needToCreateLine = dotProduct < this._epsilon;
  100. }
  101. if (needToCreateLine) {
  102. var offset = this._linesPositions.length / 3;
  103. var normal = p0.subtract(p1);
  104. normal.normalize();
  105. // Positions
  106. this._linesPositions.push(p0.x);
  107. this._linesPositions.push(p0.y);
  108. this._linesPositions.push(p0.z);
  109. this._linesPositions.push(p0.x);
  110. this._linesPositions.push(p0.y);
  111. this._linesPositions.push(p0.z);
  112. this._linesPositions.push(p1.x);
  113. this._linesPositions.push(p1.y);
  114. this._linesPositions.push(p1.z);
  115. this._linesPositions.push(p1.x);
  116. this._linesPositions.push(p1.y);
  117. this._linesPositions.push(p1.z);
  118. // Normals
  119. this._linesNormals.push(p1.x);
  120. this._linesNormals.push(p1.y);
  121. this._linesNormals.push(p1.z);
  122. this._linesNormals.push(-1);
  123. this._linesNormals.push(p1.x);
  124. this._linesNormals.push(p1.y);
  125. this._linesNormals.push(p1.z);
  126. this._linesNormals.push(1);
  127. this._linesNormals.push(p0.x);
  128. this._linesNormals.push(p0.y);
  129. this._linesNormals.push(p0.z);
  130. this._linesNormals.push(-1);
  131. this._linesNormals.push(p0.x);
  132. this._linesNormals.push(p0.y);
  133. this._linesNormals.push(p0.z);
  134. this._linesNormals.push(1);
  135. // Indices
  136. this._linesIndices.push(offset);
  137. this._linesIndices.push(offset + 1);
  138. this._linesIndices.push(offset + 2);
  139. this._linesIndices.push(offset);
  140. this._linesIndices.push(offset + 2);
  141. this._linesIndices.push(offset + 3);
  142. }
  143. }
  144. _generateEdgesLines(): void {
  145. var positions = this._source.getVerticesData(VertexBuffer.PositionKind);
  146. var indices = this._source.getIndices();
  147. if (!indices || !positions) {
  148. return;
  149. }
  150. // First let's find adjacencies
  151. var adjacencies = new Array<FaceAdjacencies>();
  152. var faceNormals = new Array<Vector3>();
  153. var index: number;
  154. var faceAdjacencies: FaceAdjacencies;
  155. // Prepare faces
  156. for (index = 0; index < indices.length; index += 3) {
  157. faceAdjacencies = new FaceAdjacencies();
  158. var p0Index = indices[index];
  159. var p1Index = indices[index + 1];
  160. var p2Index = indices[index + 2];
  161. faceAdjacencies.p0 = new Vector3(positions[p0Index * 3], positions[p0Index * 3 + 1], positions[p0Index * 3 + 2]);
  162. faceAdjacencies.p1 = new Vector3(positions[p1Index * 3], positions[p1Index * 3 + 1], positions[p1Index * 3 + 2]);
  163. faceAdjacencies.p2 = new Vector3(positions[p2Index * 3], positions[p2Index * 3 + 1], positions[p2Index * 3 + 2]);
  164. var faceNormal = Vector3.Cross(faceAdjacencies.p1.subtract(faceAdjacencies.p0), faceAdjacencies.p2.subtract(faceAdjacencies.p1));
  165. faceNormal.normalize();
  166. faceNormals.push(faceNormal);
  167. adjacencies.push(faceAdjacencies);
  168. }
  169. // Scan
  170. for (index = 0; index < adjacencies.length; index++) {
  171. faceAdjacencies = adjacencies[index];
  172. for (var otherIndex = index + 1; otherIndex < adjacencies.length; otherIndex++) {
  173. var otherFaceAdjacencies = adjacencies[otherIndex];
  174. if (faceAdjacencies.edgesConnectedCount === 3) { // Full
  175. break;
  176. }
  177. if (otherFaceAdjacencies.edgesConnectedCount === 3) { // Full
  178. continue;
  179. }
  180. var otherP0 = indices[otherIndex * 3];
  181. var otherP1 = indices[otherIndex * 3 + 1];
  182. var otherP2 = indices[otherIndex * 3 + 2];
  183. for (var edgeIndex = 0; edgeIndex < 3; edgeIndex++) {
  184. var otherEdgeIndex: number = 0;
  185. if (faceAdjacencies.edges[edgeIndex] !== undefined) {
  186. continue;
  187. }
  188. switch (edgeIndex) {
  189. case 0:
  190. if (this._checkVerticesInsteadOfIndices) {
  191. otherEdgeIndex = this._processEdgeForAdjacenciesWithVertices(faceAdjacencies.p0, faceAdjacencies.p1, otherFaceAdjacencies.p0, otherFaceAdjacencies.p1, otherFaceAdjacencies.p2);
  192. } else {
  193. otherEdgeIndex = this._processEdgeForAdjacencies(indices[index * 3], indices[index * 3 + 1], otherP0, otherP1, otherP2);
  194. }
  195. break;
  196. case 1:
  197. if (this._checkVerticesInsteadOfIndices) {
  198. otherEdgeIndex = this._processEdgeForAdjacenciesWithVertices(faceAdjacencies.p1, faceAdjacencies.p2, otherFaceAdjacencies.p0, otherFaceAdjacencies.p1, otherFaceAdjacencies.p2);
  199. } else {
  200. otherEdgeIndex = this._processEdgeForAdjacencies(indices[index * 3 + 1], indices[index * 3 + 2], otherP0, otherP1, otherP2);
  201. }
  202. break;
  203. case 2:
  204. if (this._checkVerticesInsteadOfIndices) {
  205. otherEdgeIndex = this._processEdgeForAdjacenciesWithVertices(faceAdjacencies.p2, faceAdjacencies.p0, otherFaceAdjacencies.p0, otherFaceAdjacencies.p1, otherFaceAdjacencies.p2);
  206. } else {
  207. otherEdgeIndex = this._processEdgeForAdjacencies(indices[index * 3 + 2], indices[index * 3], otherP0, otherP1, otherP2);
  208. }
  209. break;
  210. }
  211. if (otherEdgeIndex === -1) {
  212. continue;
  213. }
  214. faceAdjacencies.edges[edgeIndex] = otherIndex;
  215. otherFaceAdjacencies.edges[otherEdgeIndex] = index;
  216. faceAdjacencies.edgesConnectedCount++;
  217. otherFaceAdjacencies.edgesConnectedCount++;
  218. if (faceAdjacencies.edgesConnectedCount === 3) {
  219. break;
  220. }
  221. }
  222. }
  223. }
  224. // Create lines
  225. for (index = 0; index < adjacencies.length; index++) {
  226. // 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
  227. var current = adjacencies[index];
  228. this._checkEdge(index, current.edges[0], faceNormals, current.p0, current.p1);
  229. this._checkEdge(index, current.edges[1], faceNormals, current.p1, current.p2);
  230. this._checkEdge(index, current.edges[2], faceNormals, current.p2, current.p0);
  231. }
  232. // Merge into a single mesh
  233. var engine = this._source.getScene().getEngine();
  234. this._buffers[VertexBuffer.PositionKind] = new VertexBuffer(engine, this._linesPositions, VertexBuffer.PositionKind, false);
  235. this._buffers[VertexBuffer.NormalKind] = new VertexBuffer(engine, this._linesNormals, VertexBuffer.NormalKind, false, false, 4);
  236. this._ib = engine.createIndexBuffer(this._linesIndices);
  237. this._indicesCount = this._linesIndices.length;
  238. }
  239. public render(): void {
  240. var scene = this._source.getScene();
  241. if (!this._lineShader.isReady() || !scene.activeCamera) {
  242. return;
  243. }
  244. var engine = scene.getEngine();
  245. this._lineShader._preBind();
  246. // VBOs
  247. engine.bindBuffers(this._buffers, this._ib, <Effect>this._lineShader.getEffect());
  248. scene.resetCachedMaterial();
  249. this._lineShader.setColor4("color", this._source.edgesColor);
  250. if (scene.activeCamera.mode === Camera.ORTHOGRAPHIC_CAMERA) {
  251. this._lineShader.setFloat("width", this._source.edgesWidth / this.edgesWidthScalerForOrthographic);
  252. } else {
  253. this._lineShader.setFloat("width", this._source.edgesWidth / this.edgesWidthScalerForPerspective);
  254. }
  255. this._lineShader.setFloat("aspectRatio", engine.getAspectRatio(scene.activeCamera));
  256. this._lineShader.bind(this._source.getWorldMatrix());
  257. // Draw order
  258. engine.drawElementsType(Engine.DrawType.TRIANGLES, 0, this._indicesCount);
  259. this._lineShader.unbind();
  260. engine.setDepthWrite(true);
  261. }
  262. }
  263. }