babylon.rectangle2d.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  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. public get actualSize(): Size {
  125. return this.size;
  126. }
  127. @instanceLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 1, pi => Rectangle2D.sizeProperty = pi, false, true)
  128. public get size(): Size {
  129. return this._size;
  130. }
  131. public set size(value: Size) {
  132. this._size = value;
  133. }
  134. @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 2, pi => Rectangle2D.notRoundedProperty = pi)
  135. public get notRounded(): boolean {
  136. return this._notRounded;
  137. }
  138. public set notRounded(value: boolean) {
  139. this._notRounded = value;
  140. }
  141. @instanceLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 3, pi => Rectangle2D.roundRadiusProperty = pi)
  142. public get roundRadius(): number {
  143. return this._roundRadius;
  144. }
  145. public set roundRadius(value: number) {
  146. this._roundRadius = value;
  147. this.notRounded = value === 0;
  148. }
  149. protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
  150. // If we got there it mean the boundingInfo intersection succeed, if the rectangle has not roundRadius, it means it succeed!
  151. if (this.notRounded) {
  152. return true;
  153. }
  154. // Well, for now we neglect the area where the pickPosition could be outside due to the roundRadius...
  155. // TODO make REAL intersection test here!
  156. return true;
  157. }
  158. protected updateLevelBoundingInfo() {
  159. BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo, this.origin);
  160. }
  161. protected setupRectangle2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, size: Size, roundRadius = 0, fill?: IBrush2D, border?: IBrush2D, borderThickness: number = 1) {
  162. this.setupShape2D(owner, parent, id, position, true, fill, border, borderThickness);
  163. this.size = size;
  164. this.notRounded = !roundRadius;
  165. this.roundRadius = roundRadius;
  166. }
  167. public static Create(parent: Prim2DBase, id: string, x: number, y: number, width: number, height: number, fill?: IBrush2D, border?: IBrush2D): Rectangle2D {
  168. Prim2DBase.CheckParent(parent);
  169. let rect = new Rectangle2D();
  170. rect.setupRectangle2D(parent.owner, parent, id, new Vector2(x, y), new Size(width, height), null);
  171. rect.fill = fill;
  172. rect.border = border;
  173. return rect;
  174. }
  175. public static CreateRounded(parent: Prim2DBase, id: string, x: number, y: number, width: number, height: number, roundRadius = 0, fill?: IBrush2D, border?: IBrush2D): Rectangle2D {
  176. Prim2DBase.CheckParent(parent);
  177. let rect = new Rectangle2D();
  178. rect.setupRectangle2D(parent.owner, parent, id, new Vector2(x, y), new Size(width, height), roundRadius);
  179. rect.fill = fill || Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");
  180. rect.border = border;
  181. return rect;
  182. }
  183. public static roundSubdivisions = 16;
  184. protected createModelRenderCache(modelKey: string, isTransparent: boolean): ModelRenderCache {
  185. let renderCache = new Rectangle2DRenderCache(this.owner.engine, modelKey, isTransparent);
  186. return renderCache;
  187. }
  188. protected setupModelRenderCache(modelRenderCache: ModelRenderCache) {
  189. let renderCache = <Rectangle2DRenderCache>modelRenderCache;
  190. let engine = this.owner.engine;
  191. // Need to create WebGL resources for fill part?
  192. if (this.fill) {
  193. let vbSize = ((this.notRounded ? 1 : Rectangle2D.roundSubdivisions) * 4) + 1;
  194. let vb = new Float32Array(vbSize);
  195. for (let i = 0; i < vbSize; i++) {
  196. vb[i] = i;
  197. }
  198. renderCache.fillVB = engine.createVertexBuffer(vb);
  199. let triCount = vbSize - 1;
  200. let ib = new Float32Array(triCount * 3);
  201. for (let i = 0; i < triCount; i++) {
  202. ib[i * 3 + 0] = 0;
  203. ib[i * 3 + 2] = i + 1;
  204. ib[i * 3 + 1] = i + 2;
  205. }
  206. ib[triCount * 3 - 2] = 1;
  207. renderCache.fillIB = engine.createIndexBuffer(ib);
  208. renderCache.fillIndicesCount = triCount * 3;
  209. let ei = this.getDataPartEffectInfo(Shape2D.SHAPE2D_FILLPARTID, ["index"]);
  210. renderCache.effectFill = engine.createEffect({ vertex: "rect2d", fragment: "rect2d" }, ei.attributes, ei.uniforms, [], ei.defines, null, e => {
  211. // renderCache.setupUniformsLocation(e, ei.uniforms, Shape2D.SHAPE2D_FILLPARTID);
  212. });
  213. }
  214. // Need to create WebGL resource for border part?
  215. if (this.border) {
  216. let vbSize = (this.notRounded ? 1 : Rectangle2D.roundSubdivisions) * 4 * 2;
  217. let vb = new Float32Array(vbSize);
  218. for (let i = 0; i < vbSize; i++) {
  219. vb[i] = i;
  220. }
  221. renderCache.borderVB = engine.createVertexBuffer(vb);
  222. let triCount = vbSize;
  223. let rs = triCount / 2;
  224. let ib = new Float32Array(triCount * 3);
  225. for (let i = 0; i < rs; i++) {
  226. let r0 = i;
  227. let r1 = (i + 1) % rs;
  228. ib[i * 6 + 0] = rs + r1;
  229. ib[i * 6 + 1] = rs + r0;
  230. ib[i * 6 + 2] = r0;
  231. ib[i * 6 + 3] = r1;
  232. ib[i * 6 + 4] = rs + r1;
  233. ib[i * 6 + 5] = r0;
  234. }
  235. renderCache.borderIB = engine.createIndexBuffer(ib);
  236. renderCache.borderIndicesCount = triCount * 3;
  237. let ei = this.getDataPartEffectInfo(Shape2D.SHAPE2D_BORDERPARTID, ["index"]);
  238. renderCache.effectBorder = engine.createEffect({ vertex: "rect2d", fragment: "rect2d" }, ei.attributes, ei.uniforms, [], ei.defines, null, e => {
  239. // renderCache.setupUniformsLocation(e, ei.uniforms, Shape2D.SHAPE2D_BORDERPARTID);
  240. });
  241. }
  242. return renderCache;
  243. }
  244. protected createInstanceDataParts(): InstanceDataBase[] {
  245. var res = new Array<InstanceDataBase>();
  246. if (this.border) {
  247. res.push(new Rectangle2DInstanceData(Shape2D.SHAPE2D_BORDERPARTID));
  248. }
  249. if (this.fill) {
  250. res.push(new Rectangle2DInstanceData(Shape2D.SHAPE2D_FILLPARTID));
  251. }
  252. return res;
  253. }
  254. protected refreshInstanceDataPart(part: InstanceDataBase): boolean {
  255. if (!super.refreshInstanceDataPart(part)) {
  256. return false;
  257. }
  258. if (part.id === Shape2D.SHAPE2D_BORDERPARTID) {
  259. let d = <Rectangle2DInstanceData>part;
  260. let size = this.size;
  261. d.properties = new Vector3(size.width, size.height, this.roundRadius || 0);
  262. }
  263. else if (part.id === Shape2D.SHAPE2D_FILLPARTID) {
  264. let d = <Rectangle2DInstanceData>part;
  265. let size = this.size;
  266. d.properties = new Vector3(size.width, size.height, this.roundRadius || 0);
  267. }
  268. return true;
  269. }
  270. private _size: Size;
  271. private _notRounded: boolean;
  272. private _roundRadius: number;
  273. }
  274. }