babylon.rectangle2d.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. var __extends = (this && this.__extends) || function (d, b) {
  2. for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
  3. function __() { this.constructor = d; }
  4. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  5. };
  6. var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  7. var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
  8. if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
  9. else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
  10. return c > 3 && r && Object.defineProperty(target, key, r), r;
  11. };
  12. var BABYLON;
  13. (function (BABYLON) {
  14. var Rectangle2DRenderCache = (function (_super) {
  15. __extends(Rectangle2DRenderCache, _super);
  16. function Rectangle2DRenderCache(engine, modelKey) {
  17. _super.call(this, engine, modelKey);
  18. this.effectsReady = false;
  19. this.fillVB = null;
  20. this.fillIB = null;
  21. this.fillIndicesCount = 0;
  22. this.instancingFillAttributes = null;
  23. this.effectFill = null;
  24. this.effectFillInstanced = null;
  25. this.borderVB = null;
  26. this.borderIB = null;
  27. this.borderIndicesCount = 0;
  28. this.instancingBorderAttributes = null;
  29. this.effectBorder = null;
  30. this.effectBorderInstanced = null;
  31. }
  32. Rectangle2DRenderCache.prototype.render = function (instanceInfo, context) {
  33. // Do nothing if the shader is still loading/preparing
  34. if (!this.effectsReady) {
  35. if ((this.effectFill && (!this.effectFill.isReady() || (this.effectFillInstanced && !this.effectFillInstanced.isReady()))) ||
  36. (this.effectBorder && (!this.effectBorder.isReady() || (this.effectBorderInstanced && !this.effectBorderInstanced.isReady())))) {
  37. return false;
  38. }
  39. this.effectsReady = true;
  40. }
  41. var engine = instanceInfo.owner.owner.engine;
  42. var depthFunction = 0;
  43. if (this.effectFill && this.effectBorder) {
  44. depthFunction = engine.getDepthFunction();
  45. engine.setDepthFunctionToLessOrEqual();
  46. }
  47. var curAlphaMode = engine.getAlphaMode();
  48. if (this.effectFill) {
  49. var partIndex = instanceInfo.partIndexFromId.get(BABYLON.Shape2D.SHAPE2D_FILLPARTID.toString());
  50. var pid = context.groupInfoPartData[partIndex];
  51. if (context.renderMode !== BABYLON.Render2DContext.RenderModeOpaque) {
  52. engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
  53. }
  54. var effect = context.useInstancing ? this.effectFillInstanced : this.effectFill;
  55. engine.enableEffect(effect);
  56. engine.bindBuffersDirectly(this.fillVB, this.fillIB, [1], 4, effect);
  57. if (context.useInstancing) {
  58. if (!this.instancingFillAttributes) {
  59. this.instancingFillAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_FILLPARTID, effect);
  60. }
  61. engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingFillAttributes);
  62. engine.draw(true, 0, this.fillIndicesCount, pid._partData.usedElementCount);
  63. engine.unbindInstanceAttributes();
  64. }
  65. else {
  66. for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
  67. this.setupUniforms(effect, partIndex, pid._partData, i);
  68. engine.draw(true, 0, this.fillIndicesCount);
  69. }
  70. }
  71. }
  72. if (this.effectBorder) {
  73. var partIndex = instanceInfo.partIndexFromId.get(BABYLON.Shape2D.SHAPE2D_BORDERPARTID.toString());
  74. var pid = context.groupInfoPartData[partIndex];
  75. if (context.renderMode !== BABYLON.Render2DContext.RenderModeOpaque) {
  76. engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
  77. }
  78. var effect = context.useInstancing ? this.effectBorderInstanced : this.effectBorder;
  79. engine.enableEffect(effect);
  80. engine.bindBuffersDirectly(this.borderVB, this.borderIB, [1], 4, effect);
  81. if (context.useInstancing) {
  82. if (!this.instancingBorderAttributes) {
  83. this.instancingBorderAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, effect);
  84. }
  85. engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingBorderAttributes);
  86. engine.draw(true, 0, this.borderIndicesCount, pid._partData.usedElementCount);
  87. engine.unbindInstanceAttributes();
  88. }
  89. else {
  90. for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
  91. this.setupUniforms(effect, partIndex, pid._partData, i);
  92. engine.draw(true, 0, this.borderIndicesCount);
  93. }
  94. }
  95. }
  96. engine.setAlphaMode(curAlphaMode);
  97. if (this.effectFill && this.effectBorder) {
  98. engine.setDepthFunction(depthFunction);
  99. }
  100. return true;
  101. };
  102. Rectangle2DRenderCache.prototype.dispose = function () {
  103. if (!_super.prototype.dispose.call(this)) {
  104. return false;
  105. }
  106. if (this.fillVB) {
  107. this._engine._releaseBuffer(this.fillVB);
  108. this.fillVB = null;
  109. }
  110. if (this.fillIB) {
  111. this._engine._releaseBuffer(this.fillIB);
  112. this.fillIB = null;
  113. }
  114. if (this.effectFill) {
  115. this._engine._releaseEffect(this.effectFill);
  116. this.effectFill = null;
  117. }
  118. if (this.effectFillInstanced) {
  119. this._engine._releaseEffect(this.effectFillInstanced);
  120. this.effectFillInstanced = null;
  121. }
  122. if (this.borderVB) {
  123. this._engine._releaseBuffer(this.borderVB);
  124. this.borderVB = null;
  125. }
  126. if (this.borderIB) {
  127. this._engine._releaseBuffer(this.borderIB);
  128. this.borderIB = null;
  129. }
  130. if (this.effectBorder) {
  131. this._engine._releaseEffect(this.effectBorder);
  132. this.effectBorder = null;
  133. }
  134. if (this.effectBorderInstanced) {
  135. this._engine._releaseEffect(this.effectBorderInstanced);
  136. this.effectBorderInstanced = null;
  137. }
  138. return true;
  139. };
  140. return Rectangle2DRenderCache;
  141. }(BABYLON.ModelRenderCache));
  142. BABYLON.Rectangle2DRenderCache = Rectangle2DRenderCache;
  143. var Rectangle2DInstanceData = (function (_super) {
  144. __extends(Rectangle2DInstanceData, _super);
  145. function Rectangle2DInstanceData(partId) {
  146. _super.call(this, partId, 1);
  147. }
  148. Object.defineProperty(Rectangle2DInstanceData.prototype, "properties", {
  149. get: function () {
  150. return null;
  151. },
  152. enumerable: true,
  153. configurable: true
  154. });
  155. __decorate([
  156. BABYLON.instanceData()
  157. ], Rectangle2DInstanceData.prototype, "properties", null);
  158. return Rectangle2DInstanceData;
  159. }(BABYLON.Shape2DInstanceData));
  160. BABYLON.Rectangle2DInstanceData = Rectangle2DInstanceData;
  161. var Rectangle2D = (function (_super) {
  162. __extends(Rectangle2D, _super);
  163. /**
  164. * Create an Rectangle 2D Shape primitive. May be a sharp rectangle (with sharp corners), or a rounded one.
  165. * @param parent the parent primitive, must be a valid primitive (or the Canvas)
  166. * options:
  167. * - id a text identifier, for information purpose
  168. * - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
  169. * - origin: define the normalized origin point location, default [0.5;0.5]
  170. * - size: the size of the group. Alternatively the width and height properties can be set. Default will be [10;10].
  171. * - roundRadius: if the rectangle has rounded corner, set their radius, default is 0 (to get a sharp rectangle).
  172. * - fill: the brush used to draw the fill content of the ellipse, you can set null to draw nothing (but you will have to set a border brush), default is a SolidColorBrush of plain white.
  173. * - border: the brush used to draw the border of the ellipse, you can set null to draw nothing (but you will have to set a fill brush), default is null.
  174. * - borderThickness: the thickness of the drawn border, default is 1.
  175. * - isVisible: true if the primitive must be visible, false for hidden. Default is true.
  176. * - marginTop/Left/Right/Bottom: define the margin for the corresponding edge, if all of them are null, margin is not used in layout computing. Default Value is null for each.
  177. * - hAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
  178. * - vAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
  179. */
  180. function Rectangle2D(settings) {
  181. // Avoid checking every time if the object exists
  182. if (settings == null) {
  183. settings = {};
  184. }
  185. _super.call(this, settings);
  186. if (settings.size != null) {
  187. this.size = settings.size;
  188. }
  189. else if (settings.width || settings.height) {
  190. var size = new BABYLON.Size(settings.width, settings.height);
  191. this.size = size;
  192. }
  193. //let size = settings.size || (new Size((settings.width === null) ? null : (settings.width || 10), (settings.height === null) ? null : (settings.height || 10)));
  194. var roundRadius = (settings.roundRadius == null) ? 0 : settings.roundRadius;
  195. var borderThickness = (settings.borderThickness == null) ? 1 : settings.borderThickness;
  196. //this.size = size;
  197. this.roundRadius = roundRadius;
  198. this.borderThickness = borderThickness;
  199. }
  200. Object.defineProperty(Rectangle2D.prototype, "actualSize", {
  201. get: function () {
  202. if (this._actualSize) {
  203. return this._actualSize;
  204. }
  205. return this.size;
  206. },
  207. set: function (value) {
  208. this._actualSize = value;
  209. },
  210. enumerable: true,
  211. configurable: true
  212. });
  213. Object.defineProperty(Rectangle2D.prototype, "notRounded", {
  214. get: function () {
  215. return this._notRounded;
  216. },
  217. set: function (value) {
  218. this._notRounded = value;
  219. },
  220. enumerable: true,
  221. configurable: true
  222. });
  223. Object.defineProperty(Rectangle2D.prototype, "roundRadius", {
  224. get: function () {
  225. return this._roundRadius;
  226. },
  227. set: function (value) {
  228. this._roundRadius = value;
  229. this.notRounded = value === 0;
  230. this._positioningDirty();
  231. },
  232. enumerable: true,
  233. configurable: true
  234. });
  235. Rectangle2D.prototype.levelIntersect = function (intersectInfo) {
  236. // If we got there it mean the boundingInfo intersection succeed, if the rectangle has not roundRadius, it means it succeed!
  237. if (this.notRounded) {
  238. return true;
  239. }
  240. // If we got so far it means the bounding box at least passed, so we know it's inside the bounding rectangle, but it can be outside the roundedRectangle.
  241. // The easiest way is to check if the point is inside on of the four corners area (a little square of roundRadius size at the four corners)
  242. // If it's the case for one, check if the mouse is located in the quarter that we care about (the one who is visible) then finally make a distance check with the roundRadius radius to see if it's inside the circle quarter or outside.
  243. // First let remove the origin out the equation, to have the rectangle with an origin at bottom/left
  244. var size = this.size;
  245. Rectangle2D._i0.x = intersectInfo._localPickPosition.x;
  246. Rectangle2D._i0.y = intersectInfo._localPickPosition.y;
  247. var rr = this.roundRadius;
  248. var rrs = rr * rr;
  249. // Check if the point is in the bottom/left quarter area
  250. Rectangle2D._i1.x = rr;
  251. Rectangle2D._i1.y = rr;
  252. if (Rectangle2D._i0.x <= Rectangle2D._i1.x && Rectangle2D._i0.y <= Rectangle2D._i1.y) {
  253. // Compute the intersection point in the quarter local space
  254. Rectangle2D._i2.x = Rectangle2D._i0.x - Rectangle2D._i1.x;
  255. Rectangle2D._i2.y = Rectangle2D._i0.y - Rectangle2D._i1.y;
  256. // It's a hit if the squared distance is less/equal to the squared radius of the round circle
  257. return Rectangle2D._i2.lengthSquared() <= rrs;
  258. }
  259. // Check if the point is in the top/left quarter area
  260. Rectangle2D._i1.x = rr;
  261. Rectangle2D._i1.y = size.height - rr;
  262. if (Rectangle2D._i0.x <= Rectangle2D._i1.x && Rectangle2D._i0.y >= Rectangle2D._i1.y) {
  263. // Compute the intersection point in the quarter local space
  264. Rectangle2D._i2.x = Rectangle2D._i0.x - Rectangle2D._i1.x;
  265. Rectangle2D._i2.y = Rectangle2D._i0.y - Rectangle2D._i1.y;
  266. // It's a hit if the squared distance is less/equal to the squared radius of the round circle
  267. return Rectangle2D._i2.lengthSquared() <= rrs;
  268. }
  269. // Check if the point is in the top/right quarter area
  270. Rectangle2D._i1.x = size.width - rr;
  271. Rectangle2D._i1.y = size.height - rr;
  272. if (Rectangle2D._i0.x >= Rectangle2D._i1.x && Rectangle2D._i0.y >= Rectangle2D._i1.y) {
  273. // Compute the intersection point in the quarter local space
  274. Rectangle2D._i2.x = Rectangle2D._i0.x - Rectangle2D._i1.x;
  275. Rectangle2D._i2.y = Rectangle2D._i0.y - Rectangle2D._i1.y;
  276. // It's a hit if the squared distance is less/equal to the squared radius of the round circle
  277. return Rectangle2D._i2.lengthSquared() <= rrs;
  278. }
  279. // Check if the point is in the bottom/right quarter area
  280. Rectangle2D._i1.x = size.width - rr;
  281. Rectangle2D._i1.y = rr;
  282. if (Rectangle2D._i0.x >= Rectangle2D._i1.x && Rectangle2D._i0.y <= Rectangle2D._i1.y) {
  283. // Compute the intersection point in the quarter local space
  284. Rectangle2D._i2.x = Rectangle2D._i0.x - Rectangle2D._i1.x;
  285. Rectangle2D._i2.y = Rectangle2D._i0.y - Rectangle2D._i1.y;
  286. // It's a hit if the squared distance is less/equal to the squared radius of the round circle
  287. return Rectangle2D._i2.lengthSquared() <= rrs;
  288. }
  289. // At any other locations the point is guarantied to be inside
  290. return true;
  291. };
  292. Rectangle2D.prototype.updateLevelBoundingInfo = function () {
  293. BABYLON.BoundingInfo2D.CreateFromSizeToRef(this.actualSize, this._levelBoundingInfo);
  294. };
  295. Rectangle2D.prototype.createModelRenderCache = function (modelKey) {
  296. var renderCache = new Rectangle2DRenderCache(this.owner.engine, modelKey);
  297. return renderCache;
  298. };
  299. Rectangle2D.prototype.setupModelRenderCache = function (modelRenderCache) {
  300. var renderCache = modelRenderCache;
  301. var engine = this.owner.engine;
  302. // Need to create WebGL resources for fill part?
  303. if (this.fill) {
  304. var vbSize = ((this.notRounded ? 1 : Rectangle2D.roundSubdivisions) * 4) + 1;
  305. var vb = new Float32Array(vbSize);
  306. for (var i = 0; i < vbSize; i++) {
  307. vb[i] = i;
  308. }
  309. renderCache.fillVB = engine.createVertexBuffer(vb);
  310. var triCount = vbSize - 1;
  311. var ib = new Float32Array(triCount * 3);
  312. for (var i = 0; i < triCount; i++) {
  313. ib[i * 3 + 0] = 0;
  314. ib[i * 3 + 2] = i + 1;
  315. ib[i * 3 + 1] = i + 2;
  316. }
  317. ib[triCount * 3 - 2] = 1;
  318. renderCache.fillIB = engine.createIndexBuffer(ib);
  319. renderCache.fillIndicesCount = triCount * 3;
  320. // Get the instanced version of the effect, if the engine does not support it, null is return and we'll only draw on by one
  321. var ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_FILLPARTID, ["index"], true);
  322. if (ei) {
  323. renderCache.effectFillInstanced = engine.createEffect("rect2d", ei.attributes, ei.uniforms, [], ei.defines, null);
  324. }
  325. // Get the non instanced version
  326. ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_FILLPARTID, ["index"], false);
  327. renderCache.effectFill = engine.createEffect("rect2d", ei.attributes, ei.uniforms, [], ei.defines, null);
  328. }
  329. // Need to create WebGL resource for border part?
  330. if (this.border) {
  331. var vbSize = (this.notRounded ? 1 : Rectangle2D.roundSubdivisions) * 4 * 2;
  332. var vb = new Float32Array(vbSize);
  333. for (var i = 0; i < vbSize; i++) {
  334. vb[i] = i;
  335. }
  336. renderCache.borderVB = engine.createVertexBuffer(vb);
  337. var triCount = vbSize;
  338. var rs = triCount / 2;
  339. var ib = new Float32Array(triCount * 3);
  340. for (var i = 0; i < rs; i++) {
  341. var r0 = i;
  342. var r1 = (i + 1) % rs;
  343. ib[i * 6 + 0] = rs + r1;
  344. ib[i * 6 + 1] = rs + r0;
  345. ib[i * 6 + 2] = r0;
  346. ib[i * 6 + 3] = r1;
  347. ib[i * 6 + 4] = rs + r1;
  348. ib[i * 6 + 5] = r0;
  349. }
  350. renderCache.borderIB = engine.createIndexBuffer(ib);
  351. renderCache.borderIndicesCount = triCount * 3;
  352. // Get the instanced version of the effect, if the engine does not support it, null is return and we'll only draw on by one
  353. var ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, ["index"], true);
  354. if (ei) {
  355. renderCache.effectBorderInstanced = engine.createEffect("rect2d", ei.attributes, ei.uniforms, [], ei.defines, null);
  356. }
  357. // Get the non instanced version
  358. ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, ["index"], false);
  359. renderCache.effectBorder = engine.createEffect("rect2d", ei.attributes, ei.uniforms, [], ei.defines, null);
  360. }
  361. return renderCache;
  362. };
  363. // We override this method because if there's a roundRadius set, we will reduce the initial Content Area to make sure the computed area won't intersect with the shape contour. The formula is simple: we shrink the incoming size by the amount of the roundRadius
  364. Rectangle2D.prototype._getInitialContentAreaToRef = function (primSize, initialContentPosition, initialContentArea) {
  365. // Fall back to default implementation if there's no round Radius
  366. if (this._notRounded) {
  367. _super.prototype._getInitialContentAreaToRef.call(this, primSize, initialContentPosition, initialContentArea);
  368. }
  369. else {
  370. var rr = Math.round((this.roundRadius - (this.roundRadius / Math.sqrt(2))) * 1.3);
  371. initialContentPosition.x = initialContentPosition.y = rr;
  372. initialContentArea.width = Math.max(0, primSize.width - (rr * 2));
  373. initialContentArea.height = Math.max(0, primSize.height - (rr * 2));
  374. }
  375. };
  376. Rectangle2D.prototype.createInstanceDataParts = function () {
  377. var res = new Array();
  378. if (this.border) {
  379. res.push(new Rectangle2DInstanceData(BABYLON.Shape2D.SHAPE2D_BORDERPARTID));
  380. }
  381. if (this.fill) {
  382. res.push(new Rectangle2DInstanceData(BABYLON.Shape2D.SHAPE2D_FILLPARTID));
  383. }
  384. return res;
  385. };
  386. Rectangle2D.prototype.refreshInstanceDataPart = function (part) {
  387. if (!_super.prototype.refreshInstanceDataPart.call(this, part)) {
  388. return false;
  389. }
  390. if (part.id === BABYLON.Shape2D.SHAPE2D_BORDERPARTID) {
  391. var d = part;
  392. var size = this.actualSize;
  393. d.properties = new BABYLON.Vector3(size.width, size.height, this.roundRadius || 0);
  394. }
  395. else if (part.id === BABYLON.Shape2D.SHAPE2D_FILLPARTID) {
  396. var d = part;
  397. var size = this.actualSize;
  398. d.properties = new BABYLON.Vector3(size.width, size.height, this.roundRadius || 0);
  399. }
  400. return true;
  401. };
  402. Rectangle2D._i0 = BABYLON.Vector2.Zero();
  403. Rectangle2D._i1 = BABYLON.Vector2.Zero();
  404. Rectangle2D._i2 = BABYLON.Vector2.Zero();
  405. Rectangle2D.roundSubdivisions = 16;
  406. __decorate([
  407. BABYLON.instanceLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 1, function (pi) { return Rectangle2D.actualSizeProperty = pi; }, false, true)
  408. ], Rectangle2D.prototype, "actualSize", null);
  409. __decorate([
  410. BABYLON.modelLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 2, function (pi) { return Rectangle2D.notRoundedProperty = pi; })
  411. ], Rectangle2D.prototype, "notRounded", null);
  412. __decorate([
  413. BABYLON.instanceLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 3, function (pi) { return Rectangle2D.roundRadiusProperty = pi; })
  414. ], Rectangle2D.prototype, "roundRadius", null);
  415. Rectangle2D = __decorate([
  416. BABYLON.className("Rectangle2D")
  417. ], Rectangle2D);
  418. return Rectangle2D;
  419. }(BABYLON.Shape2D));
  420. BABYLON.Rectangle2D = Rectangle2D;
  421. })(BABYLON || (BABYLON = {}));