babylon.group2d.ts 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160
  1. module BABYLON {
  2. @className("Group2D", "BABYLON")
  3. /**
  4. * A non renderable primitive that defines a logical group.
  5. * Can also serve the purpose of caching its content into a bitmap to reduce rendering overhead
  6. */
  7. export class Group2D extends Prim2DBase {
  8. static GROUP2D_PROPCOUNT: number = Prim2DBase.PRIM2DBASE_PROPCOUNT + 5;
  9. public static sizeProperty: Prim2DPropInfo;
  10. public static actualSizeProperty: Prim2DPropInfo;
  11. /**
  12. * Default behavior, the group will use the caching strategy defined at the Canvas Level
  13. */
  14. public static GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY = 0;
  15. /**
  16. * When used, this group's content won't be cached, no matter which strategy used.
  17. * If the group is part of a WorldSpace Canvas, its content will be drawn in the Canvas cache bitmap.
  18. */
  19. public static GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE = 1;
  20. /**
  21. * When used, the group's content will be cached in the nearest cached parent group/canvas
  22. */
  23. public static GROUPCACHEBEHAVIOR_CACHEINPARENTGROUP = 2;
  24. /**
  25. * You can specify this behavior to any cached Group2D to indicate that you don't want the cached content to be resized when the Group's actualScale is changing. It will draw the content stretched or shrink which is faster than a resize. This setting is obviously for performance consideration, don't use it if you want the best rendering quality
  26. */
  27. public static GROUPCACHEBEHAVIOR_NORESIZEONSCALE = 0x100;
  28. private static GROUPCACHEBEHAVIOR_OPTIONMASK = 0xFF;
  29. /**
  30. * Create an Logical or Renderable Group.
  31. * @param settings a combination of settings, possible ones are
  32. * - parent: the parent primitive/canvas, must be specified if the primitive is not constructed as a child of another one (i.e. as part of the children array setting)
  33. * - children: an array of direct children
  34. * - id a text identifier, for information purpose
  35. * - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
  36. * - rotation: the initial rotation (in radian) of the primitive. default is 0
  37. * - scale: the initial scale of the primitive. default is 1. You can alternatively use scaleX &| scaleY to apply non uniform scale
  38. * - dontInheritParentScale: if set the parent's scale won't be taken into consideration to compute the actualScale property
  39. * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
  40. * - zOrder: override the zOrder with the specified value
  41. * - origin: define the normalized origin point location, default [0.5;0.5]
  42. * - size: the size of the group. Alternatively the width and height properties can be set. If null the size will be computed from its content, default is null.
  43. * - cacheBehavior: Define how the group should behave regarding the Canvas's cache strategy, default is Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY
  44. * - layoutEngine: either an instance of a layout engine based class (StackPanel.Vertical, StackPanel.Horizontal) or a string ('canvas' for Canvas layout, 'StackPanel' or 'HorizontalStackPanel' for horizontal Stack Panel layout, 'VerticalStackPanel' for vertical Stack Panel layout).
  45. * - isVisible: true if the group must be visible, false for hidden. Default is true.
  46. * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
  47. * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
  48. * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
  49. * - levelCollision: this primitive is an actor of the Collision Manager and only this level will be used for collision (i.e. not the children). Use deepCollision if you want collision detection on the primitives and its children.
  50. * - deepCollision: this primitive is an actor of the Collision Manager, this level AND ALSO its children will be used for collision (note: you don't need to set the children as level/deepCollision).
  51. * - layoutData: a instance of a class implementing the ILayoutData interface that contain data to pass to the primitive parent's layout engine
  52. * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
  53. * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
  54. * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
  55. * - marginBottom: bottom margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
  56. * - margin: top, left, right and bottom margin formatted as a single string (see PrimitiveThickness.fromString)
  57. * - marginHAlignment: one value of the PrimitiveAlignment type's static properties
  58. * - marginVAlignment: one value of the PrimitiveAlignment type's static properties
  59. * - marginAlignment: a string defining the alignment, see PrimitiveAlignment.fromString
  60. * - paddingTop: top padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
  61. * - paddingLeft: left padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
  62. * - paddingRight: right padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
  63. * - paddingBottom: bottom padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
  64. * - padding: top, left, right and bottom padding formatted as a single string (see PrimitiveThickness.fromString)
  65. */
  66. constructor(settings?: {
  67. parent ?: Prim2DBase,
  68. children ?: Array<Prim2DBase>,
  69. id ?: string,
  70. position ?: Vector2,
  71. x ?: number,
  72. y ?: number,
  73. scale ?: number,
  74. scaleX ?: number,
  75. scaleY ?: number,
  76. dontInheritParentScale ?: boolean,
  77. trackNode ?: Node,
  78. opacity ?: number,
  79. zOrder ?: number,
  80. origin ?: Vector2,
  81. size ?: Size,
  82. width ?: number,
  83. height ?: number,
  84. cacheBehavior ?: number,
  85. layoutEngine ?: LayoutEngineBase | string,
  86. isVisible ?: boolean,
  87. isPickable ?: boolean,
  88. isContainer ?: boolean,
  89. childrenFlatZOrder ?: boolean,
  90. levelCollision ?: boolean,
  91. deepCollision ?: boolean,
  92. layoutData ?: ILayoutData,
  93. marginTop ?: number | string,
  94. marginLeft ?: number | string,
  95. marginRight ?: number | string,
  96. marginBottom ?: number | string,
  97. margin ?: number | string,
  98. marginHAlignment ?: number,
  99. marginVAlignment ?: number,
  100. marginAlignment ?: string,
  101. paddingTop ?: number | string,
  102. paddingLeft ?: number | string,
  103. paddingRight ?: number | string,
  104. paddingBottom ?: number | string,
  105. padding ?: string,
  106. }) {
  107. if (settings == null) {
  108. settings = {};
  109. }
  110. if (settings.origin == null) {
  111. settings.origin = new Vector2(0, 0);
  112. }
  113. super(settings);
  114. let size = (!settings.size && !settings.width && !settings.height) ? null : (settings.size || (new Size(settings.width || 0, settings.height || 0)));
  115. this._trackedNode = (settings.trackNode == null) ? null : settings.trackNode;
  116. if (this._trackedNode && this.owner) {
  117. this.owner._registerTrackedNode(this);
  118. }
  119. this._cacheBehavior = (settings.cacheBehavior == null) ? Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY : settings.cacheBehavior;
  120. let rd = this._renderableData;
  121. if (rd) {
  122. rd._noResizeOnScale = (this.cacheBehavior & Group2D.GROUPCACHEBEHAVIOR_NORESIZEONSCALE) !== 0;
  123. }
  124. this.size = size;
  125. this._viewportPosition = Vector2.Zero();
  126. this._viewportSize = Size.Zero();
  127. }
  128. static _createCachedCanvasGroup(owner: Canvas2D): Group2D {
  129. var g = new Group2D({ parent: owner, id: "__cachedCanvasGroup__", position: Vector2.Zero(), origin: Vector2.Zero(), size: null, isVisible: true, isPickable: false, dontInheritParentScale: true });
  130. return g;
  131. }
  132. protected applyCachedTexture(vertexData: VertexData, material: StandardMaterial) {
  133. this._bindCacheTarget();
  134. if (vertexData) {
  135. var uv = vertexData.uvs;
  136. let nodeuv = this._renderableData._cacheNodeUVs;
  137. for (let i = 0; i < 4; i++) {
  138. uv[i * 2 + 0] = nodeuv[i].x;
  139. uv[i * 2 + 1] = nodeuv[i].y;
  140. }
  141. }
  142. if (material) {
  143. material.diffuseTexture = this._renderableData._cacheTexture;
  144. material.emissiveColor = new Color3(1, 1, 1);
  145. }
  146. this._renderableData._cacheTexture.hasAlpha = true;
  147. this._unbindCacheTarget();
  148. }
  149. /**
  150. * Allow you to access the information regarding the cached rectangle of the Group2D into the MapTexture.
  151. * If the `noWorldSpaceNode` options was used at the creation of a WorldSpaceCanvas, the rendering of the canvas must be made by the caller, so typically you want to bind the cacheTexture property to some material/mesh and you MUST use the Group2D.cachedUVs property to get the UV coordinates to use for your quad that will display the Canvas and NOT the PackedRect.UVs property which are incorrect because the allocated surface may be bigger (due to over-provisioning or shrinking without deallocating) than what the Group is actually using.
  152. */
  153. public get cachedRect(): PackedRect {
  154. if (!this._renderableData) {
  155. return null;
  156. }
  157. return this._renderableData._cacheNode;
  158. }
  159. /**
  160. * The UVs into the MapTexture that map the cached group
  161. */
  162. public get cachedUVs(): Vector2[] {
  163. if (!this._renderableData) {
  164. return null;
  165. }
  166. return this._renderableData._cacheNodeUVs;
  167. }
  168. public get cachedUVsChanged(): Observable<Vector2[]> {
  169. if (!this._renderableData) {
  170. return null;
  171. }
  172. if (!this._renderableData._cacheNodeUVsChangedObservable) {
  173. this._renderableData._cacheNodeUVsChangedObservable = new Observable<Vector2[]>();
  174. }
  175. return this._renderableData._cacheNodeUVsChangedObservable;
  176. }
  177. /**
  178. * Access the texture that maintains a cached version of the Group2D.
  179. * This is useful only if you're not using a WorldSpaceNode for your WorldSpace Canvas and therefore need to perform the rendering yourself.
  180. */
  181. public get cacheTexture(): MapTexture {
  182. if (!this._renderableData) {
  183. return null;
  184. }
  185. return this._renderableData._cacheTexture;
  186. }
  187. /**
  188. * Call this method to remove this Group and its children from the Canvas
  189. */
  190. public dispose(): boolean {
  191. if (!super.dispose()) {
  192. return false;
  193. }
  194. if (this._trackedNode != null) {
  195. this.owner._unregisterTrackedNode(this);
  196. this._trackedNode = null;
  197. }
  198. if (this._renderableData) {
  199. this._renderableData.dispose(this.owner);
  200. this._renderableData = null;
  201. }
  202. return true;
  203. }
  204. /**
  205. * @returns Returns true if the Group render content, false if it's a logical group only
  206. */
  207. public get isRenderableGroup(): boolean {
  208. return this._isRenderableGroup;
  209. }
  210. /**
  211. * @returns only meaningful for isRenderableGroup, will be true if the content of the Group is cached into a texture, false if it's rendered every time
  212. */
  213. public get isCachedGroup(): boolean {
  214. return this._isCachedGroup;
  215. }
  216. @instanceLevelProperty(Prim2DBase.PRIM2DBASE_PROPCOUNT + 1, pi => Group2D.sizeProperty = pi, false, true)
  217. public get size(): Size {
  218. return this._size;
  219. }
  220. /**
  221. * Get/Set the size of the group. If null the size of the group will be determine from its content.
  222. * BEWARE: if the Group is a RenderableGroup and its content is cache the texture will be resized each time the group is getting bigger. For performance reason the opposite won't be true: the texture won't shrink if the group does.
  223. */
  224. public set size(val: Size) {
  225. this.internalSetSize(val);
  226. }
  227. public get viewportSize(): ISize {
  228. return this._viewportSize;
  229. }
  230. @instanceLevelProperty(Prim2DBase.PRIM2DBASE_PROPCOUNT + 2, pi => Group2D.actualSizeProperty = pi)
  231. /**
  232. * Get the actual size of the group, if the size property is not null, this value will be the same, but if size is null, actualSize will return the size computed from the group's bounding content.
  233. */
  234. public get actualSize(): Size {
  235. // The computed size will be floor on both width and height
  236. let actualSize: Size;
  237. // Return the actualSize if set
  238. if (this._actualSize) {
  239. return this._actualSize;
  240. }
  241. // Return the size if set by the user
  242. if (this._size) {
  243. actualSize = new Size(Math.ceil(this._size.width), Math.ceil(this._size.height));
  244. }
  245. // Otherwise the size is computed based on the boundingInfo of the layout (or bounding info) content
  246. else {
  247. let m = this.layoutBoundingInfo.max();
  248. actualSize = new Size(Math.ceil(m.x), Math.ceil(m.y));
  249. }
  250. // Compare the size with the one we previously had, if it differs we set the property dirty and trigger a GroupChanged to synchronize a displaySprite (if any)
  251. if (!actualSize.equals(this._actualSize)) {
  252. this.onPrimitivePropertyDirty(Group2D.actualSizeProperty.flagId);
  253. this._actualSize = actualSize;
  254. this.handleGroupChanged(Group2D.actualSizeProperty);
  255. }
  256. return actualSize;
  257. }
  258. public set actualSize(value: Size) {
  259. if (!this._actualSize) {
  260. this._actualSize = value.clone();
  261. } else {
  262. this._actualSize.copyFrom(value);
  263. }
  264. }
  265. /**
  266. * Get/set the Cache Behavior, used in case the Canvas Cache Strategy is set to CACHESTRATEGY_ALLGROUPS. Can be either GROUPCACHEBEHAVIOR_CACHEINPARENTGROUP, GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE or GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY. See their documentation for more information.
  267. * GROUPCACHEBEHAVIOR_NORESIZEONSCALE can also be set if you set it at creation time.
  268. * It is critical to understand than you HAVE TO play with this behavior in order to achieve a good performance/memory ratio. Caching all groups would certainly be the worst strategy of all.
  269. */
  270. public get cacheBehavior(): number {
  271. return this._cacheBehavior;
  272. }
  273. public _addPrimToDirtyList(prim: Prim2DBase) {
  274. this._renderableData._primDirtyList.push(prim);
  275. }
  276. public _renderCachedCanvas() {
  277. this.owner._addGroupRenderCount(1);
  278. this.updateCachedStates(true);
  279. let context = new PrepareRender2DContext();
  280. this._prepareGroupRender(context);
  281. this._groupRender();
  282. }
  283. /**
  284. * Get/set the Scene's Node that should be tracked, the group's position will follow the projected position of the Node.
  285. */
  286. public get trackedNode(): Node {
  287. return this._trackedNode;
  288. }
  289. public set trackedNode(val: Node) {
  290. if (val != null) {
  291. if (!this._isFlagSet(SmartPropertyPrim.flagTrackedGroup)) {
  292. this.owner._registerTrackedNode(this);
  293. }
  294. this._trackedNode = val;
  295. } else {
  296. if (this._isFlagSet(SmartPropertyPrim.flagTrackedGroup)) {
  297. this.owner._unregisterTrackedNode(this);
  298. }
  299. this._trackedNode = null;
  300. }
  301. }
  302. protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
  303. // If we've made it so far it means the boundingInfo intersection test succeed, the Group2D is shaped the same, so we always return true
  304. return true;
  305. }
  306. protected updateLevelBoundingInfo(): boolean {
  307. let size: Size;
  308. // If the size is set by the user, the boundingInfo is computed from this value
  309. if (this.size) {
  310. size = this.size;
  311. }
  312. // Otherwise the group's level bounding info is "collapsed"
  313. else {
  314. size = new Size(0, 0);
  315. }
  316. BoundingInfo2D.CreateFromSizeToRef(size, this._levelBoundingInfo);
  317. return true;
  318. }
  319. // Method called only on renderable groups to prepare the rendering
  320. protected _prepareGroupRender(context: PrepareRender2DContext) {
  321. let sortedDirtyList: Prim2DBase[] = null;
  322. // Update the Global Transformation and visibility status of the changed primitives
  323. let rd = this._renderableData;
  324. if ((rd._primDirtyList.length > 0) || context.forceRefreshPrimitive) {
  325. sortedDirtyList = rd._primDirtyList.sort((a, b) => a.hierarchyDepth - b.hierarchyDepth);
  326. this.updateCachedStatesOf(sortedDirtyList, true);
  327. }
  328. let s = this.actualSize;
  329. let a = this.actualScale;
  330. let sw = Math.ceil(s.width * a.x);
  331. let sh = Math.ceil(s.height * a.y);
  332. // The dimension must be overridden when using the designSize feature, the ratio is maintain to compute a uniform scale, which is mandatory but if the designSize's ratio is different from the rendering surface's ratio, content will be clipped in some cases.
  333. // So we set the width/height to the rendering's one because that's what we want for the viewport!
  334. if ((this instanceof Canvas2D || this.id === "__cachedCanvasGroup__") && this.owner.designSize != null) {
  335. sw = this.owner.engine.getRenderWidth();
  336. sh = this.owner.engine.getRenderHeight();
  337. }
  338. // Setup the size of the rendering viewport
  339. // In non cache mode, we're rendering directly to the rendering canvas, in this case we have to detect if the canvas size changed since the previous iteration, if it's the case all primitives must be prepared again because their transformation must be recompute
  340. if (!this._isCachedGroup) {
  341. // Compute the WebGL viewport's location/size
  342. let t = this._globalTransform.getTranslation();
  343. let rs = this.owner._renderingSize;
  344. sh = Math.min(sh, rs.height - t.y);
  345. sw = Math.min(sw, rs.width - t.x);
  346. let x = t.x;
  347. let y = t.y;
  348. // The viewport where we're rendering must be the size of the canvas if this one fit in the rendering screen or clipped to the screen dimensions if needed
  349. this._viewportPosition.x = x;
  350. this._viewportPosition.y = y;
  351. }
  352. // For a cachedGroup we also check of the group's actualSize is changing, if it's the case then the rendering zone will be change so we also have to dirty all primitives to prepare them again.
  353. if (this._viewportSize.width !== sw || this._viewportSize.height !== sh) {
  354. context.forceRefreshPrimitive = true;
  355. this._viewportSize.width = sw;
  356. this._viewportSize.height = sh;
  357. }
  358. if ((rd._primDirtyList.length > 0) || context.forceRefreshPrimitive) {
  359. // If the group is cached, set the dirty flag to true because of the incoming changes
  360. this._cacheGroupDirty = this._isCachedGroup;
  361. rd._primNewDirtyList.splice(0);
  362. // If it's a force refresh, prepare all the children
  363. if (context.forceRefreshPrimitive) {
  364. for (let p of this._children) {
  365. p._prepareRender(context);
  366. }
  367. } else {
  368. // Each primitive that changed at least once was added into the primDirtyList, we have to sort this level using
  369. // the hierarchyDepth in order to prepare primitives from top to bottom
  370. if (!sortedDirtyList) {
  371. sortedDirtyList = rd._primDirtyList.sort((a, b) => a.hierarchyDepth - b.hierarchyDepth);
  372. }
  373. sortedDirtyList.forEach(p => {
  374. // We need to check if prepare is needed because even if the primitive is in the dirtyList, its parent primitive may also have been modified, then prepared, then recurse on its children primitives (this one for instance) if the changes where impacting them.
  375. // For instance: a Rect's position change, the position of its children primitives will also change so a prepare will be call on them. If a child was in the dirtyList we will avoid a second prepare by making this check.
  376. if (!p.isDisposed && p._needPrepare()) {
  377. p._prepareRender(context);
  378. }
  379. });
  380. }
  381. // Everything is updated, clear the dirty list
  382. rd._primDirtyList.forEach(p => {
  383. if (rd._primNewDirtyList.indexOf(p) === -1) {
  384. p._resetPropertiesDirty();
  385. } else {
  386. p._setFlags(SmartPropertyPrim.flagNeedRefresh);
  387. }
  388. });
  389. rd._primDirtyList.splice(0);
  390. rd._primDirtyList = rd._primDirtyList.concat(rd._primNewDirtyList);
  391. }
  392. // A renderable group has a list of direct children that are also renderable groups, we recurse on them to also prepare them
  393. rd._childrenRenderableGroups.forEach(g => {
  394. g._prepareGroupRender(context);
  395. });
  396. }
  397. protected _groupRender() {
  398. let engine = this.owner.engine;
  399. let failedCount = 0;
  400. // First recurse to children render group to render them (in their cache or on screen)
  401. for (let childGroup of this._renderableData._childrenRenderableGroups) {
  402. childGroup._groupRender();
  403. }
  404. // Render the primitives if needed: either if we don't cache the content or if the content is cached but has changed
  405. if (!this.isCachedGroup || this._cacheGroupDirty) {
  406. this.owner._addGroupRenderCount(1);
  407. if (this.isCachedGroup) {
  408. this._bindCacheTarget();
  409. } else {
  410. var curVP = engine.setDirectViewport(this._viewportPosition.x, this._viewportPosition.y, this._viewportSize.width, this._viewportSize.height);
  411. }
  412. let curAlphaTest = engine.getAlphaTesting() === true;
  413. let curDepthWrite = engine.getDepthWrite() === true;
  414. // ===================================================================
  415. // First pass, update the InstancedArray and render Opaque primitives
  416. // Disable Alpha Testing, Enable Depth Write
  417. engine.setAlphaTesting(false);
  418. engine.setDepthWrite(true);
  419. // For each different model of primitive to render
  420. let context = new Render2DContext(Render2DContext.RenderModeOpaque);
  421. this._renderableData._renderGroupInstancesInfo.forEach((k, v) => {
  422. // Prepare the context object, update the WebGL Instanced Array buffer if needed
  423. let renderCount = this._prepareContext(engine, context, v);
  424. // If null is returned, there's no opaque data to render
  425. if (renderCount === null) {
  426. return;
  427. }
  428. // Submit render only if we have something to render (everything may be hidden and the floatarray empty)
  429. if (!this.owner.supportInstancedArray || renderCount > 0) {
  430. // render all the instances of this model, if the render method returns true then our instances are no longer dirty
  431. let renderFailed = !v.modelRenderCache.render(v, context);
  432. // Update dirty flag/related
  433. v.opaqueDirty = renderFailed;
  434. failedCount += renderFailed ? 1 : 0;
  435. }
  436. });
  437. // =======================================================================
  438. // Second pass, update the InstancedArray and render AlphaTest primitives
  439. // Enable Alpha Testing, Enable Depth Write
  440. engine.setAlphaTesting(true);
  441. engine.setDepthWrite(true);
  442. // For each different model of primitive to render
  443. context = new Render2DContext(Render2DContext.RenderModeAlphaTest);
  444. this._renderableData._renderGroupInstancesInfo.forEach((k, v) => {
  445. // Prepare the context object, update the WebGL Instanced Array buffer if needed
  446. let renderCount = this._prepareContext(engine, context, v);
  447. // If null is returned, there's no opaque data to render
  448. if (renderCount === null) {
  449. return;
  450. }
  451. // Submit render only if we have something to render (everything may be hidden and the floatarray empty)
  452. if (!this.owner.supportInstancedArray || renderCount > 0) {
  453. // render all the instances of this model, if the render method returns true then our instances are no longer dirty
  454. let renderFailed = !v.modelRenderCache.render(v, context);
  455. // Update dirty flag/related
  456. v.opaqueDirty = renderFailed;
  457. failedCount += renderFailed ? 1 : 0;
  458. }
  459. });
  460. // =======================================================================
  461. // Third pass, transparent primitive rendering
  462. // Enable Alpha Testing, Disable Depth Write
  463. engine.setAlphaTesting(true);
  464. engine.setDepthWrite(false);
  465. // First Check if the transparent List change so we can update the TransparentSegment and PartData (sort if needed)
  466. if (this._renderableData._transparentListChanged) {
  467. this._updateTransparentData();
  468. }
  469. // From this point on we have up to date data to render, so let's go
  470. failedCount += this._renderTransparentData();
  471. // =======================================================================
  472. // Unbind target/restore viewport setting, clear dirty flag, and quit
  473. // The group's content is no longer dirty
  474. this._cacheGroupDirty = failedCount !== 0;
  475. if (this.isCachedGroup) {
  476. this._unbindCacheTarget();
  477. } else {
  478. if (curVP) {
  479. engine.setViewport(curVP);
  480. }
  481. }
  482. // Restore saved states
  483. engine.setAlphaTesting(curAlphaTest);
  484. engine.setDepthWrite(curDepthWrite);
  485. }
  486. }
  487. public _setCacheGroupDirty() {
  488. this._cacheGroupDirty = true;
  489. }
  490. private _updateTransparentData() {
  491. this.owner._addUpdateTransparentDataCount(1);
  492. let rd = this._renderableData;
  493. // Sort all the primitive from their depth, max (bottom) to min (top)
  494. rd._transparentPrimitives.sort((a, b) => b._primitive.actualZOffset - a._primitive.actualZOffset);
  495. let checkAndAddPrimInSegment = (seg: TransparentSegment, tpiI: number): boolean => {
  496. let tpi = rd._transparentPrimitives[tpiI];
  497. // Fast rejection: if gii are different
  498. if (seg.groupInsanceInfo !== tpi._groupInstanceInfo) {
  499. return false;
  500. }
  501. //let tpiZ = tpi._primitive.actualZOffset;
  502. // We've made it so far, the tpi can be part of the segment, add it
  503. tpi._transparentSegment = seg;
  504. tpi._primitive._updateTransparentSegmentIndices(seg);
  505. return true;
  506. }
  507. // Free the existing TransparentSegments
  508. for (let ts of rd._transparentSegments) {
  509. ts.dispose(this.owner.engine);
  510. }
  511. rd._transparentSegments.splice(0);
  512. let prevSeg = null;
  513. for (let tpiI = 0; tpiI < rd._transparentPrimitives.length; tpiI++) {
  514. let tpi = rd._transparentPrimitives[tpiI];
  515. // Check if the Data in which the primitive is stored is not sorted properly
  516. if (tpi._groupInstanceInfo.transparentOrderDirty) {
  517. tpi._groupInstanceInfo.sortTransparentData();
  518. }
  519. // Reset the segment, we have to create/rebuild it
  520. tpi._transparentSegment = null;
  521. // If there's a previous valid segment, check if this prim can be part of it
  522. if (prevSeg) {
  523. checkAndAddPrimInSegment(prevSeg, tpiI);
  524. }
  525. // If we couldn't insert in the adjacent segments, he have to create one
  526. if (!tpi._transparentSegment) {
  527. let ts = new TransparentSegment();
  528. ts.groupInsanceInfo = tpi._groupInstanceInfo;
  529. let prim = tpi._primitive;
  530. ts.startZ = prim.actualZOffset;
  531. prim._updateTransparentSegmentIndices(ts);
  532. ts.endZ = ts.startZ;
  533. tpi._transparentSegment = ts;
  534. rd._transparentSegments.push(ts);
  535. }
  536. // Update prevSeg
  537. prevSeg = tpi._transparentSegment;
  538. }
  539. //rd._firstChangedPrim = null;
  540. rd._transparentListChanged = false;
  541. }
  542. private _renderTransparentData(): number {
  543. let failedCount = 0;
  544. let context = new Render2DContext(Render2DContext.RenderModeTransparent);
  545. let rd = this._renderableData;
  546. let useInstanced = this.owner.supportInstancedArray;
  547. let length = rd._transparentSegments.length;
  548. for (let i = 0; i < length; i++) {
  549. context.instancedBuffers = null;
  550. let ts = rd._transparentSegments[i];
  551. let gii = ts.groupInsanceInfo;
  552. let mrc = gii.modelRenderCache;
  553. let engine = this.owner.engine;
  554. let count = ts.endDataIndex - ts.startDataIndex;
  555. // Use Instanced Array if it's supported and if there's at least minPartCountToUseInstancedArray prims to draw.
  556. // We don't want to create an Instanced Buffer for less that minPartCountToUseInstancedArray prims
  557. if (useInstanced && count >= this.owner.minPartCountToUseInstancedArray) {
  558. if (!ts.partBuffers) {
  559. let buffers = new Array<WebGLBuffer>();
  560. for (let j = 0; j < gii.transparentData.length; j++) {
  561. let gitd = gii.transparentData[j];
  562. let dfa = gitd._partData;
  563. let data = dfa.pack();
  564. let stride = dfa.stride;
  565. let neededSize = count * stride * 4;
  566. let buffer = engine.createInstancesBuffer(neededSize); // Create + bind
  567. let segData = data.subarray(ts.startDataIndex * stride, ts.endDataIndex * stride);
  568. engine.updateArrayBuffer(segData);
  569. buffers.push(buffer);
  570. }
  571. ts.partBuffers = buffers;
  572. } else if (gii.transparentDirty) {
  573. for (let j = 0; j < gii.transparentData.length; j++) {
  574. let gitd = gii.transparentData[j];
  575. let dfa = gitd._partData;
  576. let data = dfa.pack();
  577. let stride = dfa.stride;
  578. let buffer = ts.partBuffers[j];
  579. let segData = data.subarray(ts.startDataIndex * stride, ts.endDataIndex * stride);
  580. engine.bindArrayBuffer(buffer);
  581. engine.updateArrayBuffer(segData);
  582. }
  583. }
  584. context.useInstancing = true;
  585. context.instancesCount = count;
  586. context.instancedBuffers = ts.partBuffers;
  587. context.groupInfoPartData = gii.transparentData;
  588. let renderFailed = !mrc.render(gii, context);
  589. failedCount += renderFailed ? 1 : 0;
  590. } else {
  591. context.useInstancing = false;
  592. context.partDataStartIndex = ts.startDataIndex;
  593. context.partDataEndIndex = ts.endDataIndex;
  594. context.groupInfoPartData = gii.transparentData;
  595. let renderFailed = !mrc.render(gii, context);
  596. failedCount += renderFailed ? 1 : 0;
  597. }
  598. }
  599. return failedCount;
  600. }
  601. private _prepareContext(engine: Engine, context: Render2DContext, gii: GroupInstanceInfo): number {
  602. let gipd: GroupInfoPartData[] = null;
  603. let setDirty: (dirty: boolean) => void;
  604. let getDirty: () => boolean;
  605. // Render Mode specifics
  606. switch (context.renderMode) {
  607. case Render2DContext.RenderModeOpaque:
  608. {
  609. if (!gii.hasOpaqueData) {
  610. return null;
  611. }
  612. setDirty = (dirty: boolean) => { gii.opaqueDirty = dirty; };
  613. getDirty = () => gii.opaqueDirty;
  614. context.groupInfoPartData = gii.opaqueData;
  615. gipd = gii.opaqueData;
  616. break;
  617. }
  618. case Render2DContext.RenderModeAlphaTest:
  619. {
  620. if (!gii.hasAlphaTestData) {
  621. return null;
  622. }
  623. setDirty = (dirty: boolean) => { gii.alphaTestDirty = dirty; };
  624. getDirty = () => gii.alphaTestDirty;
  625. context.groupInfoPartData = gii.alphaTestData;
  626. gipd = gii.alphaTestData;
  627. break;
  628. }
  629. default:
  630. throw new Error("_prepareContext is only for opaque or alphaTest");
  631. }
  632. let renderCount = 0;
  633. // This part will pack the dynamicfloatarray and update the instanced array WebGLBufffer
  634. // Skip it if instanced arrays are not supported
  635. if (this.owner.supportInstancedArray) {
  636. // Flag for instancing
  637. context.useInstancing = true;
  638. // Make sure all the WebGLBuffers of the Instanced Array are created/up to date for the parts to render.
  639. for (let i = 0; i < gipd.length; i++) {
  640. let pid = gipd[i];
  641. // If the instances of the model was changed, pack the data
  642. let array = pid._partData;
  643. let instanceData = array.pack();
  644. renderCount += array.usedElementCount;
  645. // Compute the size the instance buffer should have
  646. let neededSize = array.usedElementCount * array.stride * 4;
  647. // Check if we have to (re)create the instancesBuffer because there's none or the size is too small
  648. if (!pid._partBuffer || (pid._partBufferSize < neededSize)) {
  649. if (pid._partBuffer) {
  650. engine.deleteInstancesBuffer(pid._partBuffer);
  651. }
  652. pid._partBuffer = engine.createInstancesBuffer(neededSize); // Create + bind
  653. pid._partBufferSize = neededSize;
  654. setDirty(false);
  655. // Update the WebGL buffer to match the new content of the instances data
  656. engine.updateArrayBuffer(instanceData);
  657. } else if (getDirty()) {
  658. // Update the WebGL buffer to match the new content of the instances data
  659. engine.bindArrayBuffer(pid._partBuffer);
  660. engine.updateArrayBuffer(instanceData);
  661. }
  662. }
  663. setDirty(false);
  664. }
  665. // Can't rely on hardware instancing, use the DynamicFloatArray instance, render its whole content
  666. else {
  667. context.partDataStartIndex = 0;
  668. // Find the first valid object to get the count
  669. if (context.groupInfoPartData.length > 0) {
  670. let i = 0;
  671. while (!context.groupInfoPartData[i]) {
  672. i++;
  673. }
  674. context.partDataEndIndex = context.groupInfoPartData[i]._partData.usedElementCount;
  675. }
  676. }
  677. return renderCount;
  678. }
  679. protected _setRenderingScale(scale: number) {
  680. if (this._renderableData._renderingScale === scale) {
  681. return;
  682. }
  683. this._renderableData._renderingScale = scale;
  684. }
  685. private static _uV = new Vector2(1, 1);
  686. private static _s = Size.Zero();
  687. private _bindCacheTarget() {
  688. let curWidth: number;
  689. let curHeight: number;
  690. let rd = this._renderableData;
  691. let rs = rd._renderingScale;
  692. let noResizeScale = rd._noResizeOnScale;
  693. let isCanvas = this.parent == null;
  694. let scale: Vector2;
  695. if (noResizeScale) {
  696. scale = isCanvas ? Group2D._uV: this.parent.actualScale;
  697. } else {
  698. scale = this.actualScale;
  699. }
  700. if (isCanvas && this.owner.cachingStrategy===Canvas2D.CACHESTRATEGY_CANVAS && this.owner.isScreenSpace) {
  701. if(this.owner.designSize || this.owner.fitRenderingDevice){
  702. Group2D._s.width = this.owner.engine.getRenderWidth();
  703. Group2D._s.height = this.owner.engine.getRenderHeight();
  704. }
  705. else{
  706. Group2D._s.copyFrom(this.owner.size);
  707. }
  708. } else {
  709. Group2D._s.width = Math.ceil(this.actualSize.width * scale.x * rs);
  710. Group2D._s.height = Math.ceil(this.actualSize.height * scale.y * rs);
  711. }
  712. let sizeChanged = !Group2D._s.equals(rd._cacheSize);
  713. if (rd._cacheNode) {
  714. let size = rd._cacheNode.contentSize;
  715. // Check if we have to deallocate because the size is too small
  716. if ((size.width < Group2D._s.width) || (size.height < Group2D._s.height)) {
  717. // For Screen space: over-provisioning of 7% more to avoid frequent resizing for few pixels...
  718. // For World space: no over-provisioning
  719. let overprovisioning = this.owner.isScreenSpace ? 1.07 : 1;
  720. curWidth = Math.floor(Group2D._s.width * overprovisioning);
  721. curHeight = Math.floor(Group2D._s.height * overprovisioning);
  722. //console.log(`[${this._globalTransformProcessStep}] Resize group ${this.id}, width: ${curWidth}, height: ${curHeight}`);
  723. rd._cacheTexture.freeRect(rd._cacheNode);
  724. rd._cacheNode = null;
  725. }
  726. }
  727. if (!rd._cacheNode) {
  728. // Check if we have to allocate a rendering zone in the global cache texture
  729. var res = this.owner._allocateGroupCache(this, this.parent && this.parent.renderGroup, curWidth ? new Size(curWidth, curHeight) : null, rd._useMipMap, rd._anisotropicLevel);
  730. rd._cacheNode = res.node;
  731. rd._cacheTexture = res.texture;
  732. if (rd._cacheRenderSprite) {
  733. rd._cacheRenderSprite.dispose();
  734. }
  735. rd._cacheRenderSprite = res.sprite;
  736. sizeChanged = true;
  737. }
  738. if (sizeChanged) {
  739. rd._cacheSize.copyFrom(Group2D._s);
  740. rd._cacheNodeUVs = rd._cacheNode.getUVsForCustomSize(rd._cacheSize);
  741. if (rd._cacheNodeUVsChangedObservable && rd._cacheNodeUVsChangedObservable.hasObservers()) {
  742. rd._cacheNodeUVsChangedObservable.notifyObservers(rd._cacheNodeUVs);
  743. }
  744. this._setFlags(SmartPropertyPrim.flagWorldCacheChanged);
  745. }
  746. let n = rd._cacheNode;
  747. rd._cacheTexture.bindTextureForPosSize(n.pos, Group2D._s, true);
  748. }
  749. private _unbindCacheTarget() {
  750. if (this._renderableData._cacheTexture) {
  751. this._renderableData._cacheTexture.unbindTexture();
  752. }
  753. }
  754. protected _spreadActualScaleDirty() {
  755. if (this._renderableData && this._renderableData._cacheRenderSprite) {
  756. this.handleGroupChanged(Prim2DBase.actualScaleProperty);
  757. }
  758. super._spreadActualScaleDirty();
  759. }
  760. protected static _unS = new Vector2(1, 1);
  761. protected handleGroupChanged(prop: Prim2DPropInfo) {
  762. // This method is only for cachedGroup
  763. let rd = this._renderableData;
  764. if (!rd) {
  765. return;
  766. }
  767. let cachedSprite = rd._cacheRenderSprite;
  768. if (!this.isCachedGroup || !cachedSprite) {
  769. return;
  770. }
  771. // For now we only support these property changes
  772. // TODO: add more! :)
  773. switch (prop.id) {
  774. case Prim2DBase.actualPositionProperty.id:
  775. cachedSprite.actualPosition = this.actualPosition.clone();
  776. if (cachedSprite.position != null) {
  777. cachedSprite.position = cachedSprite.actualPosition.clone();
  778. }
  779. break;
  780. case Prim2DBase.rotationProperty.id:
  781. cachedSprite.rotation = this.rotation;
  782. break;
  783. case Prim2DBase.scaleProperty.id:
  784. cachedSprite.scale = this.scale;
  785. break;
  786. case Prim2DBase.originProperty.id:
  787. cachedSprite.origin = this.origin.clone();
  788. break;
  789. case Group2D.actualSizeProperty.id:
  790. cachedSprite.size = this.actualSize.clone();
  791. break;
  792. }
  793. }
  794. private detectGroupStates() {
  795. var isCanvas = this instanceof Canvas2D;
  796. var canvasStrat = this.owner.cachingStrategy;
  797. // In Don't Cache mode, only the canvas is renderable, all the other groups are logical. There are not a single cached group.
  798. if (canvasStrat === Canvas2D.CACHESTRATEGY_DONTCACHE) {
  799. this._isRenderableGroup = isCanvas;
  800. this._isCachedGroup = false;
  801. }
  802. // In Canvas cached only mode, only the Canvas is cached and renderable, all other groups are logicals
  803. else if (canvasStrat === Canvas2D.CACHESTRATEGY_CANVAS) {
  804. if (isCanvas) {
  805. this._isRenderableGroup = true;
  806. this._isCachedGroup = true;
  807. } else {
  808. this._isRenderableGroup = this.id === "__cachedCanvasGroup__";
  809. this._isCachedGroup = false;
  810. }
  811. }
  812. // Top Level Groups cached only mode, the canvas is a renderable/not cached, its direct Groups are cached/renderable, all other group are logicals
  813. else if (canvasStrat === Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) {
  814. if (isCanvas) {
  815. this._isRenderableGroup = true;
  816. this._isCachedGroup = false;
  817. } else {
  818. if (this.hierarchyDepth === 1) {
  819. this._isRenderableGroup = true;
  820. this._isCachedGroup = true;
  821. } else {
  822. this._isRenderableGroup = false;
  823. this._isCachedGroup = false;
  824. }
  825. }
  826. }
  827. // All Group cached mode, all groups are renderable/cached, including the Canvas, groups with the behavior DONTCACHE are renderable/not cached, groups with CACHEINPARENT are logical ones
  828. else if (canvasStrat === Canvas2D.CACHESTRATEGY_ALLGROUPS) {
  829. if (isCanvas) {
  830. this._isRenderableGroup = true;
  831. this._isCachedGroup = false;
  832. } else {
  833. var gcb = this.cacheBehavior & Group2D.GROUPCACHEBEHAVIOR_OPTIONMASK;
  834. if ((gcb === Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE) || (gcb === Group2D.GROUPCACHEBEHAVIOR_CACHEINPARENTGROUP)) {
  835. this._isRenderableGroup = gcb === Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE;
  836. this._isCachedGroup = false;
  837. }
  838. if (gcb === Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY) {
  839. this._isRenderableGroup = true;
  840. this._isCachedGroup = true;
  841. }
  842. }
  843. }
  844. if (this._isRenderableGroup) {
  845. // Yes, we do need that check, trust me, unfortunately we can call _detectGroupStates many time on the same object...
  846. if (!this._renderableData) {
  847. this._renderableData = new RenderableGroupData();
  848. }
  849. }
  850. // If the group is tagged as renderable we add it to the renderable tree
  851. if (this._isCachedGroup) {
  852. this._renderableData._noResizeOnScale = (this.cacheBehavior & Group2D.GROUPCACHEBEHAVIOR_NORESIZEONSCALE) !== 0;
  853. let cur = this.parent;
  854. while (cur) {
  855. if (cur instanceof Group2D && cur._isRenderableGroup) {
  856. if (cur._renderableData._childrenRenderableGroups.indexOf(this) === -1) {
  857. cur._renderableData._childrenRenderableGroups.push(this);
  858. }
  859. break;
  860. }
  861. cur = cur.parent;
  862. }
  863. }
  864. }
  865. public get _cachedTexture(): MapTexture {
  866. if (this._renderableData) {
  867. return this._renderableData._cacheTexture;
  868. }
  869. return null;
  870. }
  871. private _trackedNode: Node;
  872. protected _isRenderableGroup: boolean;
  873. protected _isCachedGroup: boolean;
  874. private _cacheGroupDirty: boolean;
  875. private _cacheBehavior: number;
  876. private _viewportPosition: Vector2;
  877. private _viewportSize: Size;
  878. public _renderableData: RenderableGroupData;
  879. }
  880. export class RenderableGroupData {
  881. constructor() {
  882. this._primDirtyList = new Array<Prim2DBase>();
  883. this._primNewDirtyList = new Array<Prim2DBase>();
  884. this._childrenRenderableGroups = new Array<Group2D>();
  885. this._renderGroupInstancesInfo = new StringDictionary<GroupInstanceInfo>();
  886. this._transparentPrimitives = new Array<TransparentPrimitiveInfo>();
  887. this._transparentSegments = new Array<TransparentSegment>();
  888. this._transparentListChanged = false;
  889. this._cacheNode = null;
  890. this._cacheTexture = null;
  891. this._cacheRenderSprite = null;
  892. this._renderingScale = 1;
  893. this._cacheNodeUVs = null;
  894. this._cacheNodeUVsChangedObservable = null;
  895. this._cacheSize = Size.Zero();
  896. this._useMipMap = false;
  897. this._anisotropicLevel = 1;
  898. this._noResizeOnScale = false;
  899. }
  900. dispose(owner: Canvas2D) {
  901. let engine = owner.engine;
  902. if (this._cacheRenderSprite) {
  903. this._cacheRenderSprite.dispose();
  904. this._cacheRenderSprite = null;
  905. }
  906. if (this._cacheTexture && this._cacheNode) {
  907. this._cacheTexture.freeRect(this._cacheNode);
  908. this._cacheTexture = null;
  909. this._cacheNode = null;
  910. }
  911. if (this._primDirtyList) {
  912. this._primDirtyList.splice(0);
  913. this._primDirtyList = null;
  914. }
  915. if (this._renderGroupInstancesInfo) {
  916. this._renderGroupInstancesInfo.forEach((k, v) => {
  917. v.dispose();
  918. });
  919. this._renderGroupInstancesInfo = null;
  920. }
  921. if (this._cacheNodeUVsChangedObservable) {
  922. this._cacheNodeUVsChangedObservable.clear();
  923. this._cacheNodeUVsChangedObservable = null;
  924. }
  925. if (this._transparentSegments) {
  926. for (let ts of this._transparentSegments) {
  927. ts.dispose(engine);
  928. }
  929. this._transparentSegments.splice(0);
  930. this._transparentSegments = null;
  931. }
  932. }
  933. addNewTransparentPrimitiveInfo(prim: RenderablePrim2D, gii: GroupInstanceInfo): TransparentPrimitiveInfo {
  934. let tpi = new TransparentPrimitiveInfo();
  935. tpi._primitive = prim;
  936. tpi._groupInstanceInfo = gii;
  937. tpi._transparentSegment = null;
  938. this._transparentPrimitives.push(tpi);
  939. this._transparentListChanged = true;
  940. return tpi;
  941. }
  942. removeTransparentPrimitiveInfo(tpi: TransparentPrimitiveInfo) {
  943. let index = this._transparentPrimitives.indexOf(tpi);
  944. if (index !== -1) {
  945. this._transparentPrimitives.splice(index, 1);
  946. this._transparentListChanged = true;
  947. }
  948. }
  949. transparentPrimitiveZChanged(tpi: TransparentPrimitiveInfo) {
  950. this._transparentListChanged = true;
  951. //this.updateSmallestZChangedPrim(tpi);
  952. }
  953. _primDirtyList: Array<Prim2DBase>;
  954. _primNewDirtyList: Array<Prim2DBase>;
  955. _childrenRenderableGroups: Array<Group2D>;
  956. _renderGroupInstancesInfo: StringDictionary<GroupInstanceInfo>;
  957. _cacheNode: PackedRect;
  958. _cacheTexture: MapTexture;
  959. _cacheRenderSprite: Sprite2D;
  960. _cacheNodeUVs: Vector2[];
  961. _cacheNodeUVsChangedObservable: Observable<Vector2[]>;
  962. _cacheSize: Size;
  963. _useMipMap: boolean;
  964. _anisotropicLevel: number;
  965. _noResizeOnScale: boolean;
  966. _transparentListChanged: boolean;
  967. _transparentPrimitives: Array<TransparentPrimitiveInfo>;
  968. _transparentSegments: Array<TransparentSegment>;
  969. _renderingScale: number;
  970. }
  971. export class TransparentPrimitiveInfo {
  972. _primitive: RenderablePrim2D;
  973. _groupInstanceInfo: GroupInstanceInfo;
  974. _transparentSegment: TransparentSegment;
  975. }
  976. }