babylon.rectangle2d.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. module BABYLON {
  2. export class Rectangle2DRenderCache extends ModelRenderCache {
  3. fillVB: WebGLBuffer;
  4. fillIB: WebGLBuffer;
  5. fillIndicesCount: number;
  6. instancingFillAttributes: InstancingAttributeInfo[];
  7. effectFill: Effect;
  8. borderVB: WebGLBuffer;
  9. borderIB: WebGLBuffer;
  10. borderIndicesCount: number;
  11. instancingBorderAttributes: InstancingAttributeInfo[];
  12. effectBorder: Effect;
  13. constructor(engine: Engine, modelKey: string, isTransparent: boolean) {
  14. super(engine, modelKey, isTransparent);
  15. }
  16. render(instanceInfo: GroupInstanceInfo, context: Render2DContext): boolean {
  17. // Do nothing if the shader is still loading/preparing
  18. if ((this.effectFill && !this.effectFill.isReady()) || (this.effectBorder && !this.effectBorder.isReady())) {
  19. return false;
  20. }
  21. var engine = instanceInfo._owner.owner.engine;
  22. let depthFunction = 0;
  23. if (this.effectFill && this.effectBorder) {
  24. depthFunction = engine.getDepthFunction();
  25. engine.setDepthFunctionToLessOrEqual();
  26. }
  27. var cur: number;
  28. if (this.isTransparent) {
  29. cur = engine.getAlphaMode();
  30. engine.setAlphaMode(Engine.ALPHA_COMBINE);
  31. }
  32. if (this.effectFill) {
  33. let partIndex = instanceInfo._partIndexFromId.get(Shape2D.SHAPE2D_FILLPARTID.toString());
  34. engine.enableEffect(this.effectFill);
  35. engine.bindBuffers(this.fillVB, this.fillIB, [1], 4, this.effectFill);
  36. let count = instanceInfo._instancesPartsData[partIndex].usedElementCount;
  37. if (instanceInfo._owner.owner.supportInstancedArray) {
  38. if (!this.instancingFillAttributes) {
  39. // Compute the offset locations of the attributes in the vertex shader that will be mapped to the instance buffer data
  40. this.instancingFillAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_FILLPARTID, this.effectFill);
  41. }
  42. engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingFillAttributes);
  43. engine.draw(true, 0, this.fillIndicesCount, count);
  44. engine.unBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], this.instancingFillAttributes);
  45. } else {
  46. for (let i = 0; i < count; i++) {
  47. this.setupUniforms(this.effectFill, partIndex, instanceInfo._instancesPartsData[partIndex], i);
  48. engine.draw(true, 0, this.fillIndicesCount);
  49. }
  50. }
  51. }
  52. if (this.effectBorder) {
  53. let partIndex = instanceInfo._partIndexFromId.get(Shape2D.SHAPE2D_BORDERPARTID.toString());
  54. engine.enableEffect(this.effectBorder);
  55. engine.bindBuffers(this.borderVB, this.borderIB, [1], 4, this.effectBorder);
  56. let count = instanceInfo._instancesPartsData[partIndex].usedElementCount;
  57. if (instanceInfo._owner.owner.supportInstancedArray) {
  58. if (!this.instancingBorderAttributes) {
  59. this.instancingBorderAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_BORDERPARTID, this.effectBorder);
  60. }
  61. engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingBorderAttributes);
  62. engine.draw(true, 0, this.borderIndicesCount, count);
  63. engine.unBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], this.instancingBorderAttributes);
  64. } else {
  65. for (let i = 0; i < count; i++) {
  66. this.setupUniforms(this.effectBorder, partIndex, instanceInfo._instancesPartsData[partIndex], i);
  67. engine.draw(true, 0, this.borderIndicesCount);
  68. }
  69. }
  70. }
  71. if (this.isTransparent) {
  72. engine.setAlphaMode(cur);
  73. }
  74. if (this.effectFill && this.effectBorder) {
  75. engine.setDepthFunction(depthFunction);
  76. }
  77. return true;
  78. }
  79. public dispose(): boolean {
  80. if (!super.dispose()) {
  81. return false;
  82. }
  83. if (this.fillVB) {
  84. this._engine._releaseBuffer(this.fillVB);
  85. this.fillVB = null;
  86. }
  87. if (this.fillIB) {
  88. this._engine._releaseBuffer(this.fillIB);
  89. this.fillIB = null;
  90. }
  91. if (this.effectFill) {
  92. this._engine._releaseEffect(this.effectFill);
  93. this.effectFill = null;
  94. }
  95. if (this.borderVB) {
  96. this._engine._releaseBuffer(this.borderVB);
  97. this.borderVB = null;
  98. }
  99. if (this.borderIB) {
  100. this._engine._releaseBuffer(this.borderIB);
  101. this.borderIB = null;
  102. }
  103. if (this.effectBorder) {
  104. this._engine._releaseEffect(this.effectBorder);
  105. this.effectBorder = null;
  106. }
  107. return true;
  108. }
  109. }
  110. export class Rectangle2DInstanceData extends Shape2DInstanceData {
  111. constructor(partId: number) {
  112. super(partId, 1);
  113. }
  114. @instanceData()
  115. get properties(): Vector3 {
  116. return null;
  117. }
  118. }
  119. @className("Rectangle2D")
  120. export class Rectangle2D extends Shape2D {
  121. public static sizeProperty: Prim2DPropInfo;
  122. public static notRoundedProperty: Prim2DPropInfo;
  123. public static roundRadiusProperty: Prim2DPropInfo;
  124. @instanceLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 1, pi => Rectangle2D.sizeProperty = pi, false, true)
  125. public get size(): Size {
  126. return this._size;
  127. }
  128. public set size(value: Size) {
  129. this._size = value;
  130. }
  131. @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 2, pi => Rectangle2D.notRoundedProperty = pi)
  132. public get notRounded(): boolean {
  133. return this._notRounded;
  134. }
  135. public set notRounded(value: boolean) {
  136. this._notRounded = value;
  137. }
  138. @instanceLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 3, pi => Rectangle2D.roundRadiusProperty = pi)
  139. public get roundRadius(): number {
  140. return this._roundRadius;
  141. }
  142. public set roundRadius(value: number) {
  143. this._roundRadius = value;
  144. this.notRounded = value === 0;
  145. }
  146. protected updateLevelBoundingInfo() {
  147. BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo);
  148. }
  149. protected setupRectangle2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, size: Size, roundRadius = 0, fill?: IBrush2D, border?: IBrush2D, borderThickness: number = 1) {
  150. this.setupShape2D(owner, parent, id, position, true, fill, border, borderThickness);
  151. this.size = size;
  152. this.notRounded = !roundRadius;
  153. this.roundRadius = roundRadius;
  154. }
  155. public static Create(parent: Prim2DBase, id: string, x: number, y: number, width: number, height: number, fill?: IBrush2D, border?: IBrush2D): Rectangle2D {
  156. Prim2DBase.CheckParent(parent);
  157. let rect = new Rectangle2D();
  158. rect.setupRectangle2D(parent.owner, parent, id, new Vector2(x, y), new Size(width, height), null);
  159. rect.fill = fill;
  160. rect.border = border;
  161. return rect;
  162. }
  163. public static CreateRounded(parent: Prim2DBase, id: string, x: number, y: number, width: number, height: number, roundRadius = 0, fill?: IBrush2D, border?: IBrush2D): Rectangle2D {
  164. Prim2DBase.CheckParent(parent);
  165. let rect = new Rectangle2D();
  166. rect.setupRectangle2D(parent.owner, parent, id, new Vector2(x, y), new Size(width, height), roundRadius);
  167. rect.fill = fill || Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");
  168. rect.border = border;
  169. return rect;
  170. }
  171. public static roundSubdivisions = 16;
  172. protected createModelRenderCache(modelKey: string, isTransparent: boolean): ModelRenderCache {
  173. let renderCache = new Rectangle2DRenderCache(this.owner.engine, modelKey, isTransparent);
  174. return renderCache;
  175. }
  176. protected setupModelRenderCache(modelRenderCache: ModelRenderCache) {
  177. let renderCache = <Rectangle2DRenderCache>modelRenderCache;
  178. let engine = this.owner.engine;
  179. // Need to create WebGL resources for fill part?
  180. if (this.fill) {
  181. let vbSize = ((this.notRounded ? 1 : Rectangle2D.roundSubdivisions) * 4) + 1;
  182. let vb = new Float32Array(vbSize);
  183. for (let i = 0; i < vbSize; i++) {
  184. vb[i] = i;
  185. }
  186. renderCache.fillVB = engine.createVertexBuffer(vb);
  187. let triCount = vbSize - 1;
  188. let ib = new Float32Array(triCount * 3);
  189. for (let i = 0; i < triCount; i++) {
  190. ib[i * 3 + 0] = 0;
  191. ib[i * 3 + 2] = i + 1;
  192. ib[i * 3 + 1] = i + 2;
  193. }
  194. ib[triCount * 3 - 2] = 1;
  195. renderCache.fillIB = engine.createIndexBuffer(ib);
  196. renderCache.fillIndicesCount = triCount * 3;
  197. let ei = this.getDataPartEffectInfo(Shape2D.SHAPE2D_FILLPARTID, ["index"]);
  198. renderCache.effectFill = engine.createEffect({ vertex: "rect2d", fragment: "rect2d" }, ei.attributes, ei.uniforms, [], ei.defines, null, e => {
  199. // renderCache.setupUniformsLocation(e, ei.uniforms, Shape2D.SHAPE2D_FILLPARTID);
  200. });
  201. }
  202. // Need to create WebGL resource for border part?
  203. if (this.border) {
  204. let vbSize = (this.notRounded ? 1 : Rectangle2D.roundSubdivisions) * 4 * 2;
  205. let vb = new Float32Array(vbSize);
  206. for (let i = 0; i < vbSize; i++) {
  207. vb[i] = i;
  208. }
  209. renderCache.borderVB = engine.createVertexBuffer(vb);
  210. let triCount = vbSize;
  211. let rs = triCount / 2;
  212. let ib = new Float32Array(triCount * 3);
  213. for (let i = 0; i < rs; i++) {
  214. let r0 = i;
  215. let r1 = (i + 1) % rs;
  216. ib[i * 6 + 0] = rs + r1;
  217. ib[i * 6 + 1] = rs + r0;
  218. ib[i * 6 + 2] = r0;
  219. ib[i * 6 + 3] = r1;
  220. ib[i * 6 + 4] = rs + r1;
  221. ib[i * 6 + 5] = r0;
  222. }
  223. renderCache.borderIB = engine.createIndexBuffer(ib);
  224. renderCache.borderIndicesCount = triCount * 3;
  225. let ei = this.getDataPartEffectInfo(Shape2D.SHAPE2D_BORDERPARTID, ["index"]);
  226. renderCache.effectBorder = engine.createEffect({ vertex: "rect2d", fragment: "rect2d" }, ei.attributes, ei.uniforms, [], ei.defines, null, e => {
  227. // renderCache.setupUniformsLocation(e, ei.uniforms, Shape2D.SHAPE2D_BORDERPARTID);
  228. });
  229. }
  230. return renderCache;
  231. }
  232. protected createInstanceDataParts(): InstanceDataBase[] {
  233. var res = new Array<InstanceDataBase>();
  234. if (this.border) {
  235. res.push(new Rectangle2DInstanceData(Shape2D.SHAPE2D_BORDERPARTID));
  236. }
  237. if (this.fill) {
  238. res.push(new Rectangle2DInstanceData(Shape2D.SHAPE2D_FILLPARTID));
  239. }
  240. return res;
  241. }
  242. protected refreshInstanceDataPart(part: InstanceDataBase): boolean {
  243. if (!super.refreshInstanceDataPart(part)) {
  244. return false;
  245. }
  246. if (part.id === Shape2D.SHAPE2D_BORDERPARTID) {
  247. let d = <Rectangle2DInstanceData>part;
  248. let size = this.size;
  249. d.properties = new Vector3(size.width, size.height, this.roundRadius || 0);
  250. }
  251. else if (part.id === Shape2D.SHAPE2D_FILLPARTID) {
  252. let d = <Rectangle2DInstanceData>part;
  253. let size = this.size;
  254. d.properties = new Vector3(size.width, size.height, this.roundRadius || 0);
  255. }
  256. return true;
  257. }
  258. private _size: Size;
  259. private _notRounded: boolean;
  260. private _roundRadius: number;
  261. }
  262. }