babylon.rectangle2d.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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. function Rectangle2D() {
  164. _super.apply(this, arguments);
  165. }
  166. Object.defineProperty(Rectangle2D.prototype, "actualSize", {
  167. get: function () {
  168. return this.size;
  169. },
  170. enumerable: true,
  171. configurable: true
  172. });
  173. Object.defineProperty(Rectangle2D.prototype, "size", {
  174. get: function () {
  175. return this._size;
  176. },
  177. set: function (value) {
  178. this._size = value;
  179. },
  180. enumerable: true,
  181. configurable: true
  182. });
  183. Object.defineProperty(Rectangle2D.prototype, "notRounded", {
  184. get: function () {
  185. return this._notRounded;
  186. },
  187. set: function (value) {
  188. this._notRounded = value;
  189. },
  190. enumerable: true,
  191. configurable: true
  192. });
  193. Object.defineProperty(Rectangle2D.prototype, "roundRadius", {
  194. get: function () {
  195. return this._roundRadius;
  196. },
  197. set: function (value) {
  198. this._roundRadius = value;
  199. this.notRounded = value === 0;
  200. },
  201. enumerable: true,
  202. configurable: true
  203. });
  204. Rectangle2D.prototype.levelIntersect = function (intersectInfo) {
  205. // If we got there it mean the boundingInfo intersection succeed, if the rectangle has not roundRadius, it means it succeed!
  206. if (this.notRounded) {
  207. return true;
  208. }
  209. // 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.
  210. // 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)
  211. // 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.
  212. // First let remove the origin out the equation, to have the rectangle with an origin at bottom/left
  213. var o = this.origin;
  214. var size = this.size;
  215. Rectangle2D._i0.x = intersectInfo._localPickPosition.x + (size.width * o.x);
  216. Rectangle2D._i0.y = intersectInfo._localPickPosition.y + (size.height * o.y);
  217. var rr = this.roundRadius;
  218. var rrs = rr * rr;
  219. // Check if the point is in the bottom/left quarter area
  220. Rectangle2D._i1.x = rr;
  221. Rectangle2D._i1.y = rr;
  222. if (Rectangle2D._i0.x <= Rectangle2D._i1.x && Rectangle2D._i0.y <= Rectangle2D._i1.y) {
  223. // Compute the intersection point in the quarter local space
  224. Rectangle2D._i2.x = Rectangle2D._i0.x - Rectangle2D._i1.x;
  225. Rectangle2D._i2.y = Rectangle2D._i0.y - Rectangle2D._i1.y;
  226. // It's a hit if the squared distance is less/equal to the squared radius of the round circle
  227. return Rectangle2D._i2.lengthSquared() <= rrs;
  228. }
  229. // Check if the point is in the top/left quarter area
  230. Rectangle2D._i1.x = rr;
  231. Rectangle2D._i1.y = size.height - rr;
  232. if (Rectangle2D._i0.x <= Rectangle2D._i1.x && Rectangle2D._i0.y >= Rectangle2D._i1.y) {
  233. // Compute the intersection point in the quarter local space
  234. Rectangle2D._i2.x = Rectangle2D._i0.x - Rectangle2D._i1.x;
  235. Rectangle2D._i2.y = Rectangle2D._i0.y - Rectangle2D._i1.y;
  236. // It's a hit if the squared distance is less/equal to the squared radius of the round circle
  237. return Rectangle2D._i2.lengthSquared() <= rrs;
  238. }
  239. // Check if the point is in the top/right quarter area
  240. Rectangle2D._i1.x = size.width - rr;
  241. Rectangle2D._i1.y = size.height - rr;
  242. if (Rectangle2D._i0.x >= Rectangle2D._i1.x && Rectangle2D._i0.y >= Rectangle2D._i1.y) {
  243. // Compute the intersection point in the quarter local space
  244. Rectangle2D._i2.x = Rectangle2D._i0.x - Rectangle2D._i1.x;
  245. Rectangle2D._i2.y = Rectangle2D._i0.y - Rectangle2D._i1.y;
  246. // It's a hit if the squared distance is less/equal to the squared radius of the round circle
  247. return Rectangle2D._i2.lengthSquared() <= rrs;
  248. }
  249. // Check if the point is in the bottom/right quarter area
  250. Rectangle2D._i1.x = size.width - 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. // At any other locations the point is guarantied to be inside
  260. return true;
  261. };
  262. Rectangle2D.prototype.updateLevelBoundingInfo = function () {
  263. BABYLON.BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo, this.origin);
  264. };
  265. Rectangle2D.prototype.setupRectangle2D = function (owner, parent, id, position, origin, size, roundRadius, fill, border, borderThickness, isVisible, marginTop, marginLeft, marginRight, marginBottom, vAlignment, hAlignment) {
  266. this.setupShape2D(owner, parent, id, position, origin, isVisible, fill, border, borderThickness, marginTop, marginLeft, marginRight, marginBottom, hAlignment, vAlignment);
  267. this.size = size;
  268. this.notRounded = !roundRadius;
  269. this.roundRadius = roundRadius;
  270. };
  271. /**
  272. * Create an Rectangle 2D Shape primitive. May be a sharp rectangle (with sharp corners), or a rounded one.
  273. * @param parent the parent primitive, must be a valid primitive (or the Canvas)
  274. * options:
  275. * - id a text identifier, for information purpose
  276. * - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
  277. * - origin: define the normalized origin point location, default [0.5;0.5]
  278. * - size: the size of the group. Alternatively the width and height properties can be set. Default will be [10;10].
  279. * - roundRadius: if the rectangle has rounded corner, set their radius, default is 0 (to get a sharp rectangle).
  280. * - 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.
  281. * - 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.
  282. * - borderThickness: the thickness of the drawn border, default is 1.
  283. * - isVisible: true if the primitive must be visible, false for hidden. Default is true.
  284. * - 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.
  285. * - hAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
  286. * - vAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
  287. */
  288. Rectangle2D.Create = function (parent, options) {
  289. BABYLON.Prim2DBase.CheckParent(parent);
  290. var rect = new Rectangle2D();
  291. if (!options) {
  292. rect.setupRectangle2D(parent.owner, parent, null, BABYLON.Vector2.Zero(), null, new BABYLON.Size(10, 10), 0, BABYLON.Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF"), null, 1, true, null, null, null, null, null, null);
  293. }
  294. else {
  295. var pos = options.position || new BABYLON.Vector2(options.x || 0, options.y || 0);
  296. var size = options.size || (new BABYLON.Size(options.width || 10, options.height || 10));
  297. var fill = options.fill === undefined ? BABYLON.Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF") : options.fill;
  298. rect.setupRectangle2D(parent.owner, parent, options.id || null, pos, options.origin || null, size, (options.roundRadius == null) ? 0 : options.roundRadius, fill, options.border || null, (options.borderThickness == null) ? 1 : options.borderThickness, options.isVisible || true, options.marginTop || null, options.marginLeft || null, options.marginRight || null, options.marginBottom || null, options.vAlignment || null, options.hAlignment || null);
  299. }
  300. return rect;
  301. };
  302. Rectangle2D.prototype.createModelRenderCache = function (modelKey) {
  303. var renderCache = new Rectangle2DRenderCache(this.owner.engine, modelKey);
  304. return renderCache;
  305. };
  306. Rectangle2D.prototype.setupModelRenderCache = function (modelRenderCache) {
  307. var renderCache = modelRenderCache;
  308. var engine = this.owner.engine;
  309. // Need to create WebGL resources for fill part?
  310. if (this.fill) {
  311. var vbSize = ((this.notRounded ? 1 : Rectangle2D.roundSubdivisions) * 4) + 1;
  312. var vb = new Float32Array(vbSize);
  313. for (var i = 0; i < vbSize; i++) {
  314. vb[i] = i;
  315. }
  316. renderCache.fillVB = engine.createVertexBuffer(vb);
  317. var triCount = vbSize - 1;
  318. var ib = new Float32Array(triCount * 3);
  319. for (var i = 0; i < triCount; i++) {
  320. ib[i * 3 + 0] = 0;
  321. ib[i * 3 + 2] = i + 1;
  322. ib[i * 3 + 1] = i + 2;
  323. }
  324. ib[triCount * 3 - 2] = 1;
  325. renderCache.fillIB = engine.createIndexBuffer(ib);
  326. renderCache.fillIndicesCount = triCount * 3;
  327. // 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
  328. var ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_FILLPARTID, ["index"], true);
  329. if (ei) {
  330. renderCache.effectFillInstanced = engine.createEffect("rect2d", ei.attributes, ei.uniforms, [], ei.defines, null);
  331. }
  332. // Get the non instanced version
  333. ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_FILLPARTID, ["index"], false);
  334. renderCache.effectFill = engine.createEffect("rect2d", ei.attributes, ei.uniforms, [], ei.defines, null);
  335. }
  336. // Need to create WebGL resource for border part?
  337. if (this.border) {
  338. var vbSize = (this.notRounded ? 1 : Rectangle2D.roundSubdivisions) * 4 * 2;
  339. var vb = new Float32Array(vbSize);
  340. for (var i = 0; i < vbSize; i++) {
  341. vb[i] = i;
  342. }
  343. renderCache.borderVB = engine.createVertexBuffer(vb);
  344. var triCount = vbSize;
  345. var rs = triCount / 2;
  346. var ib = new Float32Array(triCount * 3);
  347. for (var i = 0; i < rs; i++) {
  348. var r0 = i;
  349. var r1 = (i + 1) % rs;
  350. ib[i * 6 + 0] = rs + r1;
  351. ib[i * 6 + 1] = rs + r0;
  352. ib[i * 6 + 2] = r0;
  353. ib[i * 6 + 3] = r1;
  354. ib[i * 6 + 4] = rs + r1;
  355. ib[i * 6 + 5] = r0;
  356. }
  357. renderCache.borderIB = engine.createIndexBuffer(ib);
  358. renderCache.borderIndicesCount = triCount * 3;
  359. // 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
  360. var ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, ["index"], true);
  361. if (ei) {
  362. renderCache.effectBorderInstanced = engine.createEffect("rect2d", ei.attributes, ei.uniforms, [], ei.defines, null);
  363. }
  364. // Get the non instanced version
  365. ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, ["index"], false);
  366. renderCache.effectBorder = engine.createEffect("rect2d", ei.attributes, ei.uniforms, [], ei.defines, null);
  367. }
  368. return renderCache;
  369. };
  370. Rectangle2D.prototype.createInstanceDataParts = function () {
  371. var res = new Array();
  372. if (this.border) {
  373. res.push(new Rectangle2DInstanceData(BABYLON.Shape2D.SHAPE2D_BORDERPARTID));
  374. }
  375. if (this.fill) {
  376. res.push(new Rectangle2DInstanceData(BABYLON.Shape2D.SHAPE2D_FILLPARTID));
  377. }
  378. return res;
  379. };
  380. Rectangle2D.prototype.refreshInstanceDataPart = function (part) {
  381. if (!_super.prototype.refreshInstanceDataPart.call(this, part)) {
  382. return false;
  383. }
  384. if (part.id === BABYLON.Shape2D.SHAPE2D_BORDERPARTID) {
  385. var d = part;
  386. var size = this.size;
  387. d.properties = new BABYLON.Vector3(size.width, size.height, this.roundRadius || 0);
  388. }
  389. else if (part.id === BABYLON.Shape2D.SHAPE2D_FILLPARTID) {
  390. var d = part;
  391. var size = this.size;
  392. d.properties = new BABYLON.Vector3(size.width, size.height, this.roundRadius || 0);
  393. }
  394. return true;
  395. };
  396. Rectangle2D._i0 = BABYLON.Vector2.Zero();
  397. Rectangle2D._i1 = BABYLON.Vector2.Zero();
  398. Rectangle2D._i2 = BABYLON.Vector2.Zero();
  399. Rectangle2D.roundSubdivisions = 16;
  400. __decorate([
  401. BABYLON.instanceLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 1, function (pi) { return Rectangle2D.sizeProperty = pi; }, false, true)
  402. ], Rectangle2D.prototype, "size", null);
  403. __decorate([
  404. BABYLON.modelLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 2, function (pi) { return Rectangle2D.notRoundedProperty = pi; })
  405. ], Rectangle2D.prototype, "notRounded", null);
  406. __decorate([
  407. BABYLON.instanceLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 3, function (pi) { return Rectangle2D.roundRadiusProperty = pi; })
  408. ], Rectangle2D.prototype, "roundRadius", null);
  409. Rectangle2D = __decorate([
  410. BABYLON.className("Rectangle2D")
  411. ], Rectangle2D);
  412. return Rectangle2D;
  413. })(BABYLON.Shape2D);
  414. BABYLON.Rectangle2D = Rectangle2D;
  415. })(BABYLON || (BABYLON = {}));