diff --git a/assets/lcc-ui-sorting-group/engine-extend/node.jsb.ts b/assets/lcc-ui-sorting-group/engine-extend/node.jsb.ts new file mode 100644 index 0000000..e64cd80 --- /dev/null +++ b/assets/lcc-ui-sorting-group/engine-extend/node.jsb.ts @@ -0,0 +1,71 @@ +import { director, Layers, misc, utils } from "cc"; +import { MeshRenderer } from "cc"; +import { profiler } from "cc"; +import { CCObject } from "cc"; +import { game } from "cc"; +import { Material } from "cc"; +import { Profiler } from "cc"; +import { Node } from "cc"; +import { JSB } from "cc/env"; + +const _constants = { + fontSize: 23, + quadHeight: 0.4, + segmentsPerLine: 8, + textureWidth: 256, + textureHeight: 256, +}; + +if(JSB){ + //@ts-ignore + const Node_ctor = Node.prototype._ctor; + //@ts-ignore + Node.prototype._ctor = function (name?: string) { + Node_ctor.call(this, name); + + const sharedArrayBuffer = this._getSharedArrayBufferObject(); + // Uint32Array with 3 elements: eventMask, layer, dirtyFlags + this._sharedUint32Arr = new Uint32Array(sharedArrayBuffer, 0, 3); + // Int32Array with 1 element: siblingIndex + this._sharedInt32Arr = new Int32Array(sharedArrayBuffer, 12, 1); + // uiSortingPriority + this._sharedFloatArr = new Float32Array(sharedArrayBuffer, 16, 1); + // Uint8Array with 3 elements: activeInHierarchy, active, static, uiSortingEnabled + this._sharedUint8Arr = new Uint8Array(sharedArrayBuffer, 20, 4); + + this._sharedUint32Arr[1] = Layers.Enum.DEFAULT; // this._sharedUint32Arr[1] is layer + }; + + Object.defineProperty(Node.prototype, 'uiSortingEnabled', { + configurable: true, + enumerable: true, + get (): Readonly { + return this._sharedUint8Arr[3] != 0; // Uint8, 1: active + }, + set (v) { + this._sharedUint8Arr[3] = (v ? 1 : 0); // Uint8, 1: active + }, + }); + + Object.defineProperty(Node.prototype, 'uiSortingPriority', { + configurable: true, + enumerable: true, + get (): Readonly { + return this._sharedFloatArr[0]; + }, + set (v) { + this._sharedFloatArr[0] = v; + }, + }); + + // 左下角调试节点创建过早,重新创建 + //@ts-ignore + if (profiler._rootNode && profiler._rootNode.isValid){ + //@ts-ignore + profiler._rootNode.destroy(); + //@ts-ignore + profiler._rootNode = null; + + profiler.generateNode(); + } +} diff --git a/assets/lcc-ui-sorting-group/engine-extend/render-entity.ts.meta b/assets/lcc-ui-sorting-group/engine-extend/node.jsb.ts.meta similarity index 70% rename from assets/lcc-ui-sorting-group/engine-extend/render-entity.ts.meta rename to assets/lcc-ui-sorting-group/engine-extend/node.jsb.ts.meta index bed1518..6e9ed74 100644 --- a/assets/lcc-ui-sorting-group/engine-extend/render-entity.ts.meta +++ b/assets/lcc-ui-sorting-group/engine-extend/node.jsb.ts.meta @@ -2,7 +2,7 @@ "ver": "4.0.23", "importer": "typescript", "imported": true, - "uuid": "fa9757e8-0812-47bb-b2cd-658d631f676a", + "uuid": "29c6a480-4d9e-413e-8a39-0f0f9a59a19c", "files": [], "subMetas": {}, "userData": {} diff --git a/assets/lcc-ui-sorting-group/engine-extend/render-entity.ts b/assets/lcc-ui-sorting-group/engine-extend/render-entity.ts deleted file mode 100644 index c7010d6..0000000 --- a/assets/lcc-ui-sorting-group/engine-extend/render-entity.ts +++ /dev/null @@ -1,141 +0,0 @@ - -/** - * 由于RenderEntity未暴露,所以修改就只有另辟蹊径 - */ - -import { Graphics } from "cc"; -import { UIRenderer } from "cc"; -import { TiledLayer } from "cc"; -import { UIMeshRenderer } from "cc"; -import { __private,dragonBones,sp } from "cc"; -import { JSB } from "cc/env"; -import { DEFAULT_SORTING_PRIORITY } from "../sorting-define"; - -export enum RenderEntityFloatSharedBufferView { - localOpacity, - sortingPriority, - count, -} - -export enum RenderEntityUInt8SharedBufferView { - colorR, - colorG, - colorB, - colorA, - maskMode, - count, -} - -export enum RenderEntityBoolSharedBufferView { - colorDirty, - enabled, - useLocal, - count, -} - -/** - * RenderEntity类 - */ -let RenderEntityClass:typeof __private._cocos_2d_renderer_render_entity__RenderEntity = null; - -/** - * 更新RenderEntity实体 - */ -export function UpdateRenderEntity(renderEntity:__private._cocos_2d_renderer_render_entity__RenderEntity){ - if(renderEntity && !RenderEntityClass){ - RenderEntityClass = renderEntity.constructor as typeof __private._cocos_2d_renderer_render_entity__RenderEntity; - - // @ts-ignore - RenderEntityClass.prototype.initSharedBuffer = function(){ - this._sortingPriority = DEFAULT_SORTING_PRIORITY; - if (JSB) { - //this._sharedBuffer = new Float32Array(RenderEntitySharedBufferView.count); - const buffer = this._nativeObj.getEntitySharedBufferForJS(); - let offset = 0; - this._floatSharedBuffer = new Float32Array(buffer, offset, RenderEntityFloatSharedBufferView.count); - offset += RenderEntityFloatSharedBufferView.count * 4; - this._uint8SharedBuffer = new Uint8Array(buffer, offset, RenderEntityUInt8SharedBufferView.count); - offset += RenderEntityUInt8SharedBufferView.count * 1; - this._boolSharedBuffer = new Uint8Array(buffer, offset, RenderEntityBoolSharedBufferView.count); - } - }; - - if(!('sortingPriority' in RenderEntityClass.prototype)){ - Object.defineProperty(RenderEntityClass.prototype, 'sortingPriority', { - get: function() { - return this._sortingPriority; - }, - set: function(value) { - this._sortingPriority = value; - // console.log(`JSB sortingPriority ${value}`); - if (JSB) { - this._floatSharedBuffer[RenderEntityFloatSharedBufferView.sortingPriority] = value; - } - }, - enumerable: true - }); - } - - // @ts-ignore - renderEntity.initSharedBuffer(); - - console.log('Update RenderEntity Class'); - } -} - -// @ts-ignore -const Graphics_createRenderEntity = Graphics.prototype.createRenderEntity; -// @ts-ignore -Graphics.prototype.createRenderEntity = function(){ - let entity = Graphics_createRenderEntity.call(this); - UpdateRenderEntity(entity); - return entity; -} - -const UIMeshRenderer_onLoad = UIMeshRenderer.prototype.onLoad; -UIMeshRenderer.prototype.onLoad = function(){ - UpdateRenderEntity(this._renderEntity); - return UIMeshRenderer_onLoad.call(this); -} - -// @ts-ignore -const UIRenderer_createRenderEntity = UIRenderer.prototype.createRenderEntity; -// @ts-ignore -UIRenderer.prototype.createRenderEntity = function(){ - let entity = UIRenderer_createRenderEntity.call(this); - UpdateRenderEntity(entity); - return entity; -} - -if(dragonBones){ - // @ts-ignore - const ArmatureDisplay_createRenderEntity = dragonBones.ArmatureDisplay.prototype.createRenderEntity; - // @ts-ignore - dragonBones.ArmatureDisplay.prototype.createRenderEntity = function(){ - let entity = ArmatureDisplay_createRenderEntity.call(this); - UpdateRenderEntity(entity); - return entity; - } -} - -if(sp){ - // @ts-ignore - const Skeleton_createRenderEntity = sp.Skeleton.prototype.createRenderEntity; - // @ts-ignore - sp.Skeleton.prototype.createRenderEntity = function(){ - let entity = Skeleton_createRenderEntity.call(this); - UpdateRenderEntity(entity); - return entity; - } -} - -if(TiledLayer){ - // @ts-ignore - const TiledLayer_createRenderEntity = TiledLayer.prototype.createRenderEntity; - // @ts-ignore - TiledLayer.prototype.createRenderEntity = function(){ - let entity = TiledLayer_createRenderEntity.call(this); - UpdateRenderEntity(entity); - return entity; - } -} diff --git a/assets/lcc-ui-sorting-group/engine-extend/ui-renderer.ts b/assets/lcc-ui-sorting-group/engine-extend/ui-renderer.ts index a6796aa..0dddb21 100644 --- a/assets/lcc-ui-sorting-group/engine-extend/ui-renderer.ts +++ b/assets/lcc-ui-sorting-group/engine-extend/ui-renderer.ts @@ -4,31 +4,13 @@ declare module 'cc' { interface UIRenderer { /** - * 排序优先级 - private + * 渲染优先级 */ - _sortingPriority:number; + renderPriority:number; /** - * 排序优先级 + * 渲染透明度 */ - sortingPriority:number; - - /** - * 排序透明度 - */ - sortingOpacity:number; + renderOpacity:number; } } - -if(!('sortingPriority' in UIRenderer.prototype)){ - Object.defineProperty(UIRenderer.prototype, 'sortingPriority', { - get: function() { - return this._sortingPriority; - }, - set: function(value) { - this._sortingPriority = value; - this._renderEntity.sortingPriority = value; - }, - enumerable: true - }); -} diff --git a/assets/lcc-ui-sorting-group/engine-extend/ui-transform.ts b/assets/lcc-ui-sorting-group/engine-extend/ui-transform.ts new file mode 100644 index 0000000..cb460dc --- /dev/null +++ b/assets/lcc-ui-sorting-group/engine-extend/ui-transform.ts @@ -0,0 +1,55 @@ +import { UITransform } from "cc"; +import { JSB } from "cc/env"; + +declare module 'cc' { + interface UITransform { + + /** + * 排序优先级 - private + */ + _sortingPriority:number; + + /** + * 排序优先级 + */ + sortingPriority:number; + + /** + * 排序优使能 - private + */ + _sortingEnabled:boolean; + + /** + * 排序优使能 + */ + sortingEnabled:boolean; + } +} + +if(!('sortingPriority' in UITransform.prototype)){ + Object.defineProperty(UITransform.prototype, 'sortingPriority', { + get: function() { + return this._sortingPriority; + }, + set: function(value) { + this._sortingPriority = value; + if(JSB){ + this.node.uiSortingPriority = value; + } + }, + enumerable: true + }); + + Object.defineProperty(UITransform.prototype, 'sortingEnabled', { + get: function() { + return this._sortingEnabled; + }, + set: function(value) { + this._sortingEnabled = value; + if(JSB){ + this.node.uiSortingEnabled = value; + } + }, + enumerable: true + }); +} diff --git a/assets/lcc-ui-sorting-group/engine-extend/ui-transform.ts.meta b/assets/lcc-ui-sorting-group/engine-extend/ui-transform.ts.meta new file mode 100644 index 0000000..0d9109f --- /dev/null +++ b/assets/lcc-ui-sorting-group/engine-extend/ui-transform.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "421d8e4a-74d5-4851-a739-de22c13f87e3", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/lcc-ui-sorting-group/engine-extend/ui.ts b/assets/lcc-ui-sorting-group/engine-extend/ui.ts index 9eca69b..593a87c 100644 --- a/assets/lcc-ui-sorting-group/engine-extend/ui.ts +++ b/assets/lcc-ui-sorting-group/engine-extend/ui.ts @@ -1,6 +1,5 @@ import { clamp, gfx,Node,RenderData,UI,StencilManager,UIRenderer, renderer } from 'cc'; import { JSB } from 'cc/env'; -import { DEFAULT_SORTING_PRIORITY } from '../sorting-define'; declare module 'cc' { interface UI { @@ -64,14 +63,14 @@ UI.prototype.flushRendererCache = function(){ const rendererCache = this.rendererCache; if(rendererCache.length > 0){ if(this.rendererOrder){ - rendererCache.sort((a, b)=>{ return a._sortingPriority - b._sortingPriority; }); + rendererCache.sort((a, b)=>{ return a.renderPriority - b.renderPriority; }); } // console.log(`flushRendererCache ${rendererCache.length}`); for(let render of rendererCache){ // console.log(`${render.node.name} render hash ${render.renderData.dataHash}`); render.fillBuffers(this); - if(render.sortingOpacity >= 0){ - updateOpacity(render.renderData, render.sortingOpacity); + if(render.renderOpacity >= 0){ + updateOpacity(render.renderData, render.renderOpacity); const buffer = render.renderData.getMeshBuffer(); if (buffer) { buffer.setDirty(); @@ -126,7 +125,7 @@ UI.prototype.update = function() { } } -UI.prototype.walk = function(node: Node, level = 0){ +UI.prototype.walk = function(node: Node, level = 0, sortingPriority = 0){ if (!node.activeInHierarchy) { return; } @@ -134,6 +133,8 @@ UI.prototype.walk = function(node: Node, level = 0){ const uiProps = node._uiProps; const render = uiProps.uiComp as UIRenderer; const stencilEnterLevel = render && (render.stencilStage === _cocos_2d_renderer_stencil_manager__Stage.ENTER_LEVEL || render.stencilStage === _cocos_2d_renderer_stencil_manager__Stage.ENTER_LEVEL_INVERTED); + const transform = uiProps.uiTransformComp; + sortingPriority = (transform && transform._sortingEnabled) ? transform._sortingPriority : sortingPriority; // Save opacity const parentOpacity = this._pOpacity; @@ -167,14 +168,14 @@ UI.prototype.walk = function(node: Node, level = 0){ } }else{ this.rendererCache.push(render); - render._sortingPriority = render._sortingPriority ?? DEFAULT_SORTING_PRIORITY; - if(render._sortingPriority != DEFAULT_SORTING_PRIORITY){ + render.renderPriority = sortingPriority; + if(sortingPriority != 0){ this.rendererOrder = true; } if (this._opacityDirty && render && !render.useVertexOpacity && render.renderData && render.renderData.vertexCount > 0) { - render.sortingOpacity = opacity; + render.renderOpacity = opacity; }else{ - render.sortingOpacity = -1; + render.renderOpacity = -1; } } } @@ -182,7 +183,7 @@ UI.prototype.walk = function(node: Node, level = 0){ if (children.length > 0 && !node._static) { for (let i = 0; i < children.length; ++i) { const child = children[i]; - this.walk(child, level); + this.walk(child, level, sortingPriority); } } diff --git a/assets/lcc-ui-sorting-group/sorting-define.ts b/assets/lcc-ui-sorting-group/sorting-define.ts index 36cd199..76db3b7 100644 --- a/assets/lcc-ui-sorting-group/sorting-define.ts +++ b/assets/lcc-ui-sorting-group/sorting-define.ts @@ -7,7 +7,7 @@ export enum SortingLayer { //-- 自定义,在此之上,小于 DEFAULT 的层级 /** - * 默认层级,在没有应用排序的UI渲染上的默认层级 + * 默认层级,在没有应用排序的UI渲染上的默认层级 *不要修改此枚举* */ DEFAULT = 0, @@ -21,8 +21,3 @@ export enum SortingLayer { * 在层级中最大排序值 */ export const ORDER_IN_LAYER_MAX = 100000; - -/** - * 默认排序优先级 - */ -export const DEFAULT_SORTING_PRIORITY = SortingLayer.DEFAULT * ORDER_IN_LAYER_MAX; diff --git a/assets/lcc-ui-sorting-group/sorting-group.ts b/assets/lcc-ui-sorting-group/sorting-group.ts index efa5be9..3d2a689 100644 --- a/assets/lcc-ui-sorting-group/sorting-group.ts +++ b/assets/lcc-ui-sorting-group/sorting-group.ts @@ -1,10 +1,10 @@ -import { _decorator, Component, Node, ccenum, CCInteger, CCFloat, Enum, director, UI, UIRenderer } from 'cc'; -import { DEFAULT_SORTING_PRIORITY, ORDER_IN_LAYER_MAX, SortingLayer } from './sorting-define'; +import { _decorator, Component, Node, ccenum, CCInteger, CCFloat, Enum, director, UI, UIRenderer, UITransform } from 'cc'; +import { ORDER_IN_LAYER_MAX, SortingLayer } from './sorting-define'; const { ccclass, property, type, disallowMultiple, requireComponent, executeInEditMode } = _decorator; @ccclass('lcc-ui/SortingGroup') -@requireComponent(UIRenderer) +@requireComponent(UITransform) @disallowMultiple(true) @executeInEditMode(true) export class SortingGroup extends Component { @@ -20,20 +20,19 @@ export class SortingGroup extends Component { @property({ type:CCFloat, min: 0, max : ORDER_IN_LAYER_MAX }) orderInLayer:number = 0; - /** - * UI渲染器 - */ - private _uiRenderer:UIRenderer = null; + private _uiTransform:UITransform = null; onLoad(){ - this._uiRenderer = this.getComponent(UIRenderer); + this._uiTransform = this.getComponent(UITransform); } onEnable(){ - this._uiRenderer.sortingPriority = Math.sign(this.sortingLayer) * (Math.abs(this.sortingLayer) * ORDER_IN_LAYER_MAX + this.orderInLayer); + this._uiTransform.sortingPriority = Math.sign(this.sortingLayer) * (Math.abs(this.sortingLayer) * ORDER_IN_LAYER_MAX + this.orderInLayer); + this._uiTransform.sortingEnabled = true; } onDisable(){ - this._uiRenderer.sortingPriority = DEFAULT_SORTING_PRIORITY; + this._uiTransform.sortingPriority = 0; + this._uiTransform.sortingEnabled = false; } } diff --git a/assets/test/scenes/test-scene-sorting.scene b/assets/test/scenes/test-scene-sorting.scene index 3ac39b4..a9a582b 100644 --- a/assets/test/scenes/test-scene-sorting.scene +++ b/assets/test/scenes/test-scene-sorting.scene @@ -27,11 +27,11 @@ "_active": true, "_components": [], "_prefab": { - "__id__": 42 + "__id__": 57 }, "autoReleaseAssets": false, "_globals": { - "__id__": 43 + "__id__": 58 }, "_id": "b977450f-1cd5-49fa-a8db-db655012dcf5" }, @@ -227,27 +227,33 @@ }, { "__id__": 25 + }, + { + "__id__": 38 + }, + { + "__id__": 50 } ], "_active": true, "_components": [ { - "__id__": 38 + "__id__": 53 }, { - "__id__": 39 + "__id__": 54 }, { - "__id__": 40 + "__id__": 55 }, { - "__id__": 41 + "__id__": 56 } ], "_prefab": null, "_lpos": { "__type__": "cc.Vec3", - "x": 959.9999999999999, + "x": 960.0000000000001, "y": 540, "z": 0 }, @@ -873,6 +879,9 @@ ], "_active": false, "_components": [ + { + "__id__": 36 + }, { "__id__": 37 } @@ -1024,8 +1033,8 @@ "_enabled": true, "__prefab": null, "sortingLayer": 1, - "orderInLayer": 0, - "_id": "efCgxc/lVEPqsH8au8/ebz" + "orderInLayer": 3, + "_id": "b7SyZGN8ZByo48B8plzxZd" }, { "__type__": "cc.Node", @@ -1147,9 +1156,6 @@ }, { "__id__": 35 - }, - { - "__id__": 36 } ], "_prefab": null, @@ -1240,19 +1246,6 @@ "_cacheMode": 0, "_id": "27iorVNlhOCpEZnibntFMV" }, - { - "__type__": "5c8c9BaV+VA1Z3pK+Zsi3Cb", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 33 - }, - "_enabled": true, - "__prefab": null, - "sortingLayer": 1, - "orderInLayer": 0, - "_id": "126CLe+FtLK6NZ5f0v9mHT" - }, { "__type__": "cc.UITransform", "_name": "", @@ -1274,6 +1267,529 @@ }, "_id": "c0+Znh+wNM+4H6zrz3BEP5" }, + { + "__type__": "5c8c9BaV+VA1Z3pK+Zsi3Cb", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 25 + }, + "_enabled": true, + "__prefab": null, + "sortingLayer": 1, + "orderInLayer": 2, + "_id": "0fSX301tNL3ID+MhrxV/d7" + }, + { + "__type__": "cc.Node", + "_name": "Node-001", + "_objFlags": 0, + "_parent": { + "__id__": 7 + }, + "_children": [ + { + "__id__": 39 + }, + { + "__id__": 42 + }, + { + "__id__": 45 + } + ], + "_active": false, + "_components": [ + { + "__id__": 48 + }, + { + "__id__": 49 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": -218.293, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_layer": 33554432, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "5aZiXEa0JHkqYnTDtoDhPZ" + }, + { + "__type__": "cc.Node", + "_name": "Label", + "_objFlags": 0, + "_parent": { + "__id__": 38 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 40 + }, + { + "__id__": 41 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 86.86400000000003, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_layer": 33554432, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "fcd9mrQURJl70Yh4NjZFfk" + }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 39 + }, + "_enabled": true, + "__prefab": null, + "_contentSize": { + "__type__": "cc.Size", + "width": 318.81, + "height": 50.4 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "b4U1x/26ZMYorC7jryRepx" + }, + { + "__type__": "cc.Label", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 39 + }, + "_enabled": true, + "__prefab": null, + "_customMaterial": null, + "_srcBlendFactor": 2, + "_dstBlendFactor": 4, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_string": "label111111111111", + "_horizontalAlign": 1, + "_verticalAlign": 1, + "_actualFontSize": 40, + "_fontSize": 40, + "_fontFamily": "Arial", + "_lineHeight": 40, + "_overflow": 0, + "_enableWrapText": true, + "_font": null, + "_isSystemFontUsed": true, + "_spacingX": 0, + "_isItalic": false, + "_isBold": false, + "_isUnderline": false, + "_underlineHeight": 2, + "_cacheMode": 0, + "_id": "e78f0Gaa1BVbyP/NEimc2D" + }, + { + "__type__": "cc.Node", + "_name": "SpriteSplash", + "_objFlags": 0, + "_parent": { + "__id__": 38 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 43 + }, + { + "__id__": 44 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": 4.493000000000052, + "y": 7.488500000000045, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_layer": 33554432, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "e9YhmcmeJOWpNBcf0S6ISS" + }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 42 + }, + "_enabled": true, + "__prefab": null, + "_contentSize": { + "__type__": "cc.Size", + "width": 654.13, + "height": 378.563 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "4cBrueDfBClLeJCqVLHVjF" + }, + { + "__type__": "cc.Sprite", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 42 + }, + "_enabled": true, + "__prefab": null, + "_customMaterial": null, + "_srcBlendFactor": 2, + "_dstBlendFactor": 4, + "_color": { + "__type__": "cc.Color", + "r": 122, + "g": 255, + "b": 0, + "a": 136 + }, + "_spriteFrame": { + "__uuid__": "7d8f9b89-4fd1-4c9f-a3ab-38ec7cded7ca@f9941", + "__expectedType__": "cc.SpriteFrame" + }, + "_type": 0, + "_fillType": 0, + "_sizeMode": 0, + "_fillCenter": { + "__type__": "cc.Vec2", + "x": 0, + "y": 0 + }, + "_fillStart": 0, + "_fillRange": 0, + "_isTrimmedMode": true, + "_useGrayscale": false, + "_atlas": null, + "_id": "5fDWm0jBRKnJQw/0J3YViz" + }, + { + "__type__": "cc.Node", + "_name": "Label-001", + "_objFlags": 0, + "_parent": { + "__id__": 38 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 46 + }, + { + "__id__": 47 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": -47.92500000000001, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_layer": 33554432, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "d54BCitp5CbbB0vOFgIGbA" + }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 45 + }, + "_enabled": true, + "__prefab": null, + "_contentSize": { + "__type__": "cc.Size", + "width": 329.22, + "height": 50.4 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "77RtH07pZMGoZCo4eGSEh+" + }, + { + "__type__": "cc.Label", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 45 + }, + "_enabled": true, + "__prefab": null, + "_customMaterial": null, + "_srcBlendFactor": 2, + "_dstBlendFactor": 4, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_string": "label22222222222", + "_horizontalAlign": 1, + "_verticalAlign": 1, + "_actualFontSize": 40, + "_fontSize": 40, + "_fontFamily": "Arial", + "_lineHeight": 40, + "_overflow": 0, + "_enableWrapText": true, + "_font": null, + "_isSystemFontUsed": true, + "_spacingX": 0, + "_isItalic": false, + "_isBold": false, + "_isUnderline": false, + "_underlineHeight": 2, + "_cacheMode": 0, + "_id": "b295f53fRNOoD+5khP4lUS" + }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 38 + }, + "_enabled": true, + "__prefab": null, + "_contentSize": { + "__type__": "cc.Size", + "width": 100, + "height": 100 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "f3nDcnVYdN6pZUfDtW0fDt" + }, + { + "__type__": "5c8c9BaV+VA1Z3pK+Zsi3Cb", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 38 + }, + "_enabled": true, + "__prefab": null, + "sortingLayer": 1, + "orderInLayer": 1, + "_id": "e8pXnnkRRPfo50O0CR7SRu" + }, + { + "__type__": "cc.Node", + "_name": "SpriteSplash", + "_objFlags": 0, + "_parent": { + "__id__": 7 + }, + "_children": [], + "_active": false, + "_components": [ + { + "__id__": 51 + }, + { + "__id__": 52 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": -630.484, + "y": -352.824, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_layer": 33554432, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "c0VfwcFp5DzZIfeDWG4MA7" + }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 50 + }, + "_enabled": true, + "__prefab": null, + "_contentSize": { + "__type__": "cc.Size", + "width": 654.13, + "height": 378.563 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "16eXrC15RCR7XXix5wahAm" + }, + { + "__type__": "cc.Sprite", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 50 + }, + "_enabled": true, + "__prefab": null, + "_customMaterial": null, + "_srcBlendFactor": 2, + "_dstBlendFactor": 4, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 0, + "b": 0, + "a": 136 + }, + "_spriteFrame": { + "__uuid__": "7d8f9b89-4fd1-4c9f-a3ab-38ec7cded7ca@f9941", + "__expectedType__": "cc.SpriteFrame" + }, + "_type": 0, + "_fillType": 0, + "_sizeMode": 0, + "_fillCenter": { + "__type__": "cc.Vec2", + "x": 0, + "y": 0 + }, + "_fillStart": 0, + "_fillRange": 0, + "_isTrimmedMode": true, + "_useGrayscale": false, + "_atlas": null, + "_id": "b9Wwwx4phE7oA1FN/IyNcP" + }, { "__type__": "cc.UITransform", "_name": "", @@ -1321,8 +1837,8 @@ "__prefab": null, "_alignFlags": 45, "_target": null, - "_left": -1.1368683772161603e-13, - "_right": -1.1368683772161603e-13, + "_left": 1.1368683772161603e-13, + "_right": 1.1368683772161603e-13, "_top": 0, "_bottom": 0, "_horizontalCenter": 0, @@ -1379,19 +1895,19 @@ { "__type__": "cc.SceneGlobals", "ambient": { - "__id__": 44 + "__id__": 59 }, "shadows": { - "__id__": 45 + "__id__": 60 }, "_skybox": { - "__id__": 46 + "__id__": 61 }, "fog": { - "__id__": 47 + "__id__": 62 }, "octree": { - "__id__": 48 + "__id__": 63 } }, { diff --git a/native/engine/lcc-ui-sorting-group-native/CMakeLists.txt b/native/engine/lcc-ui-sorting-group-native/CMakeLists.txt index 28f62fe..f0fba3d 100644 --- a/native/engine/lcc-ui-sorting-group-native/CMakeLists.txt +++ b/native/engine/lcc-ui-sorting-group-native/CMakeLists.txt @@ -10,10 +10,27 @@ if(NOT DEFINED COCOS_SOURCE_PLUGIN_INCLUDES) endif() # 排除替换源文件 -list(APPEND COCOS_SOURCE_PLUGIN_EXCULDE "${COCOS_X_PATH}/cocos/2d/renderer/Batcher2d.h" "${COCOS_X_PATH}/cocos/2d/renderer/Batcher2d.cpp" "${COCOS_X_PATH}/cocos/2d/renderer/RenderEntity.h" "${COCOS_X_PATH}/cocos/2d/renderer/RenderEntity.cpp") +list(APPEND COCOS_SOURCE_PLUGIN_EXCULDE + "${COCOS_X_PATH}/cocos/2d/renderer/Batcher2d.h" + "${COCOS_X_PATH}/cocos/2d/renderer/Batcher2d.cpp" + "${COCOS_X_PATH}/cocos/2d/renderer/RenderEntity.h" + "${COCOS_X_PATH}/cocos/2d/renderer/RenderEntity.cpp" + "${COCOS_X_PATH}/cocos/core/scene-graph/Node.h" + "${COCOS_X_PATH}/cocos/core/scene-graph/Node.cpp" +) # 添加替换源文件 -list(APPEND COCOS_SOURCE_PLUGIN_LIST "${CMAKE_CURRENT_LIST_DIR}/native/cocos/2d/renderer/Batcher2d.h" "${CMAKE_CURRENT_LIST_DIR}/native/cocos/2d/renderer/Batcher2d.cpp" "${CMAKE_CURRENT_LIST_DIR}/native/cocos/2d/renderer/RenderEntity.h" "${CMAKE_CURRENT_LIST_DIR}/native/cocos/2d/renderer/RenderEntity.cpp") +list(APPEND COCOS_SOURCE_PLUGIN_LIST + "${CMAKE_CURRENT_LIST_DIR}/native/cocos/2d/renderer/Batcher2d.h" + "${CMAKE_CURRENT_LIST_DIR}/native/cocos/2d/renderer/Batcher2d.cpp" + "${CMAKE_CURRENT_LIST_DIR}/native/cocos/2d/renderer/RenderEntity.h" + "${CMAKE_CURRENT_LIST_DIR}/native/cocos/2d/renderer/RenderEntity.cpp" + "${CMAKE_CURRENT_LIST_DIR}/native/cocos/core/scene-graph/Node.h" + "${CMAKE_CURRENT_LIST_DIR}/native/cocos/core/scene-graph/Node.cpp" +) # 添加替换源文件 -list(APPEND COCOS_SOURCE_PLUGIN_INCLUDES "${CMAKE_CURRENT_LIST_DIR}/native" "${CMAKE_CURRENT_LIST_DIR}/native/cocos") +list(APPEND COCOS_SOURCE_PLUGIN_INCLUDES + "${CMAKE_CURRENT_LIST_DIR}/native" + "${CMAKE_CURRENT_LIST_DIR}/native/cocos" +) diff --git a/native/engine/lcc-ui-sorting-group-native/native/cocos/2d/renderer/Batcher2d.cpp b/native/engine/lcc-ui-sorting-group-native/native/cocos/2d/renderer/Batcher2d.cpp index 09b5df3..c757992 100644 --- a/native/engine/lcc-ui-sorting-group-native/native/cocos/2d/renderer/Batcher2d.cpp +++ b/native/engine/lcc-ui-sorting-group-native/native/cocos/2d/renderer/Batcher2d.cpp @@ -102,17 +102,19 @@ void Batcher2d::syncRootNodesToNative(ccstd::vector&& rootNodes) { void Batcher2d::fillBuffersAndMergeBatches() { for (auto* rootNode : _rootNodeArr) { - walk(rootNode, 1); + walk(rootNode, 1, true, 0); // CC_LOG_INFO("-------------- flushRendererCache 1 -------------- %d", _rootNodeArr.size()); flushRendererCache(); // LCC_UI_SORTING_GROUP generateBatch(_currEntity, _currDrawInfo); } } -void Batcher2d::walk(Node* node, float parentOpacity) { // NOLINT(misc-no-recursion) +void Batcher2d::walk(Node* node, float parentOpacity, bool cacheEnable, float sortingPriority) { // NOLINT(misc-no-recursion) if (!node->isActiveInHierarchy()) { return; } + sortingPriority = node->isUISortingEnabled() ? node->getUISortingPriority() : sortingPriority; + bool breakWalk = false; auto* entity = static_cast(node->getUserData()); if (entity) { @@ -126,7 +128,7 @@ void Batcher2d::walk(Node* node, float parentOpacity) { // NOLINT(misc-no-recurs // LCC_UI_SORTING_GROUP if (entity->isEnabled()) { - if(entity->getIsMask()){ + if(entity->getIsMask() || !cacheEnable){ // CC_LOG_INFO("-------------- flushRendererCache 2 --------------"); flushRendererCache(); uint32_t size = entity->getRenderDrawInfosSize(); @@ -137,7 +139,8 @@ void Batcher2d::walk(Node* node, float parentOpacity) { // NOLINT(misc-no-recurs entity->setVBColorDirty(false); }else{ rendererCache.push_back(entity); - if(entity->getSortingPriority() != 0){ + entity->setRenderPriority(sortingPriority); + if(sortingPriority != 0){ rendererOrder = true; } } @@ -153,7 +156,7 @@ void Batcher2d::walk(Node* node, float parentOpacity) { // NOLINT(misc-no-recurs float thisOpacity = entity ? entity->getOpacity() : parentOpacity; for (const auto& child : children) { // we should find parent opacity recursively upwards if it doesn't have an entity. - walk(child, thisOpacity); + walk(child, thisOpacity, cacheEnable, sortingPriority); } } @@ -311,7 +314,7 @@ CC_FORCE_INLINE void Batcher2d::handleMiddlewareDraw(RenderEntity* entity, Rende CC_FORCE_INLINE void Batcher2d::handleSubNode(RenderEntity* entity, RenderDrawInfo* drawInfo) { // NOLINT if (drawInfo->getSubNode()) { - walk(drawInfo->getSubNode(), entity->getOpacity()); + walk(drawInfo->getSubNode(), entity->getOpacity(), false, 0); } } @@ -635,13 +638,13 @@ void Batcher2d::createClearModel() { void Batcher2d::flushRendererCache() { if(rendererCache.size() > 0){ if(rendererOrder){ - std::stable_sort(rendererCache.begin(), rendererCache.end(), [](RenderEntity* a, RenderEntity* b) { return a->getSortingPriority() < b->getSortingPriority(); }); + std::stable_sort(rendererCache.begin(), rendererCache.end(), [](RenderEntity* a, RenderEntity* b) { return a->getRenderPriority() < b->getRenderPriority(); }); } // CC_LOG_INFO("flushRendererCache %d", rendererCache.size()); for(ccstd::vector::iterator it = rendererCache.begin(); it != rendererCache.end(); it++) { RenderEntity* entity = *it; - // CC_LOG_INFO("%f", entity->getSortingPriority()); + // CC_LOG_INFO("%f", entity->getRenderPriority()); uint32_t size = entity->getRenderDrawInfosSize(); for (uint32_t i = 0; i < size; i++) { auto* drawInfo = entity->getRenderDrawInfoAt(i); diff --git a/native/engine/lcc-ui-sorting-group-native/native/cocos/2d/renderer/Batcher2d.h b/native/engine/lcc-ui-sorting-group-native/native/cocos/2d/renderer/Batcher2d.h index 25cf1cd..3af2e0b 100644 --- a/native/engine/lcc-ui-sorting-group-native/native/cocos/2d/renderer/Batcher2d.h +++ b/native/engine/lcc-ui-sorting-group-native/native/cocos/2d/renderer/Batcher2d.h @@ -64,7 +64,7 @@ public: void updateDescriptorSet(); void fillBuffersAndMergeBatches(); - void walk(Node* node, float parentOpacity); + void walk(Node* node, float parentOpacity, bool cacheEnable, float sortingPriority); void handlePostRender(RenderEntity* entity); void handleDrawInfo(RenderEntity* entity, RenderDrawInfo* drawInfo, Node* node); void handleComponentDraw(RenderEntity* entity, RenderDrawInfo* drawInfo, Node* node); diff --git a/native/engine/lcc-ui-sorting-group-native/native/cocos/2d/renderer/RenderEntity.h b/native/engine/lcc-ui-sorting-group-native/native/cocos/2d/renderer/RenderEntity.h index 3df4982..05a5075 100644 --- a/native/engine/lcc-ui-sorting-group-native/native/cocos/2d/renderer/RenderEntity.h +++ b/native/engine/lcc-ui-sorting-group-native/native/cocos/2d/renderer/RenderEntity.h @@ -52,7 +52,6 @@ enum class MaskMode : uint8_t { struct EntityAttrLayout { float localOpacity{1.0F}; - float sortingPriority{0.0F}; // LCC_UI_SORTING_GROUP uint8_t colorR{255}; uint8_t colorG{255}; uint8_t colorB{255}; @@ -126,7 +125,6 @@ public: inline Color getColor() const { return Color(_entityAttrLayout.colorR, _entityAttrLayout.colorG, _entityAttrLayout.colorB, _entityAttrLayout.colorA); } inline float getColorAlpha() const { return static_cast(_entityAttrLayout.colorA) / 255.F; } inline float getLocalOpacity() const { return _entityAttrLayout.localOpacity; } - inline float getSortingPriority() const { return _entityAttrLayout.sortingPriority; } // LCC_UI_SORTING_GROUP inline float getOpacity() const { return _opacity; } inline void setOpacity(float opacity) { _opacity = opacity; } inline bool isEnabled() const { return _entityAttrLayout.enabledIndex != 0; } @@ -137,6 +135,9 @@ public: return _renderEntityType == RenderEntityType::STATIC ? &(_staticDrawInfos[index]) : _dynamicDrawInfos[index]; } + inline float getRenderPriority() const { return _renderPriority; } + inline void setRenderPriority(float value) { _renderPriority = value; } + private: CC_DISALLOW_COPY_MOVE_ASSIGN(RenderEntity); // weak reference @@ -157,5 +158,7 @@ private: RenderEntityType _renderEntityType{RenderEntityType::STATIC}; uint8_t _staticDrawInfoSize{0}; bool _vbColorDirty{true}; + + float _renderPriority{0.0F}; }; } // namespace cc diff --git a/native/engine/lcc-ui-sorting-group-native/native/cocos/core/scene-graph/Node.cpp b/native/engine/lcc-ui-sorting-group-native/native/cocos/core/scene-graph/Node.cpp new file mode 100644 index 0000000..6bf3afe --- /dev/null +++ b/native/engine/lcc-ui-sorting-group-native/native/cocos/core/scene-graph/Node.cpp @@ -0,0 +1,984 @@ +/**************************************************************************** + Copyright (c) 2021 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated engine source code (the "Software"), a limited, + worldwide, royalty-free, non-assignable, revocable and non-exclusive license + to use Cocos Creator solely to develop games on your target platforms. You shall + not use Cocos Creator software for developing other software or tools that's + used for developing games. You are not granted to publish, distribute, + sublicense, and/or sell copies of Cocos Creator. + + The software or tools in this License Agreement are licensed, not sold. + Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ****************************************************************************/ + +#include "core/scene-graph/Node.h" +#include "base/StringUtil.h" +#include "core/data/Object.h" +#include "core/memop/CachedArray.h" +#include "core/platform/Debug.h" +#include "core/scene-graph/NodeEnum.h" +#include "core/scene-graph/Scene.h" +#include "core/utils/IDGenerator.h" +#include "math/Utils.h" + +namespace cc { + +// static variables + +uint32_t Node::clearFrame{0}; +uint32_t Node::clearRound{1000}; +const uint32_t Node::TRANSFORM_ON{1 << 0}; +uint32_t Node::globalFlagChangeVersion{0}; + +namespace { +const ccstd::string EMPTY_NODE_NAME; +IDGenerator idGenerator("Node"); + +ccstd::vector dirtyNodes; +CC_FORCE_INLINE void setDirtyNode(const index_t idx, Node *node) { + if (idx >= dirtyNodes.size()) { + if (idx >= dirtyNodes.capacity()) { + size_t minCapacity = std::max((idx + 1) * 2, 32); + if (minCapacity > dirtyNodes.capacity()) { + dirtyNodes.reserve(minCapacity); // Make a pre-allocated size for dirtyNode vector for better grow performance. + } + } + dirtyNodes.resize(idx + 1, nullptr); + } + dirtyNodes[idx] = node; +} + +CC_FORCE_INLINE Node *getDirtyNode(const index_t idx) { + if (idx < 0 || idx >= dirtyNodes.size()) { + return nullptr; + } + return dirtyNodes[idx]; +} + +} // namespace + +Node::Node() : Node(EMPTY_NODE_NAME) { +} + +Node::Node(const ccstd::string &name) { +#define NODE_SHARED_MEMORY_BYTE_LENGTH (24) + static_assert(offsetof(Node, _uiSortingEnabled) + sizeof(_uiSortingEnabled) - offsetof(Node, _eventMask) == NODE_SHARED_MEMORY_BYTE_LENGTH, "Wrong shared memory size"); + _sharedMemoryActor.initialize(&_eventMask, NODE_SHARED_MEMORY_BYTE_LENGTH); +#undef NODE_SHARED_MEMORY_BYTE_LENGTH + + _id = idGenerator.getNewId(); + if (name.empty()) { + _name.append("New Node"); + } else { + _name = name; + } + _eventProcessor = ccnew NodeEventProcessor(this); +} + +Node::~Node() { + CC_SAFE_DELETE(_eventProcessor); + if (!_children.empty()) { + // Reset children's _parent to nullptr to avoid dangerous pointer + for (const auto &child : _children) { + child->_parent = nullptr; + } + } +} + +void Node::onBatchCreated(bool dontChildPrefab) { + // onBatchCreated was implemented in TS, so code should never go here. + CC_ASSERT(false); + emit(EventTypesToJS::NODE_ON_BATCH_CREATED, dontChildPrefab); +} + +Node *Node::instantiate(Node *cloned, bool isSyncedNode) { + if (!cloned) { + CC_ASSERT(false); + // TODO(): cloned = legacyCC.instantiate._clone(this, this); + return nullptr; + } + // TODO(): + // const newPrefabInfo = cloned._prefab; + // if (EDITOR && newPrefabInfo) { + // if (cloned == = newPrefabInfo.root) { + // // newPrefabInfo.fileId = ''; + // } else { + // // var PrefabUtils = Editor.require('scene://utils/prefab'); + // // PrefabUtils.unlinkPrefab(cloned); + // } + //} + // if (EDITOR && legacyCC.GAME_VIEW) { + // const syncing = newPrefabInfo&& cloned == = newPrefabInfo.root && newPrefabInfo.sync; + // if (!syncing) { + // cloned._name += ' (Clone)'; + // } + //} + cloned->_parent = nullptr; + cloned->onBatchCreated(isSyncedNode); + return cloned; +} + +void Node::onHierarchyChangedBase(Node *oldParent) { // NOLINT(misc-unused-parameters) + Node *newParent = _parent; + auto *scene = dynamic_cast(newParent); + if (isPersistNode() && scene == nullptr) { + emit(EventTypesToJS::NODE_REMOVE_PERSIST_ROOT_NODE); +#if CC_EDITOR + debug::warnID(1623); +#endif + } +#if CC_EDITOR + auto *curScene = getScene(); + const bool inCurrentSceneBefore = oldParent && oldParent->isChildOf(curScene); + const bool inCurrentSceneNow = newParent && newParent->isChildOf(curScene); + if (!inCurrentSceneBefore && inCurrentSceneNow) { + // attached + this->notifyEditorAttached(true); + } else if (inCurrentSceneBefore && !inCurrentSceneNow) { + // detached + this->notifyEditorAttached(false); + } + // conflict detection + // _Scene.DetectConflict.afterAddChild(this); +#endif + + bool shouldActiveNow = isActive() && !!(newParent && newParent->isActiveInHierarchy()); + if (isActiveInHierarchy() != shouldActiveNow) { + // Director::getInstance()->getNodeActivator()->activateNode(this, shouldActiveNow); // TODO(xwx): use TS temporarily + emit(EventTypesToJS::NODE_ACTIVE_NODE, shouldActiveNow); + } +} + +void Node::off(const CallbacksInvoker::KeyType &type, bool useCapture) { + _eventProcessor->offAll(type, useCapture); + bool hasListeners = _eventProcessor->hasEventListener(type); + if (!hasListeners) { + if (type == NodeEventType::TRANSFORM_CHANGED) { + _eventMask &= ~TRANSFORM_ON; + } + } +} + +void Node::off(const CallbacksInvoker::KeyType &type, const CallbackID &cbID, bool useCapture) { + _eventProcessor->off(type, cbID, useCapture); + bool hasListeners = _eventProcessor->hasEventListener(type); + if (!hasListeners) { + if (type == NodeEventType::TRANSFORM_CHANGED) { + _eventMask &= ~TRANSFORM_ON; + } + } +} + +void Node::off(const CallbacksInvoker::KeyType &type, void *target, bool useCapture) { + _eventProcessor->off(type, target, useCapture); + bool hasListeners = _eventProcessor->hasEventListener(type); + if (!hasListeners) { + if (type == NodeEventType::TRANSFORM_CHANGED) { + _eventMask &= ~TRANSFORM_ON; + } + } +} + +//void Node::dispatchEvent(event::Event *eve) { +// _eventProcessor->dispatchEvent(eve); +//} + +bool Node::hasEventListener(const CallbacksInvoker::KeyType &type) const { + return _eventProcessor->hasEventListener(type); +} + +bool Node::hasEventListener(const CallbacksInvoker::KeyType &type, const CallbackID &cbID) const { + return _eventProcessor->hasEventListener(type, cbID); +} +bool Node::hasEventListener(const CallbacksInvoker::KeyType &type, void *target) const { + return _eventProcessor->hasEventListener(type, target); +} +bool Node::hasEventListener(const CallbacksInvoker::KeyType &type, void *target, const CallbackID &cbID) const { + return _eventProcessor->hasEventListener(type, target, cbID); +} + +void Node::targetOff(const CallbacksInvoker::KeyType &type) { + _eventProcessor->targetOff(type); + if ((_eventMask & TRANSFORM_ON) && !_eventProcessor->hasEventListener(NodeEventType::TRANSFORM_CHANGED)) { + _eventMask &= ~TRANSFORM_ON; + } +} + +void Node::setActive(bool isActive) { + uint8_t isActiveU8 = isActive ? 1 : 0; + if (_active != isActiveU8) { + _active = isActiveU8; + Node *parent = _parent; + if (parent) { + bool couldActiveInScene = parent->isActiveInHierarchy(); + if (couldActiveInScene) { + // Director::getInstance()->getNodeActivator()->activateNode(this, isActive); // TODO(xwx): use TS temporarily + emit(EventTypesToJS::NODE_ACTIVE_NODE, isActive); + } + } + } +} + +void Node::setParent(Node *parent, bool isKeepWorld /* = false */) { + if (isKeepWorld) { + updateWorldTransform(); + } + + if (_parent == parent) { + return; + } + + Node *oldParent = _parent; + Node *newParent = parent; +#if CC_DEBUG > 0 + if (oldParent && (oldParent->_objFlags & Flags::DEACTIVATING) == Flags::DEACTIVATING) { + debug::errorID(3821); + } +#endif + _parent = newParent; + _siblingIndex = 0; + onSetParent(oldParent, isKeepWorld); + emit(NodeEventType::PARENT_CHANGED, oldParent); + if (oldParent) { + if (!(oldParent->_objFlags & Flags::DESTROYING)) { + index_t removeAt = getIdxOfChild(oldParent->_children, this); + // TODO(): DEV + /*if (DEV && removeAt < 0) { + errorID(1633); + return; + }*/ + if (removeAt < 0) { + return; + } + oldParent->_children.erase(oldParent->_children.begin() + removeAt); + oldParent->updateSiblingIndex(); + oldParent->emit(NodeEventType::CHILD_REMOVED, this); + } + } + if (newParent) { +#if CC_DEBUG > 0 + if ((newParent->_objFlags & Flags::DEACTIVATING) == Flags::DEACTIVATING) { + debug::errorID(3821); + } +#endif + newParent->_children.emplace_back(this); + _siblingIndex = static_cast(newParent->_children.size() - 1); + newParent->emit(NodeEventType::CHILD_ADDED, this); + } + onHierarchyChanged(oldParent); +} + +void Node::walk(const WalkCallback &preFunc) { + walk(preFunc, nullptr); +} + +void Node::walk(const WalkCallback &preFunc, const WalkCallback &postFunc) { //NOLINT(misc-no-recursion) + if (preFunc) { + preFunc(this); + } + + for (const auto &child : _children) { + if (child) { + child->walk(preFunc, postFunc); + } + } + + if (postFunc) { + postFunc(this); + } +} + +//Component *Node::addComponent(Component *comp) { +// comp->_node = this; // cjh TODO: shared_ptr +// _components.emplace_back(comp); +// +// if (isActiveInHierarchy()) { +// NodeActivator::activateComp(comp); +// } +// +// return comp; +//} +// +//void Node::removeComponent(Component *comp) { +// auto iteComp = std::find(_components.begin(), _components.end(), comp); +// if (iteComp != _components.end()) { +// _components.erase(iteComp); +// } +//} + +bool Node::onPreDestroyBase() { + Flags destroyingFlag = Flags::DESTROYING; + _objFlags |= destroyingFlag; + bool destroyByParent = (!!_parent) && (!!(_parent->_objFlags & destroyingFlag)); +#if CC_EDITOR + if (!destroyByParent) { + this->notifyEditorAttached(false); + } +#endif + if (isPersistNode()) { + emit(EventTypesToJS::NODE_REMOVE_PERSIST_ROOT_NODE); + } + if (!destroyByParent) { + if (_parent) { + emit(NodeEventType::PARENT_CHANGED, this); + index_t childIdx = getIdxOfChild(_parent->_children, this); + if (childIdx != -1) { + _parent->_children.erase(_parent->_children.begin() + childIdx); + } + _siblingIndex = 0; + _parent->updateSiblingIndex(); + _parent->emit(NodeEventType::CHILD_REMOVED, this); + } + } + + //NOTE: The following code is not needed now since we override Node._onPreDestroy in node.jsb.ts + // and the logic will be done in TS. + // emit(NodeEventType::NODE_DESTROYED, this); + // for (const auto &child : _children) { + // child->destroyImmediate(); + // } + // + // emit(EventTypesToJS::NODE_DESTROY_COMPONENTS); + + _eventProcessor->destroy(); + return destroyByParent; +} + +Node *Node::getChildByName(const ccstd::string &name) const { + if (name.empty()) { + CC_LOG_INFO("Invalid name"); + return nullptr; + } + for (const auto &child : _children) { + if (child->_name == name) { + return child; + } + } + return nullptr; +} + +void Node::setScene(Node *node) { + node->updateScene(); +} + +void Node::updateScene() { + if (_parent == nullptr) { + return; + } + _scene = _parent->_scene; + emit(EventTypesToJS::NODE_SCENE_UPDATED, _scene); +} + +/* static */ +index_t Node::getIdxOfChild(const ccstd::vector> &child, Node *target) { + auto iteChild = std::find(child.begin(), child.end(), target); + if (iteChild != child.end()) { + return static_cast(iteChild - child.begin()); + } + return CC_INVALID_INDEX; +} + +Node *Node::getChildByUuid(const ccstd::string &uuid) const { + if (uuid.empty()) { + CC_LOG_INFO("Invalid uuid"); + return nullptr; + } + for (const auto &child : _children) { + if (child->_id == uuid) { + return child; + } + } + return nullptr; +} + +bool Node::isChildOf(Node *parent) const { + const Node *child = this; + do { + if (child == parent) { + return true; + } + child = child->_parent; + } while (child); + return false; +} + +void Node::removeAllChildren() { + for (auto i = static_cast(_children.size() - 1); i >= 0; --i) { + if (_children[i]) { + _children[i]->setParent(nullptr); + } + } + _children.clear(); +} + +void Node::setSiblingIndex(index_t index) { + if (!_parent) { + return; + } + if (!!(_parent->_objFlags & Flags::DEACTIVATING)) { + debug::errorID(3821); + return; + } + ccstd::vector> &siblings = _parent->_children; + index = index != -1 ? index : static_cast(siblings.size()) - 1; + index_t oldIdx = getIdxOfChild(siblings, this); + if (index != oldIdx) { + if (oldIdx != CC_INVALID_INDEX) { + siblings.erase(siblings.begin() + oldIdx); + } + if (index < siblings.size()) { + siblings.insert(siblings.begin() + index, this); + } else { + siblings.emplace_back(this); + } + _parent->updateSiblingIndex(); + if (onSiblingIndexChanged != nullptr) { + onSiblingIndexChanged(index); + } + } +} + +Node *Node::getChildByPath(const ccstd::string &path) const { + size_t end = 0; + ccstd::vector segments = StringUtil::split(path, "/"); + auto *lastNode = const_cast(this); + for (const ccstd::string &segment : segments) { + if (segment.empty()) { + continue; + } + Node *next{nullptr}; + if (lastNode) { + for (const auto &child : lastNode->_children) { + if (child->_name == segment) { + next = child; + break; + } + } + lastNode = next; + } else { + break; + } + } + return lastNode; +} + +// +void Node::setPositionInternal(float x, float y, float z, bool calledFromJS) { + _localPosition.set(x, y, z); + invalidateChildren(TransformBit::POSITION); + + if (_eventMask & TRANSFORM_ON) { + emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::POSITION); + } + + if (!calledFromJS) { + notifyLocalPositionUpdated(); + } +} + +void Node::setRotationInternal(float x, float y, float z, float w, bool calledFromJS) { + _localRotation.set(x, y, z, w); + _eulerDirty = true; + + invalidateChildren(TransformBit::ROTATION); + + if (_eventMask & TRANSFORM_ON) { + emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::ROTATION); + } + + if (!calledFromJS) { + notifyLocalRotationUpdated(); + } +} + +void Node::setRotationFromEuler(float x, float y, float z) { + _euler.set(x, y, z); + Quaternion::fromEuler(x, y, z, &_localRotation); + _eulerDirty = false; + invalidateChildren(TransformBit::ROTATION); + if (_eventMask & TRANSFORM_ON) { + emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::ROTATION); + } + + notifyLocalRotationUpdated(); +} + +void Node::setScaleInternal(float x, float y, float z, bool calledFromJS) { + _localScale.set(x, y, z); + + invalidateChildren(TransformBit::SCALE); + if (_eventMask & TRANSFORM_ON) { + emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::SCALE); + } + + if (!calledFromJS) { + notifyLocalScaleUpdated(); + } +} + +void Node::updateWorldTransform() { //NOLINT(misc-no-recursion) + if (!getDirtyFlag()) { + return; + } + + index_t i = 0; + Node *curr = this; + Mat3 mat3; + Mat3 m43; + Quaternion quat; + while (curr && curr->getDirtyFlag()) { + setDirtyNode(i++, curr); + curr = curr->getParent(); + } + Node *child{nullptr}; + uint32_t dirtyBits = 0; + while (i) { + child = getDirtyNode(--i); + if (!child) { + continue; + } + dirtyBits |= child->getDirtyFlag(); + auto *currChild = child; + if (curr) { + if (dirtyBits & static_cast(TransformBit::POSITION)) { + currChild->_worldPosition.transformMat4(currChild->_localPosition, curr->_worldMatrix); + currChild->_worldMatrix.m[12] = currChild->_worldPosition.x; + currChild->_worldMatrix.m[13] = currChild->_worldPosition.y; + currChild->_worldMatrix.m[14] = currChild->_worldPosition.z; + } + if (dirtyBits & static_cast(TransformBit::RS)) { + Mat4::fromRTS(currChild->_localRotation, currChild->_localPosition, currChild->_localScale, &currChild->_worldMatrix); + Mat4::multiply(curr->_worldMatrix, currChild->_worldMatrix, &currChild->_worldMatrix); + if (dirtyBits & static_cast(TransformBit::ROTATION)) { + Quaternion::multiply(curr->_worldRotation, currChild->_localRotation, &currChild->_worldRotation); + } + quat = currChild->_worldRotation; + quat.conjugate(); + Mat3::fromQuat(quat, &mat3); + Mat3::fromMat4(currChild->_worldMatrix, &m43); + Mat3::multiply(mat3, m43, &mat3); + currChild->_worldScale.set(mat3.m[0], mat3.m[4], mat3.m[8]); + } + } else if (child) { + if (dirtyBits & static_cast(TransformBit::POSITION)) { + currChild->_worldPosition.set(currChild->_localPosition); + currChild->_worldMatrix.m[12] = currChild->_worldPosition.x; + currChild->_worldMatrix.m[13] = currChild->_worldPosition.y; + currChild->_worldMatrix.m[14] = currChild->_worldPosition.z; + } + if (dirtyBits & static_cast(TransformBit::RS)) { + if (dirtyBits & static_cast(TransformBit::ROTATION)) { + currChild->_worldRotation.set(currChild->_localRotation); + } + if (dirtyBits & static_cast(TransformBit::SCALE)) { + currChild->_worldScale.set(currChild->_localScale); + Mat4::fromRTS(currChild->_worldRotation, currChild->_worldPosition, currChild->_worldScale, &currChild->_worldMatrix); + } + } + } + child->setDirtyFlag(static_cast(TransformBit::NONE)); + curr = child; + } +} + +const Mat4 &Node::getWorldMatrix() const { //NOLINT(misc-no-recursion) + const_cast(this)->updateWorldTransform(); + return _worldMatrix; +} + +Mat4 Node::getWorldRS() { + updateWorldTransform(); + Mat4 target{_worldMatrix}; + target.m[12] = target.m[13] = target.m[14] = 0; + return target; +} + +Mat4 Node::getWorldRT() { + updateWorldTransform(); + Mat4 target; + Mat4::fromRT(_worldRotation, _worldPosition, &target); + return target; +} + +void Node::invalidateChildren(TransformBit dirtyBit) { + auto curDirtyBit{static_cast(dirtyBit)}; + const uint32_t childDirtyBit{curDirtyBit | static_cast(TransformBit::POSITION)}; + setDirtyNode(0, this); + int i{0}; + while (i >= 0) { + Node *cur = getDirtyNode(i--); + if (cur == nullptr) { + continue; + } + + const uint32_t hasChangedFlags = cur->getChangedFlags(); + if (cur->isValid() && (cur->getDirtyFlag() & hasChangedFlags & curDirtyBit) != curDirtyBit) { + cur->setDirtyFlag(cur->getDirtyFlag() | curDirtyBit); + cur->setChangedFlags(hasChangedFlags | curDirtyBit); + + for (Node *curChild : cur->getChildren()) { + setDirtyNode(++i, curChild); + } + } + curDirtyBit = childDirtyBit; + } +} + +void Node::setWorldPosition(float x, float y, float z) { + _worldPosition.set(x, y, z); + if (_parent) { + _parent->updateWorldTransform(); + Mat4 invertWMat{_parent->_worldMatrix}; + invertWMat.inverse(); + _localPosition.transformMat4(_worldPosition, invertWMat); + } else { + _localPosition.set(_worldPosition); + } + notifyLocalPositionUpdated(); + + invalidateChildren(TransformBit::POSITION); + + if (_eventMask & TRANSFORM_ON) { + emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::POSITION); + } +} + +const Vec3 &Node::getWorldPosition() const { + const_cast(this)->updateWorldTransform(); + return _worldPosition; +} + +void Node::setWorldRotation(float x, float y, float z, float w) { + _worldRotation.set(x, y, z, w); + if (_parent) { + _parent->updateWorldTransform(); + _localRotation.set(_parent->_worldRotation.getConjugated()); + _localRotation.multiply(_worldRotation); + } else { + _localRotation.set(_worldRotation); + } + + _eulerDirty = true; + + invalidateChildren(TransformBit::ROTATION); + + if (_eventMask & TRANSFORM_ON) { + emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::ROTATION); + } + + notifyLocalRotationUpdated(); +} + +const Quaternion &Node::getWorldRotation() const { //NOLINT(misc-no-recursion) + const_cast(this)->updateWorldTransform(); + return _worldRotation; +} + +void Node::setWorldScale(float x, float y, float z) { + _worldScale.set(x, y, z); + if (_parent != nullptr) { + _parent->updateWorldTransform(); + Mat3 mat3; + Mat3::fromQuat(_parent->_worldRotation.getConjugated(), &mat3); + Mat3 b; + Mat3::fromMat4(_parent->_worldMatrix, &b); + Mat3::multiply(mat3, b, &mat3); + Mat3 mat3Scaling; + mat3Scaling.m[0] = _worldScale.x; + mat3Scaling.m[4] = _worldScale.y; + mat3Scaling.m[8] = _worldScale.z; + + mat3.inverse(); + Mat3::multiply(mat3Scaling, mat3, &mat3); + _localScale.x = Vec3{mat3.m[0], mat3.m[1], mat3.m[2]}.length(); + _localScale.y = Vec3{mat3.m[3], mat3.m[4], mat3.m[5]}.length(); + _localScale.z = Vec3{mat3.m[6], mat3.m[7], mat3.m[8]}.length(); + } else { + _localScale = _worldScale; + } + + notifyLocalScaleUpdated(); + + invalidateChildren(TransformBit::SCALE); + if (_eventMask & TRANSFORM_ON) { + emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::SCALE); + } +} + +const Vec3 &Node::getWorldScale() const { + const_cast(this)->updateWorldTransform(); + return _worldScale; +} + +void Node::setForward(const Vec3 &dir) { + const float len = dir.length(); + Vec3 v3Temp = dir * (-1.F / len); + Quaternion qTemp{Quaternion::identity()}; + Quaternion::fromViewUp(v3Temp, &qTemp); + setWorldRotation(qTemp); +} + +void Node::setAngle(float val) { + _euler.set(0, 0, val); + Quaternion::createFromAngleZ(val, &_localRotation); + _eulerDirty = false; + invalidateChildren(TransformBit::ROTATION); + if (_eventMask & TRANSFORM_ON) { + emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::ROTATION); + } + + notifyLocalRotationUpdated(); +} + +void Node::onSetParent(Node *oldParent, bool keepWorldTransform) { + if (_parent) { + if ((oldParent == nullptr || oldParent->_scene != _parent->_scene) && _parent->_scene != nullptr) { + walk(setScene); + } + } + + if (keepWorldTransform) { + if (_parent) { + _parent->updateWorldTransform(); + if (mathutils::approx(_parent->_worldMatrix.determinant(), 0.F, mathutils::EPSILON)) { + CC_LOG_WARNING("14300"); + _dirtyFlag |= static_cast(TransformBit::TRS); + updateWorldTransform(); + } else { + Mat4 tmpMat4 = _parent->_worldMatrix.getInversed() * _worldMatrix; + Mat4::toRTS(tmpMat4, &_localRotation, &_localPosition, &_localScale); + } + } else { + _localPosition.set(_worldPosition); + _localRotation.set(_worldRotation); + _localScale.set(_worldScale); + } + + notifyLocalPositionRotationScaleUpdated(); + _eulerDirty = true; + } + invalidateChildren(TransformBit::TRS); +} + +void Node::rotate(const Quaternion &rot, NodeSpace ns /* = NodeSpace::LOCAL*/, bool calledFromJS /* = false*/) { + Quaternion qTempA{rot}; + qTempA.normalize(); + if (ns == NodeSpace::LOCAL) { + _localRotation *= qTempA; + } else if (ns == NodeSpace::WORLD) { + Quaternion qTempB{Quaternion::identity()}; + qTempB = qTempA * getWorldRotation(); + qTempA = _worldRotation; + qTempA.inverse(); + qTempB = qTempA * qTempB; + _localRotation = _localRotation * qTempB; + } + _eulerDirty = true; + invalidateChildren(TransformBit::ROTATION); + if (_eventMask & TRANSFORM_ON) { + emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::ROTATION); + } + + if (!calledFromJS) { + notifyLocalRotationUpdated(); + } +} + +void Node::lookAt(const Vec3 &pos, const Vec3 &up) { + Vec3 vTemp = getWorldPosition(); + Quaternion qTemp{Quaternion::identity()}; + vTemp -= pos; + vTemp.normalize(); + Quaternion::fromViewUp(vTemp, up, &qTemp); + setWorldRotation(qTemp); +} + +Vec3 Node::inverseTransformPoint(const Vec3 &p) { + Vec3 out; + out.set(p.x, p.y, p.z); + Node *cur{this}; + index_t i{0}; + while (cur != nullptr && cur->getParent()) { + setDirtyNode(i++, cur); + cur = cur->getParent(); + } + while (i >= 0) { + Vec3::transformInverseRTS(out, cur->getRotation(), cur->getPosition(), cur->getScale(), &out); + --i; + cur = getDirtyNode(i); + } + return out; +} + +void Node::setMatrix(const Mat4 &val) { + val.decompose(&_localScale, &_localRotation, &_localPosition); + notifyLocalPositionRotationScaleUpdated(); + + invalidateChildren(TransformBit::TRS); + _eulerDirty = true; + if (_eventMask & TRANSFORM_ON) { + emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::TRS); + } +} + +void Node::setWorldRotationFromEuler(float x, float y, float z) { + Quaternion::fromEuler(x, y, z, &_worldRotation); + if (_parent) { + _parent->updateWorldTransform(); + _localRotation = _parent->_worldRotation.getConjugated() * _worldRotation; + } else { + _localRotation = _worldRotation; + } + _eulerDirty = true; + + invalidateChildren(TransformBit::ROTATION); + if (_eventMask & TRANSFORM_ON) { + emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::ROTATION); + } + + notifyLocalRotationUpdated(); +} + +void Node::setRTSInternal(Quaternion *rot, Vec3 *pos, Vec3 *scale, bool calledFromJS) { + uint32_t dirtyBit = 0; + if (rot) { + dirtyBit |= static_cast(TransformBit::ROTATION); + _localRotation = *rot; + _eulerDirty = true; + } + if (pos) { + _localPosition = *pos; + dirtyBit |= static_cast(TransformBit::POSITION); + } + if (scale) { + _localScale = *scale; + dirtyBit |= static_cast(TransformBit::SCALE); + } + + if (!calledFromJS) { + notifyLocalPositionRotationScaleUpdated(); + } + + if (dirtyBit) { + invalidateChildren(static_cast(dirtyBit)); + if (_eventMask & TRANSFORM_ON) { + emit(NodeEventType::TRANSFORM_CHANGED, dirtyBit); + } + } +} + +void Node::resetChangedFlags() { + globalFlagChangeVersion++; +} + +void Node::clearNodeArray() { + if (clearFrame < clearRound) { + clearFrame++; + } else { + clearFrame = 0; + dirtyNodes.clear(); + } +} + +ccstd::string Node::getPathInHierarchy() const { + ccstd::string result = getName(); + Node *curNode = getParent(); + while (curNode && curNode->getParent()) { + result.insert(0, "/").insert(0, curNode->getName()); + curNode = curNode->getParent(); + } + return result; +} + +void Node::translate(const Vec3 &trans, NodeSpace ns) { + Vec3 v3Temp{trans}; + if (ns == NodeSpace::LOCAL) { + v3Temp.transformQuat(_localRotation); + _localPosition.x += v3Temp.x; + _localPosition.y += v3Temp.y; + _localPosition.z += v3Temp.z; + } else if (ns == NodeSpace::WORLD) { + if (_parent) { + Quaternion qTemp = _parent->getWorldRotation(); + qTemp.inverse(); + v3Temp.transformQuat(qTemp); + Vec3 scale{_worldScale}; + _localPosition.x += v3Temp.x / scale.x; + _localPosition.y += v3Temp.y / scale.y; + _localPosition.z += v3Temp.z / scale.z; + } else { + _localPosition.x += trans.x; + _localPosition.y += trans.y; + _localPosition.z += trans.z; + } + } + + notifyLocalPositionUpdated(); + + invalidateChildren(TransformBit::POSITION); + if (_eventMask & TRANSFORM_ON) { + emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::POSITION); + } +} + +bool Node::onPreDestroy() { + bool result = onPreDestroyBase(); + // TODO(Lenovo): bookOfChange free + return result; +} + +void Node::onHierarchyChanged(Node *oldParent) { + emit(EventTypesToJS::NODE_REATTACH); + _eventProcessor->reattach(); + onHierarchyChangedBase(oldParent); +} + +/* static */ +//Node *Node::find(const ccstd::string &path, Node *referenceNode /* = nullptr*/) { +// return cc::find(path, referenceNode); +//} + +// For deserialization +// void Node::_setChild(index_t i, Node *child) { +// if (i < _children.size()) { +// _children[i] = child; +// } else { +// CC_LOG_ERROR("Invalid index (%d) for Node children (size: %u)", i, static_cast(_children.size())); +// } +//} +// +// Node *Node::_getChild(index_t i) { +// if (i < _children.size()) { +// return _children[i]; +// } +// CC_LOG_ERROR("Invalid index (%d) for Node children (size: %u)", i, static_cast(_children.size())); +// return nullptr; +//} +// +// void Node::_setChildrenSize(uint32_t size) { +// _children.resize(size); +//} +// +// uint32_t Node::_getChildrenSize() { +// return _children.size(); +//} +// +void Node::_setChildren(ccstd::vector> &&children) { + _children = std::move(children); +} + +// + +} // namespace cc diff --git a/native/engine/lcc-ui-sorting-group-native/native/cocos/core/scene-graph/Node.h b/native/engine/lcc-ui-sorting-group-native/native/cocos/core/scene-graph/Node.h new file mode 100644 index 0000000..4f364ef --- /dev/null +++ b/native/engine/lcc-ui-sorting-group-native/native/cocos/core/scene-graph/Node.h @@ -0,0 +1,861 @@ +/**************************************************************************** + Copyright (c) 2021 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated engine source code (the "Software"), a limited, + worldwide, royalty-free, non-assignable, revocable and non-exclusive license + to use Cocos Creator solely to develop games on your target platforms. You shall + not use Cocos Creator software for developing other software or tools that's + used for developing games. You are not granted to publish, distribute, + sublicense, and/or sell copies of Cocos Creator. + + The software or tools in this License Agreement are licensed, not sold. + Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ****************************************************************************/ + +#pragma once + +#include "base/Ptr.h" +#include "base/std/any.h" +#include "bindings/utils/BindingUtils.h" +//#include "core/components/Component.h" +//#include "core/event/Event.h" +#include "core/event/EventTypesToJS.h" +#include "core/scene-graph/BaseNode.h" +#include "core/scene-graph/Layers.h" +#include "core/scene-graph/NodeEnum.h" +#include "core/scene-graph/NodeEvent.h" +#include "core/scene-graph/NodeEventProcessor.h" + +#include "math/Mat3.h" +#include "math/Mat4.h" +#include "math/Quaternion.h" +#include "math/Vec3.h" +#include "math/Vec4.h" + +namespace cc { + +class Scene; +class NodeEventProcessor; + +/** + * Event types emitted by Node + */ +using EventType = NodeEventType; +/** + * Bit masks for Node transformation parts + */ +using TransformDirtyBit = TransformBit; + +class Node : public BaseNode { +public: + class UserData : public RefCounted { + public: + ~UserData() override = default; + + protected: + UserData() = default; + }; + + using Super = BaseNode; + + static const uint32_t TRANSFORM_ON; + + static Node *instantiate(Node *cloned, bool isSyncedNode); + static void setScene(Node *); + + /** + * @en Finds a node by hierarchy path, the path is case-sensitive. + * It will traverse the hierarchy by splitting the path using '/' character. + * This function will still returns the node even if it is inactive. + * It is recommended to not use this function every frame instead cache the result at startup. + * @zh 通过路径从节点树中查找节点的方法,路径是大小写敏感的,并且通过 `/` 来分隔节点层级。 + * 即使节点的状态是未启用的也可以找到,建议将结果缓存,而不是每次需要都去查找。 + * @param path The path of the target node + * @param referenceNode If given, the search will be limited in the sub node tree of the reference node + */ + // static Node *find(const ccstd::string &path, Node *referenceNode = nullptr); + + /** + * @en Determine whether the given object is a normal Node. Will return false if [[Scene]] given. + * @zh 指定对象是否是普通的节点?如果传入 [[Scene]] 会返回 false。 + */ + template + static bool isNode(T *obj); + + static void resetChangedFlags(); + static void clearNodeArray(); + + Node(); + explicit Node(const ccstd::string &name); + ~Node() override; + + virtual void onPostActivated(bool active) {} + + void setParent(Node *parent, bool isKeepWorld = false); + + inline Scene *getScene() const { return _scene; }; + + using WalkCallback = std::function; + void walk(const WalkCallback &preFunc); + void walk(const WalkCallback &preFunc, const WalkCallback &postFunc); + + template + void on(const CallbacksInvoker::KeyType &type, void (Target::*memberFn)(Args...), Target *target, bool useCapture = false); + + template + void on(const CallbacksInvoker::KeyType &type, std::function &&callback, CallbackID &cbID, bool useCapture = false); + + template + void on(const CallbacksInvoker::KeyType &type, std::function &&callback, Target *target, CallbackID &cbID, bool useCapture = false); + + template + std::enable_if_t::value, void> + on(const CallbacksInvoker::KeyType &type, LambdaType &&callback, Target *target, CallbackID &cbID, bool useCapture = false); + + template + std::enable_if_t::value, void> + on(const CallbacksInvoker::KeyType &type, LambdaType &&callback, CallbackID &cbID, bool useCapture = false); + + template + void on(const CallbacksInvoker::KeyType &type, std::function &&callback, bool useCapture = false); + + template + void on(const CallbacksInvoker::KeyType &type, std::function &&callback, Target *target, bool useCapture = false); + + template + std::enable_if_t::value, void> + on(const CallbacksInvoker::KeyType &type, LambdaType &&callback, Target *target, bool useCapture = false); + + template + std::enable_if_t::value, void> + on(const CallbacksInvoker::KeyType &type, LambdaType &&callback, bool useCapture = false); + + template + void once(const CallbacksInvoker::KeyType &type, void (Target::*memberFn)(Args...), Target *target, bool useCapture = false); + + template + void once(const CallbacksInvoker::KeyType &type, std::function &&callback, CallbackID &cbID, bool useCapture = false); + + template + void once(const CallbacksInvoker::KeyType &type, std::function &&callback, Target *target, CallbackID &cbID, bool useCapture = false); + + template + std::enable_if_t::value, void> + once(const CallbacksInvoker::KeyType &type, LambdaType &&callback, CallbackID &cbID, bool useCapture = false); + + template + std::enable_if_t::value, void> + once(const CallbacksInvoker::KeyType &type, LambdaType &&callback, Target *target, CallbackID &cbID, bool useCapture = false); + + template + void once(const CallbacksInvoker::KeyType &type, std::function &&callback, bool useCapture = false); + + template + void once(const CallbacksInvoker::KeyType &type, std::function &&callback, Target *target, bool useCapture = false); + + template + std::enable_if_t::value, void> + once(const CallbacksInvoker::KeyType &type, LambdaType &&callback, bool useCapture = false); + + template + std::enable_if_t::value, void> + once(const CallbacksInvoker::KeyType &type, LambdaType &&callback, Target *target, bool useCapture = false); + + void off(const CallbacksInvoker::KeyType &type, bool useCapture = false); + + void off(const CallbacksInvoker::KeyType &type, const CallbackID &cbID, bool useCapture = false); + + void off(const CallbacksInvoker::KeyType &type, void *target, bool useCapture = false); + + template + void off(const CallbacksInvoker::KeyType &type, void (Target::*memberFn)(Args...), Target *target, bool useCapture = false); + + template + void emit(const CallbacksInvoker::KeyType &type, Args &&...args); + + // void dispatchEvent(event::Event *event); + bool hasEventListener(const CallbacksInvoker::KeyType &type) const; + bool hasEventListener(const CallbacksInvoker::KeyType &type, const CallbackID &cbID) const; + bool hasEventListener(const CallbacksInvoker::KeyType &type, void *target) const; + bool hasEventListener(const CallbacksInvoker::KeyType &type, void *target, const CallbackID &cbID) const; + + template + bool hasEventListener(const CallbacksInvoker::KeyType &type, void (Target::*memberFn)(Args...), Target *target) const; + + void targetOff(const CallbacksInvoker::KeyType &type); + + bool destroy() override { + if (CCObject::destroy()) { + setActive(false); + return true; + } + return false; + } + + inline void destroyAllChildren() { + for (const auto &child : _children) { + child->destroy(); + } + } + + inline void updateSiblingIndex() { + index_t i = 0; + for (const auto &child : _children) { + child->_siblingIndex = i++; + } + emit(NodeEventType::SIBLING_ORDER_CHANGED); + } + + inline void addChild(Node *node) { node->setParent(this); } + + inline void removeChild(Node *node) const { + auto idx = getIdxOfChild(_children, node); + if (idx != -1) { + node->setParent(nullptr); + } + } + inline void removeFromParent() { + if (_parent) { + _parent->removeChild(this); + } + } + void removeAllChildren(); + + bool isChildOf(Node *parent) const; + + void setActive(bool isActive); + + void setSiblingIndex(index_t index); + + inline bool isPersistNode() const { + return static_cast(_objFlags & Flags::DONT_DESTROY) > 0; + } + + inline void setPersistNode(bool val) { + val ? _objFlags |= Flags::DONT_DESTROY : _objFlags &= ~Flags::DONT_DESTROY; + } + + inline const ccstd::string &getUuid() const { + return _id; + } + + inline bool isActive() const { return _active != 0; } + + inline bool isActiveInHierarchy() const { return _activeInHierarchy != 0; } + inline void setActiveInHierarchy(bool v) { + _activeInHierarchy = (v ? 1 : 0); + } + + inline const ccstd::vector> &getChildren() const { return _children; } + inline Node *getParent() const { return _parent; } + inline NodeEventProcessor *getEventProcessor() const { return _eventProcessor; } + + Node *getChildByUuid(const ccstd::string &uuid) const; + Node *getChildByName(const ccstd::string &name) const; + Node *getChildByPath(const ccstd::string &path) const; + inline index_t getSiblingIndex() const { return _siblingIndex; } + inline UserData *getUserData() { return _userData.get(); } + inline void setUserData(UserData *data) { _userData = data; } + inline void insertChild(Node *child, index_t siblingIndex) { + child->setParent(this); + child->setSiblingIndex(siblingIndex); + } + + void invalidateChildren(TransformBit dirtyBit); + + void translate(const Vec3 &, NodeSpace ns = NodeSpace::LOCAL); + void rotate(const Quaternion &rot, NodeSpace ns = NodeSpace::LOCAL, bool calledFromJS = false); + inline void rotateForJS(float x, float y, float z, float w, NodeSpace ns = NodeSpace::LOCAL) { + rotate(Quaternion(x, y, z, w), ns, true); + } + void lookAt(const Vec3 &pos, const Vec3 &up = Vec3::UNIT_Y); + + void pauseSystemEvents(bool recursive) {} // cjh TODO: + void resumeSystemEvents(bool recursive) {} // cjh TODO: + + ccstd::string getPathInHierarchy() const; + // =============================== + // transform + // =============================== + + /** + * @en Set position in local coordinate system + * @zh 设置本地坐标 + * @param position Target position + */ + inline void setPosition(const Vec3 &pos) { setPosition(pos.x, pos.y, pos.z); } + inline void setPosition(float x, float y) { setPosition(x, y, _localPosition.z); } + inline void setPosition(float x, float y, float z) { setPositionInternal(x, y, z, false); } + inline void setPositionInternal(float x, float y, bool calledFromJS) { setPositionInternal(x, y, _localPosition.z, calledFromJS); } + void setPositionInternal(float x, float y, float z, bool calledFromJS); + // It is invoked after deserialization. It only sets position value, not triggers other logic. + inline void setPositionForJS(float x, float y, float z) { _localPosition.set(x, y, z); } + /** + * @en Get position in local coordinate system, please try to pass `out` vector and reuse it to avoid garbage. + * @zh 获取本地坐标,注意,尽可能传递复用的 [[Vec3]] 以避免产生垃圾。 + * @param out Set the result to out vector + * @return If `out` given, the return value equals to `out`, otherwise a new vector will be generated and return + */ + inline const Vec3 &getPosition() const { return _localPosition; } + + /** + * @en Set rotation in local coordinate system with a quaternion representing the rotation + * @zh 用四元数设置本地旋转 + * @param rotation Rotation in quaternion + */ + inline void setRotation(const Quaternion &rotation) { setRotation(rotation.x, rotation.y, rotation.z, rotation.w); } + inline void setRotation(float x, float y, float z, float w) { setRotationInternal(x, y, z, w, false); } + void setRotationInternal(float x, float y, float z, float w, bool calledFromJS); + inline void setRotationForJS(float x, float y, float z, float w) { _localRotation.set(x, y, z, w); } + + inline void setEulerAngles(const Vec3 &val) { setRotationFromEuler(val.x, val.y, val.z); } + inline void setRotationFromEuler(const Vec3 &val) { setRotationFromEuler(val.x, val.y, val.z); } + inline void setRotationFromEuler(float x, float y) { setRotationFromEuler(x, y, _euler.z); } + void setRotationFromEuler(float x, float y, float z); + inline void setRotationFromEulerForJS(float x, float y, float z) { _euler.set(x, y, z); } + /** + * @en Get rotation as quaternion in local coordinate system, please try to pass `out` quaternion and reuse it to avoid garbage. + * @zh 获取本地旋转,注意,尽可能传递复用的 [[Quat]] 以避免产生垃圾。 + * @param out Set the result to out quaternion + * @return If `out` given, the return value equals to `out`, otherwise a new quaternion will be generated and return + */ + inline const Quaternion &getRotation() const { return _localRotation; } + + /** + * @en Set scale in local coordinate system + * @zh 设置本地缩放 + * @param scale Target scale + */ + inline void setScale(const Vec3 &scale) { setScale(scale.x, scale.y, scale.z); } + inline void setScale(float x, float y) { setScale(x, y, _localScale.z); } + inline void setScale(float x, float y, float z) { setScaleInternal(x, y, z, false); } + inline void setScaleInternal(float x, float y, bool calledFromJS) { setScaleInternal(x, y, _localScale.z, calledFromJS); } + void setScaleInternal(float x, float y, float z, bool calledFromJS); + inline void setScaleForJS(float x, float y, float z) { _localScale.set(x, y, z); } + /** + * @en Get scale in local coordinate system, please try to pass `out` vector and reuse it to avoid garbage. + * @zh 获取本地缩放,注意,尽可能传递复用的 [[Vec3]] 以避免产生垃圾。 + * @param out Set the result to out vector + * @return If `out` given, the return value equals to `out`, otherwise a new vector will be generated and return + */ + inline const Vec3 &getScale() const { return _localScale; } + + /** + * @en Inversely transform a point from world coordinate system to local coordinate system. + * @zh 逆向变换一个空间点,一般用于将世界坐标转换到本地坐标系中。 + * @param p A position in world coordinate system + * @return The result point in local coordinate system will be stored in this vector + */ + Vec3 inverseTransformPoint(const Vec3 &p); + + /** + * @en Set position in world coordinate system + * @zh 设置世界坐标 + * @param position Target position + */ + inline void setWorldPosition(const Vec3 &pos) { setWorldPosition(pos.x, pos.y, pos.z); } + void setWorldPosition(float x, float y, float z); + + /** + * @en Get position in world coordinate system, please try to pass `out` vector and reuse it to avoid garbage. + * @zh 获取世界坐标,注意,尽可能传递复用的 [[Vec3]] 以避免产生垃圾。 + * @param out Set the result to out vector + * @return If `out` given, the return value equals to `out`, otherwise a new vector will be generated and return + */ + const Vec3 &getWorldPosition() const; + + /** + * @en Set rotation in world coordinate system with a quaternion representing the rotation + * @zh 用四元数设置世界坐标系下的旋转 + * @param rotation Rotation in quaternion + */ + inline void setWorldRotation(const Quaternion &rotation) { setWorldRotation(rotation.x, rotation.y, rotation.z, rotation.w); } + void setWorldRotation(float x, float y, float z, float w); + /** + * @en Get rotation as quaternion in world coordinate system, please try to pass `out` quaternion and reuse it to avoid garbage. + * @zh 获取世界坐标系下的旋转,注意,尽可能传递复用的 [[Quat]] 以避免产生垃圾。 + * @param out Set the result to out quaternion + * @return If `out` given, the return value equals to `out`, otherwise a new quaternion will be generated and return + */ + const Quaternion &getWorldRotation() const; + + /** + * @en Set rotation in world coordinate system with euler angles + * @zh 用欧拉角设置世界坐标系下的旋转 + * @param x X axis rotation + * @param y Y axis rotation + * @param z Z axis rotation + */ + inline void setWorldScale(const Vec3 &scale) { setWorldScale(scale.x, scale.y, scale.z); } + void setWorldScale(float x, float y, float z); + + /** + * @en Get scale in world coordinate system, please try to pass `out` vector and reuse it to avoid garbage. + * @zh 获取世界缩放,注意,尽可能传递复用的 [[Vec3]] 以避免产生垃圾。 + * @param out Set the result to out vector + * @return If `out` given, the return value equals to `out`, otherwise a new vector will be generated and return + */ + const Vec3 &getWorldScale() const; + + void setWorldRotationFromEuler(float x, float y, float z); + + /** + * @en Local transformation matrix + * @zh 本地坐标系变换矩阵 + */ + void setMatrix(const Mat4 &val); + + /** + * @en Update the world transform information if outdated + * @zh 更新节点的世界变换信息 + */ + void updateWorldTransform(); + + /** + * @en Get a world transform matrix + * @zh 获取世界变换矩阵 + * @param out Set the result to out matrix + * @return If `out` given, the return value equals to `out`, otherwise a new matrix will be generated and return + */ + const Mat4 &getWorldMatrix() const; + + /** + * @en Get a world transform matrix with only rotation and scale + * @zh 获取只包含旋转和缩放的世界变换矩阵 + * @param out Set the result to out matrix + * @return If `out` given, the return value equals to `out`, otherwise a new matrix will be generated and return + */ + Mat4 getWorldRS(); + + /** + * @en Get a world transform matrix with only rotation and translation + * @zh 获取只包含旋转和位移的世界变换矩阵 + * @param out Set the result to out matrix + * @return If `out` given, the return value equals to `out`, otherwise a new matrix will be generated and return + */ + Mat4 getWorldRT(); + + /** + * @en Set local transformation with rotation, position and scale separately. + * @zh 一次性设置所有局部变换(平移、旋转、缩放)信息 + * @param rot The rotation + * @param pos The position + * @param scale The scale + */ + void setRTSInternal(Quaternion *rot, Vec3 *pos, Vec3 *scale, bool calledFromJS); + inline void setRTS(Quaternion *rot, Vec3 *pos, Vec3 *scale) { setRTSInternal(rot, pos, scale, false); } + + void setForward(const Vec3 &dir); + + void setAngle(float); + + inline const Vec3 &getEulerAngles() { + if (_eulerDirty) { + Quaternion::toEuler(_localRotation, false, &_euler); + _eulerDirty = false; + } + return _euler; + } + + inline float getAngle() const { + return _euler.z; + } + + inline Vec3 getForward() const { + Vec3 forward{0, 0, -1}; + forward.transformQuat(getWorldRotation()); + return forward; + } + + inline Vec3 getUp() const { + Vec3 up{0, 1, 0}; + up.transformQuat(getWorldRotation()); + return up; + } + + inline Vec3 getRight() const { + Vec3 right{1, 0, 0}; + right.transformQuat(getWorldRotation()); + return right; + } + + inline bool isStatic() const { + return _isStatic != 0; + } + + inline void setStatic(bool v) { + _isStatic = v ? 1 : 0; + } + + /** + * @en Whether the node's transformation have changed during the current frame. + * @zh 这个节点的空间变换信息在当前帧内是否有变过? + */ + inline uint32_t getChangedFlags() const { + return _hasChangedFlagsVersion == globalFlagChangeVersion ? _hasChangedFlags : 0; + } + inline void setChangedFlags(uint32_t value) { + _hasChangedFlagsVersion = globalFlagChangeVersion; + _hasChangedFlags = value; + } + + inline void setDirtyFlag(uint32_t value) { _dirtyFlag = value; } + inline uint32_t getDirtyFlag() const { return _dirtyFlag; } + inline void setLayer(uint32_t layer) { + _layer = layer; + emit(NodeEventType::LAYER_CHANGED, layer); + } + inline uint32_t getLayer() const { return _layer; } + + inline float getUISortingPriority() const { return _uiSortingPriority; } + + inline bool isUISortingEnabled() const { return _uiSortingEnabled != 0; } + + // inline NodeUiProperties *getUIProps() const { return _uiProps.get(); } + + // // ------------------ Component code start ----------------------------- + // // TODO(Lenovo): + // + // template ::value>> + // static Component *findComponent(Node * /*node*/) { + // // cjh TODO: + // CC_ASSERT(false); + // return nullptr; + // } + // + // template ::value>> + // static Component *findComponents(Node * /*node*/, const ccstd::vector & /*components*/) { + // // cjh TODO: + // CC_ASSERT(false); + // return nullptr; + // } + // + // template ::value>> + // static Component *findChildComponent(const ccstd::vector & /*children*/) { + // // cjh TODO: + // CC_ASSERT(false); + // return nullptr; + // } + // + // template ::value>> + // static void findChildComponents(const ccstd::vector & /*children*/, ccstd::vector & /*components*/) { + // // cjh TODO: + // CC_ASSERT(false); + // } + // + // template , T>> + // T *addComponent() { + // T *comp = new T(); + // return static_cast(addComponent(comp)); + // } + // + // template ::value>> + // void removeComponent() { + // for (auto iter = _components.begin(); iter != _components.end(); ++iter) { + // if (dynamic_cast(*iter) != nullptr) { + // _components.erase(iter); + // } + // } + // } + // + // Component *addComponent(Component *comp); + // void removeComponent(Component *comp); + // + // template ::value>> + // Component *getComponent() const { + // for (auto *component : _components) { + // if (dynamic_cast(component) != nullptr) { + // return component; + // } + // } + // return nullptr; + // } + // + // // TODO(Lenovo): + // template ::value>> + // ccstd::vector getComponents() const { + // // cjh TODO: + // CC_ASSERT(false); + // return {}; + // }; + // + // template ::value>> + // Component *getComponentInChildren(const T & /*comp*/) const { + // // cjh TODO: + // CC_ASSERT(false); + // return nullptr; + // } + // + // template ::value>> + // ccstd::vector getComponentsInChildren() const { + // // cjh TODO: + // CC_ASSERT(false); + // return {}; + // } + // + // inline ccstd::vector getComponents() const { return _components; } + // + // void checkMultipleComp(Component *comp) {} + // ccstd::vector _components; + // + // friend void componentCorrupted(Node *node, Component *comp, uint32_t index); + // ------------------ Component code end ----------------------------- + + // For deserialization + // void _setChild(index_t i, Node *child); + // Node * _getChild(index_t i); + // void _setChildrenSize(uint32_t size); + // uint32_t _getChildrenSize(); + void _setChildren(ccstd::vector> &&children); // NOLINT + + inline se::Object *_getSharedArrayBufferObject() const { return _sharedMemoryActor.getSharedArrayBufferObject(); } // NOLINT + + bool onPreDestroy() override; + bool onPreDestroyBase(); + + std::function onSiblingIndexChanged{nullptr}; + // For deserialization + ccstd::string _id; + Node *_parent{nullptr}; + +private: + static index_t getIdxOfChild(const ccstd::vector> &, Node *); + + virtual void onBatchCreated(bool dontChildPrefab); + virtual void updateScene(); + + void onSetParent(Node *oldParent, bool keepWorldTransform); + void onHierarchyChanged(Node *); + void onHierarchyChangedBase(Node *oldParent); + + inline void notifyLocalPositionUpdated() { + emit(EventTypesToJS::NODE_LOCAL_POSITION_UPDATED, _localPosition.x, _localPosition.y, _localPosition.z); + } + + inline void notifyLocalRotationUpdated() { + emit(EventTypesToJS::NODE_LOCAL_ROTATION_UPDATED, _localRotation.x, _localRotation.y, _localRotation.z, _localRotation.w); + } + + inline void notifyLocalScaleUpdated() { + emit(EventTypesToJS::NODE_LOCAL_SCALE_UPDATED, _localScale.x, _localScale.y, _localScale.z); + } + + inline void notifyLocalPositionRotationScaleUpdated() { + emit(EventTypesToJS::NODE_LOCAL_POSITION_ROTATION_SCALE_UPDATED, + _localPosition.x, _localPosition.y, _localPosition.z, + _localRotation.x, _localRotation.y, _localRotation.z, _localRotation.w, + _localScale.x, _localScale.y, _localScale.z); + } + +#if CC_EDITOR + inline void notifyEditorAttached(bool attached) { + emit(EventTypesToJS::NODE_EDITOR_ATTACHED, attached); + } +#endif + + // increase on every frame, used to identify the frame + static uint32_t globalFlagChangeVersion; + + static uint32_t clearFrame; + static uint32_t clearRound; + + Scene *_scene{nullptr}; + NodeEventProcessor *_eventProcessor{nullptr}; + IntrusivePtr _userData; + + ccstd::vector> _children; + bindings::NativeMemorySharedToScriptActor _sharedMemoryActor; + // local transform + Vec3 _localPosition{Vec3::ZERO}; + Vec3 _localScale{Vec3::ONE}; + Quaternion _localRotation{Quaternion::identity()}; + // world transform + Vec3 _worldPosition{Vec3::ZERO}; + Vec3 _worldScale{Vec3::ONE}; + Vec3 _euler{0, 0, 0}; + Quaternion _worldRotation{Quaternion::identity()}; + Mat4 _worldMatrix{Mat4::IDENTITY}; + + // Shared memory with JS + // NOTE: TypeArray created in node.jsb.ts _ctor should have the same memory layout + uint32_t _eventMask{0}; // Uint32: 0 + uint32_t _layer{static_cast(Layers::LayerList::DEFAULT)}; // Uint32: 1 + uint32_t _dirtyFlag{0}; // Uint32: 2 + index_t _siblingIndex{0}; // Int32: 0 + float _uiSortingPriority{0.0F}; // Float: 0 + uint8_t _activeInHierarchy{0}; // Uint8: 0 + uint8_t _active{1}; // Uint8: 1 + uint8_t _isStatic{0}; // Uint8: 2 + uint8_t _uiSortingEnabled{0}; // Uint8: 3 + + /* set _hasChangedFlagsVersion to globalFlagChangeVersion when `_hasChangedFlags` updated. + * `globalFlagChangeVersion == _hasChangedFlagsVersion` means that "_hasChangedFlags is dirty in current frametime". + */ + uint32_t _hasChangedFlagsVersion{0}; + uint32_t _hasChangedFlags{0}; + + bool _eulerDirty{false}; + + friend class NodeActivator; + friend class Scene; + + CC_DISALLOW_COPY_MOVE_ASSIGN(Node); +}; + +template +bool Node::isNode(T *obj) { + return dynamic_cast(obj) != nullptr && dynamic_cast(obj) == nullptr; +} + +template +void Node::emit(const CallbacksInvoker::KeyType &type, Args &&...args) { + _eventProcessor->emit(type, std::forward(args)...); +} + +template +void Node::on(const CallbacksInvoker::KeyType &type, void (Target::*memberFn)(Args...), Target *target, bool useCapture) { + if (type == NodeEventType::TRANSFORM_CHANGED) { + _eventMask |= TRANSFORM_ON; + } + _eventProcessor->on(type, memberFn, target, useCapture); +} + +template +void Node::on(const CallbacksInvoker::KeyType &type, std::function &&callback, CallbackID &cbID, bool useCapture) { + if (type == NodeEventType::TRANSFORM_CHANGED) { + _eventMask |= TRANSFORM_ON; + } + _eventProcessor->on(type, std::forward>(callback), cbID, useCapture); +} + +template +void Node::on(const CallbacksInvoker::KeyType &type, std::function &&callback, Target *target, CallbackID &cbID, bool useCapture) { + if (type == NodeEventType::TRANSFORM_CHANGED) { + _eventMask |= TRANSFORM_ON; + } + _eventProcessor->on(type, std::forward>(callback), target, cbID, useCapture); +} + +template +std::enable_if_t::value, void> +Node::on(const CallbacksInvoker::KeyType &type, LambdaType &&callback, Target *target, CallbackID &cbID, bool useCapture) { + if (type == NodeEventType::TRANSFORM_CHANGED) { + _eventMask |= TRANSFORM_ON; + } + _eventProcessor->on(type, callback, target, cbID, useCapture); +} + +template +std::enable_if_t::value, void> +Node::on(const CallbacksInvoker::KeyType &type, LambdaType &&callback, CallbackID &cbID, bool useCapture) { + if (type == NodeEventType::TRANSFORM_CHANGED) { + _eventMask |= TRANSFORM_ON; + } + _eventProcessor->on(type, callback, cbID, useCapture); +} + +template +void Node::on(const CallbacksInvoker::KeyType &type, std::function &&callback, bool useCapture) { + CallbackID unusedID{0}; + on(type, callback, unusedID, useCapture); +} + +template +void Node::on(const CallbacksInvoker::KeyType &type, std::function &&callback, Target *target, bool useCapture) { + CallbackID unusedID{0}; + on(type, callback, target, unusedID, useCapture); +} + +template +std::enable_if_t::value, void> +Node::on(const CallbacksInvoker::KeyType &type, LambdaType &&callback, Target *target, bool useCapture) { + CallbackID unusedID{0}; + on(type, callback, target, unusedID, useCapture); +} + +template +std::enable_if_t::value, void> +Node::on(const CallbacksInvoker::KeyType &type, LambdaType &&callback, bool useCapture) { + CallbackID unusedID{0}; + on(type, callback, unusedID, useCapture); +} +template +void Node::once(const CallbacksInvoker::KeyType &type, void (Target::*memberFn)(Args...), Target *target, bool useCapture) { + _eventProcessor->once(type, memberFn, target, useCapture); +} +template +void Node::once(const CallbacksInvoker::KeyType &type, std::function &&callback, CallbackID &cbID, bool useCapture) { + _eventProcessor->once(type, callback, cbID, useCapture); +} + +template +void Node::once(const CallbacksInvoker::KeyType &type, std::function &&callback, Target *target, CallbackID &cbID, bool useCapture) { + _eventProcessor->once(type, std::forward>(callback), target, cbID, useCapture); +} + +template +std::enable_if_t::value, void> +Node::once(const CallbacksInvoker::KeyType &type, LambdaType &&callback, CallbackID &cbID, bool useCapture) { + _eventProcessor->once(type, callback, cbID, useCapture); +} + +template +std::enable_if_t::value, void> +Node::once(const CallbacksInvoker::KeyType &type, LambdaType &&callback, Target *target, CallbackID &cbID, bool useCapture) { + _eventProcessor->once(type, callback, target, cbID, useCapture); +} + +template +void Node::once(const CallbacksInvoker::KeyType &type, std::function &&callback, bool useCapture) { + CallbackID unusedID{0}; + once(type, callback, unusedID, useCapture); +} + +template +void Node::once(const CallbacksInvoker::KeyType &type, std::function &&callback, Target *target, bool useCapture) { + CallbackID unusedID{0}; + once(type, callback, target, unusedID, useCapture); +} + +template +std::enable_if_t::value, void> +Node::once(const CallbacksInvoker::KeyType &type, LambdaType &&callback, bool useCapture) { + CallbackID unusedID{0}; + once(type, callback, unusedID, useCapture); +} + +template +std::enable_if_t::value, void> +Node::once(const CallbacksInvoker::KeyType &type, LambdaType &&callback, Target *target, bool useCapture) { + CallbackID unusedID{0}; + once(type, callback, target, unusedID, useCapture); +} + +template +void Node::off(const CallbacksInvoker::KeyType &type, void (Target::*memberFn)(Args...), Target *target, bool useCapture) { + _eventProcessor->off(type, memberFn, target, useCapture); + bool hasListeners = _eventProcessor->hasEventListener(type); + if (!hasListeners) { + if (type == NodeEventType::TRANSFORM_CHANGED) { + _eventMask &= ~TRANSFORM_ON; + } + } +} + +template +bool Node::hasEventListener(const CallbacksInvoker::KeyType &type, void (Target::*memberFn)(Args...), Target *target) const { + return _eventProcessor->hasEventListener(type, memberFn, target); +} + +} // namespace cc diff --git a/readme.md b/readme.md index 460d9c3..62d4414 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,7 @@ 相信大部分人都做过UI渲染优化,其重点也就是合批。但是在当前cocos使用节点树深度遍历的方式中,想要把相同类型的组件放在一起,势必会修改节点树,很可能破坏掉优雅合理的节点组织结构;还有可能因为节点树的改动,需要添加一些冗余糟糕的代码。 最近刚好有时间,逛论坛的时候看到有同学提这类问题,各位大哥也给出了解决方案,就是在渲染的时候给节点重新排序。于是心血来潮去看了下3.6.3的源码,好像要实现其实挺简单的。 # 原理 -为需要排序的UI渲染器设置排序优先级; 在UI渲染遍历节点树阶段,不立即执行各种UI渲染器,而是把UI渲染器缓存起来;在UI渲染遍历完节点树后,对UI渲染器缓存通过排序优先级进行排序后执行。然而,遮罩会打断这一过程,所以如果项目中大量使用了遮罩,优化效果可能会不太明显。 +为需要排序的UI渲染器(Sprite或者Label等)设置排序优先级; 在UI渲染遍历节点树阶段,不立即执行各种UI渲染器,而是把UI渲染器缓存起来;在UI渲染遍历完节点树后,对UI渲染器缓存通过排序优先级进行排序后执行。然而,遮罩会打断这一过程,所以如果项目中大量使用了遮罩,优化效果可能会不太明显。 # 效果对比 在ScrollView下生成200个项目测试对比。 @@ -68,8 +68,11 @@ export enum SortingLayer { */ export const ORDER_IN_LAYER_MAX = 100000; ``` -4. 在需要排序的UI渲染器上(Sprite或者Label等),添加SortingGroup组件,并设置排序层和排序值 ![QQ截图20230205173334|524x176](./docs/images/QQ截图20230205173334.png) -和Unity不同的是`Order In Layer`不必是整数,这里可以使用小数。 +4. 在需要排序的UI节点上,添加`SortingGroup`组件,并设置排序层和排序值,其配置会应用于当前节点和子孙节点上的UI渲染器; ![QQ截图20230205173334|524x176](./docs/images/QQ截图20230205173334.png) + + UI渲染器的优先级以当前节点或者最近的祖先节点上的`SortingGroup`组件配置为准,如果没有则默认为`0`。 + + 和Unity不同的是`Order In Layer`不必是整数,这里可以使用小数。 不同`Sorting Layer`的情况下,`Sorting Layer`枚举越小越先渲染;相同`Sorting Layer`的情况下,`Order In Layer`的值越小越先渲染。 # 注意