diff --git a/cocos/2d/assembler/label/text-processing.ts b/cocos/2d/assembler/label/text-processing.ts index 92a90ab2c63..6f5e6dd5add 100644 --- a/cocos/2d/assembler/label/text-processing.ts +++ b/cocos/2d/assembler/label/text-processing.ts @@ -44,6 +44,7 @@ import { isUnicodeSpace, safeMeasureText, } from '../../utils/text-utils'; +import type { Batcher2D } from '../../renderer/batcher-2d'; const Alignment = [ 'left', // macro.TextAlignment.LEFT @@ -568,9 +569,9 @@ export class TextProcessing { // since `tex.reset(...)` will reset `gfxTexture` and `gfxSampler`. // The other non-JSB branch uses `tex.getHash()` which returns the hash value that will not be // changed after `tex.reset(...)`, so there will be no problems for non-JSB branch. - cclegacy.director.root.batcher2D._releaseDescriptorSetCache(oldGfxTexture, oldGfxSampler); + (cclegacy.director.root.batcher2D as Batcher2D)._releaseDescriptorSetCache(oldGfxTexture, oldGfxSampler); } else { - cclegacy.director.root.batcher2D._releaseDescriptorSetCache(tex.getHash()); + (cclegacy.director.root.batcher2D as Batcher2D)._releaseDescriptorSetCache(tex.getHash()); } } } diff --git a/cocos/2d/components/rich-text.ts b/cocos/2d/components/rich-text.ts index c352e867c34..939c8c17982 100644 --- a/cocos/2d/components/rich-text.ts +++ b/cocos/2d/components/rich-text.ts @@ -23,7 +23,7 @@ THE SOFTWARE. */ -import { ccclass, executeInEditMode, executionOrder, help, menu, tooltip, multiline, type, displayOrder, serializable } from 'cc.decorator'; +import { ccclass, executeInEditMode, executionOrder, help, menu, multiline, type, displayOrder, serializable, editable } from 'cc.decorator'; import { DEBUG, DEV, EDITOR } from 'internal:constants'; import { Font, SpriteAtlas, TTFFont, SpriteFrame } from '../assets'; import { EventTouch } from '../../input/types'; @@ -162,7 +162,6 @@ export class RichText extends Component { * 富文本显示的文本内容。 */ @multiline - @tooltip('i18n:richtext.string') get string (): string { return this._string; } @@ -183,7 +182,6 @@ export class RichText extends Component { * 文本内容的水平对齐方式。 */ @type(HorizontalTextAlignment) - @tooltip('i18n:richtext.horizontal_align') get horizontalAlign (): HorizontalTextAlignment { return this._horizontalAlign; } @@ -206,7 +204,6 @@ export class RichText extends Component { * 文本内容的竖直对齐方式。 */ @type(VerticalTextAlignment) - @tooltip('i18n:richtext.vertical_align') get verticalAlign (): VerticalTextAlignment { return this._verticalAlign; } @@ -228,7 +225,7 @@ export class RichText extends Component { * @zh * 富文本字体大小。 */ - @tooltip('i18n:richtext.font_size') + @editable get fontSize (): number { return this._fontSize; } @@ -270,7 +267,7 @@ export class RichText extends Component { * @zh * 富文本定制系统字体。 */ - @tooltip('i18n:richtext.font_family') + @editable get fontFamily (): string { return this._fontFamily; } @@ -289,7 +286,6 @@ export class RichText extends Component { * 富文本定制字体。 */ @type(Font) - @tooltip('i18n:richtext.font') get font (): TTFFont | null { return this._font; } @@ -313,12 +309,11 @@ export class RichText extends Component { /** * @en - * Whether use system font name or not. + * Whether to use system font name or not. * * @zh * 是否使用系统字体。 */ - @tooltip('i18n:richtext.use_system_font') @displayOrder(12) get useSystemFont (): boolean { return this._isSystemFontUsed; @@ -350,7 +345,6 @@ export class RichText extends Component { * 文本缓存模式, 该模式只支持系统字体。 */ @type(CacheMode) - @tooltip('i18n:richtext.cache_mode') get cacheMode (): CacheMode { return this._cacheMode; } @@ -369,7 +363,7 @@ export class RichText extends Component { * @zh * 富文本的最大宽度。 */ - @tooltip('i18n:richtext.max_width') + @editable get maxWidth (): number { return this._maxWidth; } @@ -391,7 +385,7 @@ export class RichText extends Component { * @zh * 富文本行高。 */ - @tooltip('i18n:richtext.line_height') + @editable get lineHeight (): number { return this._lineHeight; } @@ -414,7 +408,6 @@ export class RichText extends Component { * 对于 img 标签里面的 src 属性名称,都需要在 imageAtlas 里面找到一个有效的 spriteFrame,否则 img tag 会判定为无效。 */ @type(SpriteAtlas) - @tooltip('i18n:richtext.image_atlas') get imageAtlas (): SpriteAtlas | null { return this._imageAtlas; } @@ -437,7 +430,7 @@ export class RichText extends Component { * @zh * 选中此选项后,RichText 将阻止节点边界框中的所有输入事件(鼠标和触摸),从而防止输入事件穿透到底层节点。 */ - @tooltip('i18n:richtext.handleTouchEvent') + @editable get handleTouchEvent (): boolean { return this._handleTouchEvent; } diff --git a/cocos/2d/components/sprite.ts b/cocos/2d/components/sprite.ts index d1c4155cbf6..794bd6eafd9 100644 --- a/cocos/2d/components/sprite.ts +++ b/cocos/2d/components/sprite.ts @@ -34,6 +34,7 @@ import { PixelFormat } from '../../asset/assets/asset-enum'; import { TextureBase } from '../../asset/assets/texture-base'; import { Material, RenderTexture } from '../../asset/assets'; import { NodeEventType } from '../../scene-graph/node-event'; +import assetManager from '../../asset/asset-manager/asset-manager'; /** * @en @@ -727,7 +728,7 @@ export class Sprite extends UIRenderer { } if (!this.spriteAtlas || this.spriteAtlas.uuid !== spriteFrame.atlasUuid) { - cclegacy.assetManager.loadAny(spriteFrame.atlasUuid, (err: Error, asset: SpriteAtlas) => { + assetManager.loadAny(spriteFrame.atlasUuid, (err: Error, asset: SpriteAtlas) => { if (err) { this.spriteAtlas = null; error(err); diff --git a/cocos/2d/framework/render-root-2d.ts b/cocos/2d/framework/render-root-2d.ts index 2c8c2196b47..46e038a5c34 100644 --- a/cocos/2d/framework/render-root-2d.ts +++ b/cocos/2d/framework/render-root-2d.ts @@ -27,6 +27,7 @@ import { ccclass, disallowMultiple, executeInEditMode, import { Component } from '../../scene-graph/component'; import { cclegacy } from '../../core'; import { UITransform } from './ui-transform'; +import type { Batcher2D } from '../renderer/batcher-2d'; /** * @en The entry node for 2D object data collection, all 2D rendering objects need to be rendered under the RenderRoot node. @@ -41,14 +42,14 @@ import { UITransform } from './ui-transform'; @executeInEditMode export class RenderRoot2D extends Component { public onEnable (): void { - cclegacy.director.root!.batcher2D.addScreen(this); + (cclegacy.director.root!.batcher2D as Batcher2D).addScreen(this); } public onDisable (): void { - cclegacy.director.root!.batcher2D.removeScreen(this); + (cclegacy.director.root!.batcher2D as Batcher2D).removeScreen(this); } public onDestroy (): void { - cclegacy.director.root!.batcher2D.removeScreen(this); + (cclegacy.director.root!.batcher2D as Batcher2D).removeScreen(this); } } diff --git a/cocos/2d/framework/sprite-renderer.ts b/cocos/2d/framework/sprite-renderer.ts index 69d4547fcc5..194d4551b06 100644 --- a/cocos/2d/framework/sprite-renderer.ts +++ b/cocos/2d/framework/sprite-renderer.ts @@ -28,7 +28,7 @@ import { Material } from '../../asset/assets'; import { Color, Vec2, cclegacy } from '../../core'; import { ModelLocalBindings } from '../../rendering/define'; import { Model } from '../../render-scene/scene'; -import { Root } from '../../root'; +import type { Root } from '../../root'; import { TransformBit } from '../../scene-graph/node-enum'; import { SpriteFrame } from '../assets/sprite-frame'; import { ModelRenderer } from '../../misc'; @@ -138,7 +138,7 @@ export class SpriteRenderer extends ModelRenderer { public onDestroy (): void { if (this._model) { - cclegacy.director.root.destroyModel(this._model); + (cclegacy.director.root as Root).destroyModel(this._model); this._model = null; this._models.length = 0; } diff --git a/cocos/2d/framework/ui-renderer.ts b/cocos/2d/framework/ui-renderer.ts index c4e1cd1d32f..1bb55a0e652 100644 --- a/cocos/2d/framework/ui-renderer.ts +++ b/cocos/2d/framework/ui-renderer.ts @@ -168,7 +168,6 @@ export class UIRenderer extends Renderer { */ @type(Material) @displayOrder(0) - @displayName('CustomMaterial') @disallowAnimation get customMaterial (): Material | null { return this._customMaterial; diff --git a/cocos/2d/renderer/batcher-2d.ts b/cocos/2d/renderer/batcher-2d.ts index 3a272c470ce..c8a84fb3669 100644 --- a/cocos/2d/renderer/batcher-2d.ts +++ b/cocos/2d/renderer/batcher-2d.ts @@ -50,6 +50,7 @@ import { scene } from '../../render-scene'; import { builtinResMgr } from '../../asset/asset-manager'; import { RenderingSubMesh } from '../../asset/assets'; import { IAssembler } from './base'; +import type { Director } from '../../game/director'; const _dsInfo = new DescriptorSetInfo(null!); const m4_1 = new Mat4(); @@ -160,7 +161,7 @@ export class Batcher2D implements IBatcher { StencilManager.sharedManager!.destroy(); if (this._maskClearModel && this._maskModelMesh) { - cclegacy.director.root.destroyModel(this._maskClearModel); + (cclegacy.director.root as Root).destroyModel(this._maskClearModel); this._maskModelMesh.destroy(); } if (this._maskClearMtl) { @@ -571,7 +572,7 @@ export class Batcher2D implements IBatcher { dssHash = StencilManager.sharedManager!.getStencilHash(comp.stencilStage); } - const stamp: number = cclegacy.director.getTotalFrames(); + const stamp: number = (cclegacy.director as Director).getTotalFrames(); if (model) { model.updateTransform(stamp); model.updateUBOs(stamp); @@ -866,7 +867,10 @@ export class Batcher2D implements IBatcher { // TODO: Not a good way to do the job // Although it's a private method, it is invoked in text-processing.ts and texture-base.ts // by legacyCC.director.root.batcher2D._releaseDescriptorSetCache - private _releaseDescriptorSetCache (textureHash: number | Texture, sampler: Sampler | null = null): void { + /** + * @engineInternal + */ + public _releaseDescriptorSetCache (textureHash: number | Texture | null, sampler: Sampler | null = null): void { if (JSB) { this._nativeObj.releaseDescriptorSetCache(textureHash as Texture, sampler as Sampler); } else { @@ -879,7 +883,7 @@ export class Batcher2D implements IBatcher { if (!this._maskClearModel) { this._maskClearMtl = builtinResMgr.get('default-clear-stencil'); - this._maskClearModel = cclegacy.director.root.createModel(scene.Model); + this._maskClearModel = (cclegacy.director.root as Root).createModel(scene.Model); const stride = getAttributeStride(vfmt); const gfxDevice: Device = deviceManager.gfxDevice; const vertexBuffer = gfxDevice.createBuffer(new BufferInfo( @@ -903,7 +907,7 @@ export class Batcher2D implements IBatcher { this._maskModelMesh = new RenderingSubMesh([vertexBuffer], vfmt, PrimitiveMode.TRIANGLE_LIST, indexBuffer); this._maskModelMesh.subMeshIdx = 0; - this._maskClearModel!.initSubModel(0, this._maskModelMesh, this._maskClearMtl); + this._maskClearModel.initSubModel(0, this._maskModelMesh, this._maskClearMtl); } } @@ -925,7 +929,7 @@ export class Batcher2D implements IBatcher { } const model = this._maskClearModel!; - const stamp: number = cclegacy.director.getTotalFrames(); + const stamp: number = (cclegacy.director as Director).getTotalFrames(); if (model) { model.updateTransform(stamp); model.updateUBOs(stamp); diff --git a/cocos/3d/framework/mesh-renderer.ts b/cocos/3d/framework/mesh-renderer.ts index 1fbefc14ecc..283237ef239 100644 --- a/cocos/3d/framework/mesh-renderer.ts +++ b/cocos/3d/framework/mesh-renderer.ts @@ -43,6 +43,7 @@ import { getPhaseID } from '../../rendering/pass-phase'; import { SubModel } from '../../render-scene/scene'; import { isEnableEffect } from '../../rendering/define'; import type { Model } from '../../render-scene/scene'; +import type { ReflectionProbeManager } from '../reflection-probe'; const { ccclass, help, executeInEditMode, executionOrder, menu, visible, type, formerlySerializedAs, serializable, editable, disallowAnimation } = _decorator; @@ -646,7 +647,7 @@ export class MeshRenderer extends ModelRenderer { public onDestroy (): void { if (this._model) { - cclegacy.director.root.destroyModel(this._model); + (cclegacy.director.root as Root).destroyModel(this._model); this._model = null; this._models.length = 0; } @@ -979,7 +980,7 @@ export class MeshRenderer extends ModelRenderer { model.visFlags = this.visibility; model.node = model.transform = this.node; this._models.length = 0; - this._models.push(this._model); + this._models.push(model); if (this._morphInstance && model instanceof MorphModel) { model.setMorphRendering(this._morphInstance); } @@ -1167,16 +1168,18 @@ export class MeshRenderer extends ModelRenderer { protected onReflectionProbeChanged (): void { this._updateUseReflectionProbe(); this._onUpdateLocalShadowBiasAndProbeId(); + const reflectionProbeManager = cclegacy.internal.reflectionProbeManager as ReflectionProbeManager; + const model = this._model!; if (this.bakeSettings.reflectionProbe === ReflectionProbeType.BAKED_CUBEMAP || this.bakeSettings.reflectionProbe === ReflectionProbeType.BLEND_PROBES || this.bakeSettings.reflectionProbe === ReflectionProbeType.BLEND_PROBES_AND_SKYBOX) { - cclegacy.internal.reflectionProbeManager.selectReflectionProbe(this._model); - if (!cclegacy.internal.reflectionProbeManager.getUsedReflectionProbe(this._model, false)) { + reflectionProbeManager.selectReflectionProbe(model); + if (!reflectionProbeManager.getUsedReflectionProbe(model, false)) { warnID(16302); } } else if (this.bakeSettings.reflectionProbe === ReflectionProbeType.PLANAR_REFLECTION) { - cclegacy.internal.reflectionProbeManager.selectPlanarReflectionProbe(this._model); - if (!cclegacy.internal.reflectionProbeManager.getUsedReflectionProbe(this._model, true)) { + reflectionProbeManager.selectPlanarReflectionProbe(model); + if (!reflectionProbeManager.getUsedReflectionProbe(model, true)) { warnID(16302); } } diff --git a/cocos/3d/lights/light-component.ts b/cocos/3d/lights/light-component.ts index 332bed635e6..dfc4035e119 100644 --- a/cocos/3d/lights/light-component.ts +++ b/cocos/3d/lights/light-component.ts @@ -272,7 +272,7 @@ export class Light extends Component { protected _destroyLight (): void { if (this._light) { - cclegacy.director.root.recycleLight(this._light); + (cclegacy.director.root as Root).recycleLight(this._light); this._light = null; } } diff --git a/cocos/3d/reflection-probe/reflection-probe-manager.ts b/cocos/3d/reflection-probe/reflection-probe-manager.ts index 6a8e788dc48..4938a777ac8 100644 --- a/cocos/3d/reflection-probe/reflection-probe-manager.ts +++ b/cocos/3d/reflection-probe/reflection-probe-manager.ts @@ -33,6 +33,7 @@ import { Texture } from '../../gfx'; import { Camera, Model } from '../../render-scene/scene'; import { ProbeType, ReflectionProbe } from '../../render-scene/scene/reflection-probe'; import { Layers } from '../../scene-graph/layers'; +import type { Director } from '../../game/director'; const REFLECTION_PROBE_DEFAULT_MASK = Layers.makeMaskExclude([Layers.BitMask.UI_2D, Layers.BitMask.UI_3D, Layers.BitMask.GIZMOS, Layers.BitMask.EDITOR, Layers.BitMask.SCENE_GIZMO, Layers.BitMask.PROFILER, Layers.Enum.IGNORE_RAYCAST]); @@ -74,7 +75,7 @@ export class ReflectionProbeManager { */ public registerEvent (): void { if (!this._registeredEvent) { - cclegacy.director.on(cclegacy.Director.EVENT_BEFORE_UPDATE, this.onUpdateProbes, this); + cclegacy.director.on(cclegacy.DirectorEvent.BEFORE_UPDATE, this.onUpdateProbes, this); this._registeredEvent = true; } } diff --git a/cocos/3d/skinned-mesh-renderer/skinned-mesh-renderer.ts b/cocos/3d/skinned-mesh-renderer/skinned-mesh-renderer.ts index c59dfa62b93..0d26d775d92 100644 --- a/cocos/3d/skinned-mesh-renderer/skinned-mesh-renderer.ts +++ b/cocos/3d/skinned-mesh-renderer/skinned-mesh-renderer.ts @@ -35,6 +35,7 @@ import type { SkeletalAnimation } from '../skeletal-animation'; import { cclegacy, assertIsTrue } from '../../core'; import { SkinningModel } from '../models/skinning-model'; import { BakedSkinningModel } from '../models/baked-skinning-model'; +import type { Root } from '../../root'; /** * @en The skinned mesh renderer component. @@ -130,7 +131,7 @@ export class SkinnedMeshRenderer extends MeshRenderer { if (!force && this._modelType === modelType) { return; } this._modelType = modelType; if (this._model) { - cclegacy.director.root.destroyModel(this._model); + (cclegacy.director.root as Root).destroyModel(this._model); this._model = null; this._models.length = 0; this._updateModels(); diff --git a/cocos/animation/animation-clip.ts b/cocos/animation/animation-clip.ts index eca1c287722..f09feb96017 100644 --- a/cocos/animation/animation-clip.ts +++ b/cocos/animation/animation-clip.ts @@ -48,6 +48,7 @@ import { EmbeddedPlayableState, EmbeddedPlayer } from './embedded-player/embedde import { AuxiliaryCurveEntry } from './auxiliary-curve-entry'; import { removeIf } from '../core/utils/array'; import { invokeComponentMethodsEngagedInAnimationEvent } from './event/event-emitter'; +import type { DataPoolManager } from '../3d/skeletal-animation/data-pool-manager'; export declare namespace AnimationClip { export interface IEvent { @@ -410,8 +411,9 @@ export class AnimationClip extends Asset { } public destroy (): boolean { - if (cclegacy.director.root?.dataPoolManager) { - (cclegacy.director.root.dataPoolManager).releaseAnimationClip(this); + const dataPoolManager = cclegacy.director.root?.dataPoolManager as (DataPoolManager | null); + if (dataPoolManager) { + dataPoolManager.releaseAnimationClip(this); } SkelAnimDataHub.destroy(this); return super.destroy(); diff --git a/cocos/asset/asset-manager/asset-manager.ts b/cocos/asset/asset-manager/asset-manager.ts index 9b593bb4caf..e59e71ff836 100644 --- a/cocos/asset/asset-manager/asset-manager.ts +++ b/cocos/asset/asset-manager/asset-manager.ts @@ -39,7 +39,7 @@ import packManager from './pack-manager'; import parser, { Parser } from './parser'; import { Pipeline } from './pipeline'; import preprocess from './preprocess'; -import { releaseManager } from './release-manager'; +import { ReleaseManager, releaseManager } from './release-manager'; import RequestItem from './request-item'; import { presets, @@ -307,7 +307,7 @@ export class AssetManager { */ public references = references; - private _releaseManager = releaseManager; + private _releaseManager$ = releaseManager; private _files$ = files; private _parsed$ = parsed; private _parsePipeline$ = BUILD ? null : new Pipeline('parse existing json', [this.loadPipe]); @@ -331,6 +331,13 @@ export class AssetManager { private constructor () {} + /** + * @engineInternal + */ + public getReleaseManager (): ReleaseManager { + return this._releaseManager$; + } + /** * @en * The builtin 'main' bundle. @@ -422,7 +429,7 @@ export class AssetManager { this._files$.clear(); this._parsed$.clear(); - this._releaseManager.init(); + this._releaseManager$.init(); this.assets.clear(); this.bundles.clear(); this.packManager.init(); diff --git a/cocos/asset/asset-manager/bundle.ts b/cocos/asset/asset-manager/bundle.ts index 72b8a8349ac..cde42eb778e 100644 --- a/cocos/asset/asset-manager/bundle.ts +++ b/cocos/asset/asset-manager/bundle.ts @@ -31,6 +31,7 @@ import { releaseManager } from './release-manager'; import RequestItem from './request-item'; import { assets, bundles, RequestType } from './shared'; import { parseLoadResArgs, parseParameters } from './utilities'; +import type { AssetManager } from './asset-manager'; /** * @en @@ -244,7 +245,7 @@ export default class Bundle { ): void { const { type: _type, onProgress: onProg, onComplete: onComp } = parseLoadResArgs(type, onProgress, onComplete); const options = { __requestType__: RequestType.PATH, type: _type, bundle: this.name, __outputAsArray__: Array.isArray(paths) }; - cclegacy.assetManager.loadAny(paths, options, onProg, onComp); + (cclegacy.assetManager as AssetManager).loadAny(paths, options, onProg, onComp); } /** @@ -307,7 +308,7 @@ export default class Bundle { onComplete?: ((err: Error | null, data: RequestItem[]) => void) | null, ): void { const { type: _type, onProgress: onProg, onComplete: onComp } = parseLoadResArgs(type, onProgress, onComplete); - cclegacy.assetManager.preloadAny(paths, { __requestType__: RequestType.PATH, type: _type, bundle: this.name }, onProg, onComp); + (cclegacy.assetManager as AssetManager).preloadAny(paths, { __requestType__: RequestType.PATH, type: _type, bundle: this.name }, onProg, onComp); } /** @@ -363,7 +364,7 @@ export default class Bundle { onComplete?: ((err: Error | null, data: T[]) => void) | null, ): void { const { type: _type, onProgress: onProg, onComplete: onComp } = parseLoadResArgs(type, onProgress, onComplete); - cclegacy.assetManager.loadAny(dir, { __requestType__: RequestType.DIR, type: _type, bundle: this.name, __outputAsArray__: true }, onProg, onComp); + (cclegacy.assetManager as AssetManager).loadAny(dir, { __requestType__: RequestType.DIR, type: _type, bundle: this.name, __outputAsArray__: true }, onProg, onComp); } /** @@ -420,7 +421,7 @@ export default class Bundle { onComplete?: ((err: Error | null, data: RequestItem[]) => void)| null, ): void { const { type: _type, onProgress: onProg, onComplete: onComp } = parseLoadResArgs(type, onProgress, onComplete); - cclegacy.assetManager.preloadAny(dir, { __requestType__: RequestType.DIR, type: _type, bundle: this.name }, onProg, onComp); + (cclegacy.assetManager as AssetManager).preloadAny(dir, { __requestType__: RequestType.DIR, type: _type, bundle: this.name }, onProg, onComp); } /** @@ -464,12 +465,12 @@ export default class Bundle { opts.preset = opts.preset || 'scene'; opts.bundle = this.name; - cclegacy.assetManager.loadAny({ scene: sceneName }, opts, onProg, (err, sceneAsset): void => { + (cclegacy.assetManager as AssetManager).loadAny({ scene: sceneName }, opts, onProg, (err, sceneAsset: SceneAsset): void => { if (err) { error(err.message, err.stack); } else if (sceneAsset.scene) { const scene = sceneAsset.scene; - scene._id = sceneAsset._uuid; + scene.id = sceneAsset._uuid; scene.name = sceneAsset.name; } else { err = new Error(`The asset ${sceneAsset._uuid} is not a scene`); @@ -522,7 +523,7 @@ export default class Bundle { const { options: opts, onProgress: onProg, onComplete: onComp } = parseParameters<((err?: Error | null) => void)>(options, onProgress, onComplete); opts.bundle = this.name; - cclegacy.assetManager.preloadAny({ scene: sceneName }, opts, onProg, (err): void => { + (cclegacy.assetManager as AssetManager).preloadAny({ scene: sceneName }, opts, onProg, (err): void => { if (err) { errorID(1210, sceneName, err.message); } diff --git a/cocos/asset/asset-manager/downloader.ts b/cocos/asset/asset-manager/downloader.ts index 034e197c3ac..0f7283bdc91 100644 --- a/cocos/asset/asset-manager/downloader.ts +++ b/cocos/asset/asset-manager/downloader.ts @@ -32,6 +32,7 @@ import { files } from './shared'; import { retry, RetryFunction, urlAppendTimestamp } from './utilities'; import { IConfigOption } from './config'; import { CCON, parseCCONJson, decodeCCONBinary } from '../../serialization/ccon'; +import type { AssetManager } from './asset-manager'; export type DownloadHandler = (url: string, options: Record, onComplete: ((err: Error | null, data?: any) => void)) => void; @@ -489,7 +490,7 @@ export class Downloader { * @deprecated loader.downloader.loadSubpackage is deprecated, please use AssetManager.loadBundle instead. */ public loadSubpackage (name: string, completeCallback?: ((err?: Error | null) => void)): void { - cclegacy.assetManager.loadBundle(name, null, completeCallback); + (cclegacy.assetManager as AssetManager).loadBundle(name, null, completeCallback); } private constructor () {} diff --git a/cocos/asset/asset-manager/release-manager.ts b/cocos/asset/asset-manager/release-manager.ts index 6ebfe9052d9..f6d320f2d5f 100644 --- a/cocos/asset/asset-manager/release-manager.ts +++ b/cocos/asset/asset-manager/release-manager.ts @@ -112,7 +112,7 @@ function checkCircularReference (asset: Asset): number { return refs[asset._uuid]; } -class ReleaseManager { +export class ReleaseManager { private _persistNodeDeps$ = new Cache(); private _toDelete$ = new Cache(); private _eventListener$ = false; diff --git a/cocos/asset/asset-manager/utilities.ts b/cocos/asset/asset-manager/utilities.ts index 6d6a55d2bd1..c962b4c3087 100644 --- a/cocos/asset/asset-manager/utilities.ts +++ b/cocos/asset/asset-manager/utilities.ts @@ -32,6 +32,7 @@ import { isScene } from './helper'; import RequestItem from './request-item'; import { assets, references } from './shared'; import Task from './task'; +import type { AssetManager } from './asset-manager'; let defaultProgressCallback: ((finished: number, total: number, item: RequestItem) => void) | null = null; @@ -143,7 +144,7 @@ export function setProperties (uuid: string, asset: Asset, assetsMap: Record 0) { - return this.colorTextures[0]!.width; + return this.colorTextures[0]?.width ?? this._width$; } else if (this.depthStencilTexture) { return this.depthStencilTexture.width; } @@ -66,7 +66,7 @@ export abstract class Framebuffer extends GFXObject { public get height (): number { if (this.colorTextures.length > 0) { - return this.colorTextures[0]!.height; + return this.colorTextures[0]?.height ?? this._height$; } else if (this.depthStencilTexture) { return this.depthStencilTexture.height; } diff --git a/cocos/gi/light-probe/sh.ts b/cocos/gi/light-probe/sh.ts index 6ac5d872339..9a835463ce0 100644 --- a/cocos/gi/light-probe/sh.ts +++ b/cocos/gi/light-probe/sh.ts @@ -79,8 +79,7 @@ export class LightProbeSampler { export class SH { private static LMAX = 2; - private static basisFunctions: { (v: Vec3): number }[] = - [ + private static basisFunctions: { (v: Vec3): number }[] = [ (v: Vec3): number => 0.282095, // 0.5 * Math.sqrt(1.0 / Math.PI) (v: Vec3): number => 0.488603 * v.y, // 0.5 * Math.sqrt(3.0 / Math.PI) * v.y (v: Vec3): number => 0.488603 * v.z, // 0.5 * Math.sqrt(3.0 / Math.PI) * v.z @@ -92,8 +91,7 @@ export class SH { (v: Vec3): number => 0.546274 * (v.x * v.x - v.y * v.y), // 0.25 * Math.sqrt(15.0 / Math.PI) * (v.x * v.x - v.y * v.y) ]; - private static basisOverPI: number[] = - [ + private static basisOverPI: number[] = [ 0.0897936, // 0.282095 / Math.PI 0.155527, // 0.488603 / Math.PI 0.155527, // 0.488603 / Math.PI diff --git a/cocos/input/types/event/event-mouse.ts b/cocos/input/types/event/event-mouse.ts index 551a0e66c37..6bbbb902553 100644 --- a/cocos/input/types/event/event-mouse.ts +++ b/cocos/input/types/event/event-mouse.ts @@ -26,6 +26,7 @@ import { Event } from './event'; import { Vec2, cclegacy } from '../../../core'; import { SystemEventTypeUnion } from '../event-enum'; +import type { View } from '../../../ui/view'; /** * @en The mouse event @@ -218,7 +219,7 @@ export class EventMouse extends Event { out = new Vec2(); } - Vec2.set(out, this._x, cclegacy.view._designResolutionSize.height - this._y); + Vec2.set(out, this._x, (cclegacy.view as View)._designResolutionSize.height - this._y); return out; } @@ -233,7 +234,7 @@ export class EventMouse extends Event { } Vec2.set(out, this._x, this._y); - cclegacy.view._convertToUISpace(out); + (cclegacy.view as View)._convertToUISpace(out); return out; } @@ -262,7 +263,7 @@ export class EventMouse extends Event { } Vec2.set(out, this._prevX, this._prevY); - cclegacy.view._convertToUISpace(out); + (cclegacy.view as View)._convertToUISpace(out); return out; } @@ -305,8 +306,8 @@ export class EventMouse extends Event { if (!out) { out = new Vec2(); } - - Vec2.set(out, (this._x - this._prevX) / cclegacy.view.getScaleX(), (this._y - this._prevY) / cclegacy.view.getScaleY()); + const view = cclegacy.view as View; + Vec2.set(out, (this._x - this._prevX) / view.getScaleX(), (this._y - this._prevY) / view.getScaleY()); return out; } @@ -315,7 +316,7 @@ export class EventMouse extends Event { * @zh 获取鼠标距离上一次事件移动在 UI 坐标系下的 X 轴距离。 */ public getUIDeltaX (): number { - return (this._x - this._prevX) / cclegacy.view.getScaleX(); + return (this._x - this._prevX) / (cclegacy.view as View).getScaleX(); } /** @@ -323,7 +324,7 @@ export class EventMouse extends Event { * @zh 获取鼠标距离上一次事件移动在 UI 坐标系下的 Y 轴距离。 */ public getUIDeltaY (): number { - return (this._y - this._prevY) / cclegacy.view.getScaleY(); + return (this._y - this._prevY) / (cclegacy.view as View).getScaleY(); } /** @@ -364,8 +365,9 @@ export class EventMouse extends Event { * @zh 获取鼠标当前 X 轴位置。 */ public getUILocationX (): number { - const viewport = cclegacy.view.getViewportRect(); - return (this._x - viewport.x) / cclegacy.view.getScaleX(); + const view = cclegacy.view as View; + const viewport = view.getViewportRect(); + return (this._x - viewport.x) / view.getScaleX(); } /** @@ -373,8 +375,9 @@ export class EventMouse extends Event { * @zh 获取鼠标当前 Y 轴位置。 */ public getUILocationY (): number { - const viewport = cclegacy.view.getViewportRect(); - return (this._y - viewport.y) / cclegacy.view.getScaleY(); + const view = cclegacy.view as View; + const viewport = view.getViewportRect(); + return (this._y - viewport.y) / view.getScaleY(); } } diff --git a/cocos/input/types/touch.ts b/cocos/input/types/touch.ts index e49b9d69deb..10b847d10b1 100644 --- a/cocos/input/types/touch.ts +++ b/cocos/input/types/touch.ts @@ -24,6 +24,7 @@ */ import { Vec2, cclegacy } from '../../core'; +import type { View } from '../../ui/view'; const _vec2 = new Vec2(); /** @@ -92,7 +93,7 @@ export class Touch { } out.set(this._point$.x, this._point$.y); - cclegacy.view._convertToUISpace(out); + (cclegacy.view as View)._convertToUISpace(out); return out; } @@ -101,8 +102,9 @@ export class Touch { * @zh 获取当前触点在 UI 坐标系中 X 轴位置。 */ public getUILocationX (): number { - const viewport = cclegacy.view.getViewportRect(); - return (this._point$.x - viewport.x) / cclegacy.view.getScaleX(); + const view = cclegacy.view as View; + const viewport = view.getViewportRect(); + return (this._point$.x - viewport.x) / view.getScaleX(); } /** @@ -110,8 +112,9 @@ export class Touch { * @zh 获取当前触点在 UI 坐标系中 Y 轴位置。 */ public getUILocationY (): number { - const viewport = cclegacy.view.getViewportRect(); - return (this._point$.y - viewport.y) / cclegacy.view.getScaleY(); + const view = cclegacy.view as View; + const viewport = view.getViewportRect(); + return (this._point$.y - viewport.y) / view.getScaleY(); } /** @@ -139,7 +142,7 @@ export class Touch { } out.set(this._prevPoint$.x, this._prevPoint$.y); - cclegacy.view._convertToUISpace(out); + (cclegacy.view as View)._convertToUISpace(out); return out; } @@ -168,7 +171,7 @@ export class Touch { } out.set(this._startPoint$.x, this._startPoint$.y); - cclegacy.view._convertToUISpace(out); + (cclegacy.view as View)._convertToUISpace(out); return out; } @@ -199,7 +202,8 @@ export class Touch { _vec2.set(this._point$); _vec2.subtract(this._prevPoint$); - out.set(cclegacy.view.getScaleX() as number, cclegacy.view.getScaleY() as number); + const view = cclegacy.view as View; + out.set(view.getScaleX(), view.getScaleY()); Vec2.divide(out, _vec2, out); return out; } @@ -214,7 +218,7 @@ export class Touch { out = new Vec2(); } - out.set(this._point$.x, cclegacy.view._designResolutionSize.height - this._point$.y); + out.set(this._point$.x, (cclegacy.view as View)._designResolutionSize.height - this._point$.y); return out; } @@ -228,7 +232,7 @@ export class Touch { out = new Vec2(); } - out.set(this._prevPoint$.x, cclegacy.view._designResolutionSize.height - this._prevPoint$.y); + out.set(this._prevPoint$.x, (cclegacy.view as View)._designResolutionSize.height - this._prevPoint$.y); return out; } @@ -242,7 +246,7 @@ export class Touch { out = new Vec2(); } - out.set(this._startPoint$.x, cclegacy.view._designResolutionSize.height - this._startPoint$.y); + out.set(this._startPoint$.x, (cclegacy.view as View)._designResolutionSize.height - this._startPoint$.y); return out; } @@ -267,7 +271,6 @@ export class Touch { this._id = id; if (!this._startPointCaptured$) { this._startPoint$ = new Vec2(this._point$); - // cc.view._convertToUISpace(this._startPoint); this._startPointCaptured$ = true; } } diff --git a/cocos/misc/camera-component.ts b/cocos/misc/camera-component.ts index e74b42b8d3d..c7275fbe358 100644 --- a/cocos/misc/camera-component.ts +++ b/cocos/misc/camera-component.ts @@ -41,6 +41,8 @@ import { ClearFlagBit } from '../gfx'; import { PostProcess } from '../rendering/post-process/components/post-process'; import { property } from '../core/data/class-decorator'; import type { Ray } from '../core/geometry'; +import type { View } from '../ui/view'; +import type { Root } from '../root'; const _temp_vec3_1 = new Vec3(); @@ -495,7 +497,7 @@ export class Camera extends Component { this._updateTargetTexture(); if (!value && this._camera) { - this._camera.changeTargetWindow(EDITOR ? cclegacy.director.root.tempWindow : null); + this._camera.changeTargetWindow(EDITOR ? (cclegacy.director.root as Root).tempWindow : null); this._camera.isWindowSize = true; } this.node.emit(CameraEvent.TARGET_TEXTURE_CHANGE, this); @@ -549,8 +551,8 @@ export class Camera extends Component { set inEditorMode (value) { this._inEditorMode = value; if (this._camera) { - this._camera.changeTargetWindow(value ? cclegacy.director.root && cclegacy.director.root.mainWindow - : cclegacy.director.root && cclegacy.director.root.tempWindow); + const root = cclegacy.director.root as Root; + this._camera.changeTargetWindow(value ? root && root.mainWindow : root && root.tempWindow); } } @@ -678,11 +680,12 @@ export class Camera extends Component { this.worldToScreen(wpos, _temp_vec3_1); const cmp = uiNode.getComponent('cc.UITransform') as UITransform; - const designSize = cclegacy.view.getVisibleSize(); + const view = cclegacy.view as View; + const designSize = view.getVisibleSize(); const xoffset = _temp_vec3_1.x - this._camera.width * 0.5; const yoffset = _temp_vec3_1.y - this._camera.height * 0.5; - _temp_vec3_1.x = xoffset / cclegacy.view.getScaleX() + designSize.width * 0.5; - _temp_vec3_1.y = yoffset / cclegacy.view.getScaleY() + designSize.height * 0.5; + _temp_vec3_1.x = xoffset / view.getScaleX() + designSize.width * 0.5; + _temp_vec3_1.y = yoffset / view.getScaleY() + designSize.height * 0.5; if (cmp) { cmp.convertToNodeSpaceAR(_temp_vec3_1, out); @@ -696,8 +699,8 @@ export class Camera extends Component { */ public _createCamera (): void { if (!this._camera) { - this._camera = (cclegacy.director.root).createCamera(); - this._camera!.initialize({ + this._camera = (cclegacy.director.root as Root).createCamera(); + this._camera.initialize({ name: this.node.name, node: this.node, projection: this._projection, @@ -708,22 +711,22 @@ export class Camera extends Component { trackingType: this.trackingType, }); - this._camera!.setViewportInOrientedSpace(this._rect); - this._camera!.fovAxis = this._fovAxis; - this._camera!.fov = toRadian(this._fov); - this._camera!.orthoHeight = this._orthoHeight; - this._camera!.nearClip = this._near; - this._camera!.farClip = this._far; - this._camera!.clearColor = this._color; - this._camera!.clearDepth = this._depth; - this._camera!.clearStencil = this._stencil; - this._camera!.clearFlag = this._clearFlags; - this._camera!.visibility = this._visibility; - this._camera!.aperture = this._aperture; - this._camera!.shutter = this._shutter; - this._camera!.iso = this._iso; - this._camera!.postProcess = this._postProcess; - this._camera!.usePostProcess = this._usePostProcess; + this._camera.setViewportInOrientedSpace(this._rect); + this._camera.fovAxis = this._fovAxis; + this._camera.fov = toRadian(this._fov); + this._camera.orthoHeight = this._orthoHeight; + this._camera.nearClip = this._near; + this._camera.farClip = this._far; + this._camera.clearColor = this._color; + this._camera.clearDepth = this._depth; + this._camera.clearStencil = this._stencil; + this._camera.clearFlag = this._clearFlags; + this._camera.visibility = this._visibility; + this._camera.aperture = this._aperture; + this._camera.shutter = this._shutter; + this._camera.iso = this._iso; + this._camera.postProcess = this._postProcess; + this._camera.usePostProcess = this._usePostProcess; } this._updateTargetTexture(); diff --git a/cocos/misc/missing-script.ts b/cocos/misc/missing-script.ts index 92b3cc88469..ddfd15cab18 100644 --- a/cocos/misc/missing-script.ts +++ b/cocos/misc/missing-script.ts @@ -25,7 +25,7 @@ import { ccclass, inspector, editorOnly, serializable } from 'cc.decorator'; import { Component } from '../scene-graph/component'; -import { warnID, error, js, cclegacy, errorID } from '../core'; +import { warnID, js, cclegacy, errorID } from '../core'; /** * @en diff --git a/cocos/particle/renderer/particle-system-renderer-base.ts b/cocos/particle/renderer/particle-system-renderer-base.ts index 633cdc119dd..f623e0570dd 100644 --- a/cocos/particle/renderer/particle-system-renderer-base.ts +++ b/cocos/particle/renderer/particle-system-renderer-base.ts @@ -31,6 +31,7 @@ import { RenderMode } from '../enum'; import { cclegacy } from '../../core'; import { Pass } from '../../render-scene'; import type { ParticleSystem } from '../particle-system'; +import type { Root } from '../../root'; export abstract class ParticleSystemRendererBase { protected _particleSystem: ParticleSystem | null = null; @@ -83,7 +84,7 @@ export abstract class ParticleSystemRendererBase { public onDestroy (): void { if (this._model) { - cclegacy.director.root.destroyModel(this._model); + (cclegacy.director.root as Root).destroyModel(this._model); this._model = null; } } diff --git a/cocos/particle/renderer/trail.ts b/cocos/particle/renderer/trail.ts index 457b5177ad8..717fecf0cf3 100644 --- a/cocos/particle/renderer/trail.ts +++ b/cocos/particle/renderer/trail.ts @@ -36,6 +36,7 @@ import { Space, TextureMode, TrailMode } from '../enum'; import { Particle } from '../particle'; import { TransformBit } from '../../scene-graph/node-enum'; import type { ParticleSystem } from '../particle-system'; +import type { Root } from '../../root'; const PRE_TRIANGLE_INDEX = 1; const NEXT_TRIANGLE_INDEX = 1 << 2; @@ -669,7 +670,7 @@ export default class TrailModule { return; } - this._trailModel = cclegacy.director.root.createModel(scene.Model); + this._trailModel = (cclegacy.director.root as Root).createModel(scene.Model); } private rebuild (): void { diff --git a/cocos/profiler/profiler.ts b/cocos/profiler/profiler.ts index d379f5c57eb..fd1a831ce24 100644 --- a/cocos/profiler/profiler.ts +++ b/cocos/profiler/profiler.ts @@ -35,7 +35,7 @@ import { PerfCounter } from './perf-counter'; import { Pass } from '../render-scene'; import { preTransforms, System, sys, cclegacy, settings, warnID, SettingsCategory } from '../core'; import { Root } from '../root'; -import { director, DirectorEvent } from '../game'; +import { director, DirectorEvent, game } from '../game'; import { ccwindow } from '../core/global-exports'; const _characters = '0123456789. '; @@ -281,7 +281,7 @@ export class Profiler extends System { this._rootNode = new Node('PROFILER_NODE'); this._rootNode._objFlags = cclegacy.Object.Flags.DontSave | cclegacy.Object.Flags.HideInHierarchy; - cclegacy.game.addPersistRootNode(this._rootNode); + game.addPersistRootNode(this._rootNode); const managerNode = new Node('Profiler_Root'); managerNode.parent = this._rootNode; @@ -367,7 +367,7 @@ export class Profiler extends System { } const now = performance.now(); - if (cclegacy.director.isPaused()) { + if (director.isPaused()) { (this._profilerStats.frame.counter as PerfCounter).start(now); } else { (this._profilerStats.logic.counter as PerfCounter).end(now); diff --git a/cocos/render-scene/scene/camera.ts b/cocos/render-scene/scene/camera.ts index 5ddbf147879..eff16e74171 100644 --- a/cocos/render-scene/scene/camera.ts +++ b/cocos/render-scene/scene/camera.ts @@ -31,6 +31,7 @@ import { RenderWindow } from '../core/render-window'; import { GeometryRenderer } from '../../rendering/geometry-renderer'; import { PostProcess } from '../../rendering/post-process/components/post-process'; import type { Frustum } from '../../core/geometry'; +import type { Root } from '../../root'; /** * @en The enumeration type for the fixed axis of the camera. @@ -1034,7 +1035,7 @@ export class Camera { if (this._proj$ === CameraProjection.PERSPECTIVE) { if (xr && xr.isWebXR && xr.webXRWindowMap && xr.webXRMatProjs) { const wndXREye = xr.webXRWindowMap.get(this._window$); - this._matProj$.set(xr.webXRMatProjs[wndXREye]); + this._matProj$.set(xr.webXRMatProjs[wndXREye] as Mat4); } else { Mat4.perspective( this._matProj$, @@ -1134,7 +1135,8 @@ export class Camera { */ public initGeometryRenderer (): void { if (!this._geometryRenderer$) { - this._geometryRenderer$ = cclegacy.internal.GeometryRenderer ? new cclegacy.internal.GeometryRenderer() : null; + const GeometryRenderer = cclegacy.internal.GeometryRenderer; + this._geometryRenderer$ = GeometryRenderer ? new GeometryRenderer() : null; this._geometryRenderer$?.activate(this._device$); } } @@ -1181,7 +1183,7 @@ export class Camera { if (this._window$) { this._window$.detachCamera(this); } - const win = window || cclegacy.director.root.mainWindow; + const win = window || (cclegacy.director.root as Root).mainWindow; if (win) { win.attachCamera(this); this.window = win; diff --git a/cocos/render-scene/scene/model.ts b/cocos/render-scene/scene/model.ts index d5399825f23..ad2ea177771 100644 --- a/cocos/render-scene/scene/model.ts +++ b/cocos/render-scene/scene/model.ts @@ -47,6 +47,8 @@ import { TextureCube } from '../../asset/assets'; import { ShadowType } from './shadows'; import { ProbeType, ReflectionProbe } from './reflection-probe'; import { ReflectionProbeType } from '../../3d/reflection-probe/reflection-probe-enum'; +import type { SH } from '../../gi/light-probe/sh'; +import type { PipelineSceneData } from '../../rendering'; const m4_1 = new Mat4(); @@ -823,8 +825,8 @@ export class Model { } const coefficients: Vec3[] = []; - const weights = new Vec4(0.0, 0.0, 0.0, 0.0); - const lightProbes = (cclegacy.director.root as Root).pipeline.pipelineSceneData.lightProbes; + const weights = new Vec4(); + const lightProbes = (cclegacy.director.root.pipeline.pipelineSceneData as PipelineSceneData).lightProbes; this._lastWorldBoundCenter$.set(center); this._tetrahedronIndex$ = lightProbes.data!.getInterpolationWeights(center, this._tetrahedronIndex$, weights); @@ -837,8 +839,9 @@ export class Model { return; } - cclegacy.internal.SH.reduceRinging(coefficients, lightProbes.reduceRinging); - cclegacy.internal.SH.updateUBOData(this._localSHData, UBOSHEnum.SH_LINEAR_CONST_R_OFFSET, coefficients); + const SHCls: typeof SH = cclegacy.internal.SH; + SHCls.reduceRinging(coefficients, lightProbes.reduceRinging); + SHCls.updateUBOData(this._localSHData, UBOSHEnum.SH_LINEAR_CONST_R_OFFSET, coefficients); this.updateSHBuffer$(); } diff --git a/cocos/render-scene/scene/skybox.ts b/cocos/render-scene/scene/skybox.ts index 7e148c6ef98..b155d3607fd 100644 --- a/cocos/render-scene/scene/skybox.ts +++ b/cocos/render-scene/scene/skybox.ts @@ -318,7 +318,7 @@ export class Skybox { this._default = builtinResMgr.get('default-cube-texture'); if (!this._model) { - this._model = cclegacy.director.root.createModel(cclegacy.renderer.scene.Model) as Model; + this._model = (cclegacy.director.root as Root).createModel(cclegacy.renderer.scene.Model as typeof Model); //The skybox material has added properties of 'environmentMap' that need local ubo //this._model._initLocalDescriptors = () => {}; //this._model._initWorldBoundDescriptors = () => {}; diff --git a/cocos/rendering/custom/executor.ts b/cocos/rendering/custom/executor.ts index 1e24f23f00e..930a8cc54f4 100644 --- a/cocos/rendering/custom/executor.ts +++ b/cocos/rendering/custom/executor.ts @@ -62,6 +62,7 @@ import { Rect, RenderPass, RenderPassInfo, + SamplerInfo, StoreOp, SurfaceTransform, Swapchain, @@ -99,7 +100,6 @@ import { PersistentTexture, RasterPass, RasterSubpass, - RasterView, RaytracePass, RenderData, RenderGraph, @@ -134,8 +134,10 @@ import { updateGlobalDescBinding, } from './define'; import { LightResource, SceneCulling } from './scene-culling'; -import { Pass, RenderScene, scene } from '../../render-scene'; +import { Pass, RenderScene } from '../../render-scene'; import { WebProgramLibrary } from './web-program-library'; +import { recordCommand, RenderQueue as RenderExeQueue } from './web-pipeline-types'; +import { SpotLight, SphereLight } from '../../render-scene/scene'; class ResourceVisitor implements ResourceGraphVisitor { name: string; @@ -150,26 +152,22 @@ class ResourceVisitor implements ResourceGraphVisitor { this.name = value; } checkTexture (name: string): boolean { - const dTex = context.deviceTextures.get(name)!; - const resID = context.resourceGraph.vertex(this.name); - const desc = context.resourceGraph.getDesc(resID); - let res = false; - if (dTex.texture) { - res = dTex.texture.width === desc.width && dTex.texture.height === desc.height; - } else if (dTex.swapchain) { - res = dTex.swapchain.width === desc.width && dTex.swapchain.height === desc.height; - } - return res; + const dTex = context.deviceTextures.get(name); + if (!dTex) return false; + const { width: descWidth, height: descHeight } = context.resourceGraph.getDesc(context.resourceGraph.vertex(name)); + const checkDimensions = (actualWidth: number, actualHeight: number): boolean => actualWidth === descWidth && actualHeight === descHeight; + return (dTex.texture ? checkDimensions(dTex.texture.width, dTex.texture.height) + : dTex.swapchain ? checkDimensions(dTex.swapchain.width, dTex.swapchain.height) + : false); } createDeviceTex (value: PersistentTexture | Framebuffer | ManagedResource | RenderSwapchain): void { - if (!context.deviceTextures.get(this.name)) { - const deviceTex = new DeviceTexture(this.name, value); - context.deviceTextures.set(this.name, deviceTex); - } else if (!this.checkTexture(this.name)) { - const dTex = context.deviceTextures.get(this.name)!; - dTex.texture?.destroy(); - const deviceTex = new DeviceTexture(this.name, value); - context.deviceTextures.set(this.name, deviceTex); + let dTex = context.deviceTextures.get(this.name); + if (!dTex || !this.checkTexture(this.name)) { + if (dTex?.texture) { + dTex.texture.destroy(); + } + dTex = new DeviceTexture(this.name, value); + context.deviceTextures.set(this.name, dTex); } } checkBuffer (name: string): boolean { @@ -179,15 +177,13 @@ class ResourceVisitor implements ResourceGraphVisitor { return dBuf.buffer!.size >= desc.width; } createDeviceBuf (value: ManagedBuffer | PersistentBuffer): void { - const mount: boolean = !!context.deviceBuffers.get(this.name); - if (!mount) { - const deviceBuf = new DeviceBuffer(this.name, value); - context.deviceBuffers.set(this.name, deviceBuf); - } else if (!this.checkBuffer(this.name)) { - const dBuf = context.deviceBuffers.get(this.name)!; - dBuf.buffer?.destroy(); - const deviceBuf = new DeviceBuffer(this.name, value); - context.deviceBuffers.set(this.name, deviceBuf); + let dBuf = context.deviceBuffers.get(this.name); + if (!dBuf || !this.checkBuffer(this.name)) { + if (dBuf?.buffer) { + dBuf.buffer.destroy(); + } + dBuf = new DeviceBuffer(this.name, value); + context.deviceBuffers.set(this.name, dBuf); } } managed (value: ManagedResource): void { @@ -240,86 +236,81 @@ class DeviceTexture extends DeviceResource { protected _framebuffer: Framebuffer | null = null; protected _desc: ResourceDesc | null = null; protected _trait: ResourceTraits | null = null; + get texture (): Texture | null { return this._texture; } set framebuffer (val: Framebuffer | null) { this._framebuffer = val; } get framebuffer (): Framebuffer | null { return this._framebuffer; } get description (): ResourceDesc | null { return this._desc; } get trait (): ResourceTraits | null { return this._trait; } get swapchain (): Swapchain | null { return this._swapchain; } + constructor (name: string, tex: PersistentTexture | Framebuffer | RenderSwapchain | ManagedResource) { super(name); const resGraph = context.resourceGraph; const verID = resGraph.vertex(name); this._desc = resGraph.getDesc(verID); this._trait = resGraph.getTraits(verID); + if (tex instanceof Texture) { this._texture = tex; - return; - } - if (tex instanceof Framebuffer) { + } else if (tex instanceof Framebuffer) { this._framebuffer = tex; - return; - } - if (tex instanceof RenderSwapchain) { + } else if (tex instanceof RenderSwapchain) { this._swapchain = tex.swapchain; - return; + } else { + this.createTextureFromDesc(this._desc); } + } - const info = this._desc; + private createTextureFromDesc (desc: ResourceDesc): void { let type = TextureType.TEX2D; - - switch (info.dimension) { + switch (desc.dimension) { case ResourceDimension.TEXTURE1D: type = TextureType.TEX1D; break; - case ResourceDimension.TEXTURE2D: - type = TextureType.TEX2D; - break; case ResourceDimension.TEXTURE3D: type = TextureType.TEX3D; break; default: } - let usageFlags = TextureUsageBit.NONE; - if (info.flags & ResourceFlags.COLOR_ATTACHMENT) usageFlags |= TextureUsageBit.COLOR_ATTACHMENT; - if (info.flags & ResourceFlags.DEPTH_STENCIL_ATTACHMENT) usageFlags |= TextureUsageBit.DEPTH_STENCIL_ATTACHMENT; - if (info.flags & ResourceFlags.INPUT_ATTACHMENT) usageFlags |= TextureUsageBit.INPUT_ATTACHMENT; - if (info.flags & ResourceFlags.SAMPLED) usageFlags |= TextureUsageBit.SAMPLED; - if (info.flags & ResourceFlags.STORAGE) usageFlags |= TextureUsageBit.STORAGE; - if (info.flags & ResourceFlags.TRANSFER_SRC) usageFlags |= TextureUsageBit.TRANSFER_SRC; - if (info.flags & ResourceFlags.TRANSFER_DST) usageFlags |= TextureUsageBit.TRANSFER_DST; + + const usageFlags = [ + [ResourceFlags.COLOR_ATTACHMENT, TextureUsageBit.COLOR_ATTACHMENT], + [ResourceFlags.DEPTH_STENCIL_ATTACHMENT, TextureUsageBit.DEPTH_STENCIL_ATTACHMENT], + [ResourceFlags.INPUT_ATTACHMENT, TextureUsageBit.INPUT_ATTACHMENT], + [ResourceFlags.SAMPLED, TextureUsageBit.SAMPLED], + [ResourceFlags.STORAGE, TextureUsageBit.STORAGE], + [ResourceFlags.TRANSFER_SRC, TextureUsageBit.TRANSFER_SRC], + [ResourceFlags.TRANSFER_DST, TextureUsageBit.TRANSFER_DST], + ].reduce((acc, [flag, bit]) => (desc.flags & flag ? acc | bit : acc), TextureUsageBit.NONE); this._texture = context.device.createTexture(new TextureInfo( type, usageFlags, - info.format, - info.width, - info.height, + desc.format, + desc.width, + desc.height, )); } release (): void { - if (this.framebuffer) { - this.framebuffer.destroy(); - this._framebuffer = null; - } - if (this.texture) { - this.texture.destroy(); - this._texture = null; - } + this.framebuffer?.destroy(); + this._framebuffer = null; + this.texture?.destroy(); + this._texture = null; } } -function isShadowMap (scene?: SceneData): boolean | undefined { +function isShadowMap (scene?: SceneData): boolean { const pSceneData: PipelineSceneData = cclegacy.director.root.pipeline.pipelineSceneData; - return pSceneData.shadows.enabled + return !!(pSceneData.shadows.enabled && pSceneData.shadows.type === ShadowType.ShadowMap && scene - && (scene.flags & SceneFlags.SHADOW_CASTER) !== 0; + && (scene.flags & SceneFlags.SHADOW_CASTER) !== 0); } class DeviceBuffer extends DeviceResource { - private _buffer: Buffer | null; + private _buffer: Buffer | null = null; get buffer (): Buffer | null { return this._buffer; @@ -330,47 +321,54 @@ class DeviceBuffer extends DeviceResource { const resGraph = context.resourceGraph; const verID = resGraph.vertex(name); const desc = resGraph.getDesc(verID); - const bufferInfo = new BufferInfo(); - bufferInfo.size = desc.width; - bufferInfo.memUsage = MemoryUsageBit.DEVICE; + const bufferInfo = new BufferInfo( + this.calculateBufferUsage(desc.flags), + MemoryUsageBit.DEVICE, + desc.width, + ); + this._buffer = context.device.createBuffer(bufferInfo); + } - if (desc.flags & ResourceFlags.INDIRECT) bufferInfo.usage |= BufferUsageBit.INDIRECT; - if (desc.flags & ResourceFlags.UNIFORM) bufferInfo.usage |= BufferUsageBit.UNIFORM; - if (desc.flags & ResourceFlags.STORAGE) bufferInfo.usage |= BufferUsageBit.STORAGE; - if (desc.flags & ResourceFlags.TRANSFER_SRC) bufferInfo.usage |= BufferUsageBit.TRANSFER_SRC; - if (desc.flags & ResourceFlags.TRANSFER_DST) bufferInfo.usage |= BufferUsageBit.TRANSFER_DST; + private calculateBufferUsage (flags: ResourceFlags): BufferUsageBit { + const flagToUsageMap: [ResourceFlags, BufferUsageBit][] = [ + [ResourceFlags.INDIRECT, BufferUsageBit.INDIRECT], + [ResourceFlags.UNIFORM, BufferUsageBit.UNIFORM], + [ResourceFlags.STORAGE, BufferUsageBit.STORAGE], + [ResourceFlags.TRANSFER_SRC, BufferUsageBit.TRANSFER_SRC], + [ResourceFlags.TRANSFER_DST, BufferUsageBit.TRANSFER_DST], + ]; - this._buffer = context.device.createBuffer(bufferInfo); + return flagToUsageMap.reduce((acc, [flag, usage]) => ((flags & flag) ? acc | usage : acc), BufferUsageBit.NONE); } release (): void { - if (this._buffer) { - this._buffer.destroy(); - this._buffer = null; - } + this._buffer?.destroy(); + this._buffer = null; } } const _vec4Array = new Float32Array(4); + class BlitDesc { private _isUpdate = false; private _isGatherLight = false; private _blit: Blit; private _screenQuad: PipelineInputAssemblerData | null = null; - private _queue: DeviceRenderQueue | null = null; private _stageDesc: DescriptorSet | undefined; // If VOLUMETRIC_LIGHTING is turned on, it needs to be assigned private _lightVolumeBuffer: Buffer | null = null; private _lightMeterScale = 10000.0; private _lightBufferData!: Float32Array; + get screenQuad (): PipelineInputAssemblerData | null { return this._screenQuad; } get blit (): Blit { return this._blit; } set blit (blit: Blit) { this._blit = blit; } get stageDesc (): DescriptorSet | undefined { return this._stageDesc; } - constructor (blit: Blit, queue: DeviceRenderQueue) { + + constructor (blit: Blit) { this._blit = blit; - this._queue = queue; } + /** * @zh * 创建四边形输入汇集器。 @@ -378,34 +376,35 @@ class BlitDesc { protected _createQuadInputAssembler (): PipelineInputAssemblerData { return context.blit.pipelineIAData; } + createScreenQuad (): void { if (!this._screenQuad) { this._screenQuad = this._createQuadInputAssembler(); } } + private _gatherVolumeLights (camera: Camera): void { - if (!camera.scene) { return; } + if (!camera.scene) return; + const pipeline = context.pipeline; const cmdBuff = context.commandBuffer; - const sphereLights = camera.scene.sphereLights; const spotLights = camera.scene.spotLights; - const _sphere = Sphere.create(0, 0, 0, 1); const exposure = camera.exposure; - - let idx = 0; const maxLights = UBODeferredLight.LIGHTS_PER_PASS; const elementLen = Vec4.length; // sizeof(vec4) / sizeof(float32) const fieldLen = elementLen * maxLights; + const _sphere = Sphere.create(0, 0, 0, 1); + let idx = 0; - for (let i = 0; i < sphereLights.length && idx < maxLights; i++, ++idx) { - const light = sphereLights[i]; + const processLight = (light: SphereLight | SpotLight, isSpot: boolean): void => { + if (idx >= maxLights) return; Sphere.set(_sphere, light.position.x, light.position.y, light.position.z, light.range); if (intersect.sphereFrustum(_sphere, camera.frustum)) { // cc_lightPos Vec3.toArray(_vec4Array, light.position); - _vec4Array[3] = 0; - this._lightBufferData.set(_vec4Array, idx * elementLen); + _vec4Array[3] = isSpot ? 1 : 0; + this._lightBufferData.set(_vec4Array, idx * elementLen + fieldLen * 0); // cc_lightColor Vec3.toArray(_vec4Array, light.color); @@ -415,68 +414,42 @@ class BlitDesc { _vec4Array[1] *= tempRGB.y; _vec4Array[2] *= tempRGB.z; } - - if (pipeline.pipelineSceneData.isHDR) { - _vec4Array[3] = light.luminance * exposure * this._lightMeterScale; - } else { - _vec4Array[3] = light.luminance; - } - + _vec4Array[3] = pipeline.pipelineSceneData.isHDR + ? light.luminance * exposure * this._lightMeterScale + : light.luminance; this._lightBufferData.set(_vec4Array, idx * elementLen + fieldLen * 1); // cc_lightSizeRangeAngle _vec4Array[0] = light.size; _vec4Array[1] = light.range; - _vec4Array[2] = 0.0; + _vec4Array[2] = isSpot ? (light as SpotLight).spotAngle : 0.0; this._lightBufferData.set(_vec4Array, idx * elementLen + fieldLen * 2); - } - } - for (let i = 0; i < spotLights.length && idx < maxLights; i++, ++idx) { - const light = spotLights[i]; - Sphere.set(_sphere, light.position.x, light.position.y, light.position.z, light.range); - if (intersect.sphereFrustum(_sphere, camera.frustum)) { - // cc_lightPos - Vec3.toArray(_vec4Array, light.position); - _vec4Array[3] = 1; - this._lightBufferData.set(_vec4Array, idx * elementLen + fieldLen * 0); - - // cc_lightColor - Vec3.toArray(_vec4Array, light.color); - if (light.useColorTemperature) { - const tempRGB = light.colorTemperatureRGB; - _vec4Array[0] *= tempRGB.x; - _vec4Array[1] *= tempRGB.y; - _vec4Array[2] *= tempRGB.z; - } - if (pipeline.pipelineSceneData.isHDR) { - _vec4Array[3] = light.luminance * exposure * this._lightMeterScale; - } else { - _vec4Array[3] = light.luminance; + if (isSpot) { + // cc_lightDir + Vec3.toArray(_vec4Array, (light as SpotLight).direction); + this._lightBufferData.set(_vec4Array, idx * elementLen + fieldLen * 3); } - this._lightBufferData.set(_vec4Array, idx * elementLen + fieldLen * 1); + idx++; + } + }; - // cc_lightSizeRangeAngle - _vec4Array[0] = light.size; - _vec4Array[1] = light.range; - _vec4Array[2] = light.spotAngle; - this._lightBufferData.set(_vec4Array, idx * elementLen + fieldLen * 2); + for (const light of sphereLights) { + processLight(light, false); + } - // cc_lightDir - Vec3.toArray(_vec4Array, light.direction); - this._lightBufferData.set(_vec4Array, idx * elementLen + fieldLen * 3); - } + for (const light of spotLights) { + processLight(light, true); } - // the count of lights is set to cc_lightDir[0].w + // Set the count of lights in cc_lightDir[0].w const offset = fieldLen * 3 + 3; this._lightBufferData.set([idx], offset); - cmdBuff.updateBuffer(this._lightVolumeBuffer!, this._lightBufferData); } + update (): void { - if (this.blit.sceneFlags & SceneFlags.VOLUMETRIC_LIGHTING - && this.blit.camera && !this._isGatherLight) { + if (this.blit.sceneFlags & SceneFlags.VOLUMETRIC_LIGHTING && this.blit.camera && !this._isGatherLight) { this._gatherVolumeLights(this.blit.camera); this._isGatherLight = true; this._isUpdate = false; @@ -496,11 +469,8 @@ class BlitDesc { const blit = this.blit; const pass = blit.material!.passes[blit.passID]; const device = context.device; - this._stageDesc = context.blit.stageDescs.get(pass); - if (!this._stageDesc) { - this._stageDesc = device.createDescriptorSet(new DescriptorSetInfo(pass.localSetLayout)); - context.blit.stageDescs.set(pass, this._stageDesc); - } + this._stageDesc = context.blit.stageDescs.get(pass) || device.createDescriptorSet(new DescriptorSetInfo(pass.localSetLayout)); + context.blit.stageDescs.set(pass, this._stageDesc); if (this.blit.sceneFlags & SceneFlags.VOLUMETRIC_LIGHTING) { this._lightVolumeBuffer = context.blit.lightVolumeBuffer; const deferredLitsBufView = context.blit.deferredLitsBufView; @@ -508,6 +478,7 @@ class BlitDesc { this._lightBufferData.fill(0); this._stageDesc.bindBuffer(UBOForwardLight.BINDING, deferredLitsBufView); } + this._stageDesc.bindBuffer(UBOLocal.BINDING, context.blit.emptyLocalUBO); } } @@ -621,7 +592,7 @@ class DeviceRenderQueue implements RecordingInterface { } createBlitDesc (blit: Blit): void { if (!this._blitDesc) { - this._blitDesc = new BlitDesc(blit, this); + this._blitDesc = new BlitDesc(blit); } this._blitDesc.createScreenQuad(); this._blitDesc.createStageDescriptor(); @@ -667,6 +638,7 @@ class DeviceRenderQueue implements RecordingInterface { class RenderPassLayoutInfo { protected _layoutID = 0; protected _vertID = -1; + private _resID = -1; protected _stage: RenderStageData | null = null; protected _layout: PipelineLayoutData; protected _inputName: string; @@ -675,54 +647,74 @@ class RenderPassLayoutInfo { this._inputName = input[0]; this._layoutID = layoutId; this._vertID = vertId; + const lg = context.layoutGraph; this._stage = lg.j(layoutId); this._layout = lg.getLayout(layoutId); + const layoutData = this._layout.descriptorSets.get(UpdateFrequency.PER_PASS); if (!layoutData) { return; } + const layoutDesc = layoutData.descriptorSet!; - // find resource const deviceTex = context.deviceTextures.get(this._inputName); const gfxTex = deviceTex?.texture; const deviceBuf = context.deviceBuffers.get(this._inputName); const gfxBuf = deviceBuf?.buffer; + if (!gfxTex && !gfxBuf) { - throw Error(`Could not find texture with resource name ${this._inputName}`); + throw new Error(`Could not find texture with resource name ${this._inputName}`); } - const resId = context.resourceGraph.vertex(this._inputName); - const samplerInfo = context.resourceGraph.getSampler(resId); + + this._resID = context.resourceGraph.vertex(this._inputName); + const samplerInfo = context.resourceGraph.getSampler(this._resID); + // bind descriptors for (const descriptor of input[1]) { const descriptorName = descriptor.name; const descriptorID = lg.attributeIndex.get(descriptorName); - // find descriptor binding - for (const block of layoutData.descriptorSetLayoutData.descriptorBlocks) { - for (let i = 0; i !== block.descriptors.length; ++i) { - if (descriptorID === block.descriptors[i].descriptorID) { - if (gfxTex) { - layoutDesc.bindTexture(block.offset + i, gfxTex); - const renderData = context.renderGraph.getData(this._vertID); - layoutDesc.bindSampler(block.offset + i, renderData.samplers.get(descriptorID) || context.device.getSampler(samplerInfo)); - } else { - const desc = context.resourceGraph.getDesc(resId); - if (desc.flags & ResourceFlags.STORAGE) { - const access = input[1][0].accessType !== AccessType.READ ? AccessFlagBit.COMPUTE_SHADER_WRITE - : AccessFlagBit.COMPUTE_SHADER_READ_OTHER; - (layoutDesc as any).bindBuffer(block.offset + i, gfxBuf!, 0, access); - } else { - layoutDesc.bindBuffer(block.offset + i, gfxBuf!); - } - } - if (!this._descriptorSet) this._descriptorSet = layoutDesc; - continue; + if (descriptorID === undefined) { + continue; + } + this.bindDescriptor(layoutDesc, descriptorID, gfxTex!, gfxBuf!, samplerInfo, input[1][0].accessType); + } + } + + private bindDescriptor ( + layoutDesc: DescriptorSet, + descriptorID: number, + gfxTex: Texture, + gfxBuf: Buffer, + samplerInfo: SamplerInfo, + accessType: AccessType, + ): void { + const layoutData = this._layout.descriptorSets.get(UpdateFrequency.PER_PASS)!; + const desc = context.resourceGraph.getDesc(this._resID); + for (const block of layoutData.descriptorSetLayoutData.descriptorBlocks) { + for (let i = 0; i < block.descriptors.length; ++i) { + if (descriptorID === block.descriptors[i].descriptorID) { + if (gfxTex) { + layoutDesc.bindTexture(block.offset + i, gfxTex); + const renderData = context.renderGraph.getData(this._vertID); + const sampler = renderData.samplers.get(descriptorID) || context.device.getSampler(samplerInfo); + layoutDesc.bindSampler(block.offset + i, sampler); + } else if (desc.flags & ResourceFlags.STORAGE) { + const access = accessType !== AccessType.READ ? AccessFlagBit.COMPUTE_SHADER_WRITE : AccessFlagBit.COMPUTE_SHADER_READ_OTHER; + (layoutDesc as any).bindBuffer(block.offset + i, gfxBuf, 0, access); + } else { + layoutDesc.bindBuffer(block.offset + i, gfxBuf); } + if (!this._descriptorSet) { + this._descriptorSet = layoutDesc; + } + break; } } } } + get descriptorSet (): DescriptorSet | null { return this._descriptorSet; } get layoutID (): number { return this._layoutID; } get vertID (): number { return this._vertID; } @@ -753,8 +745,8 @@ class DeviceRenderPass implements RecordingInterface { const device = context.device; this._layoutName = context.renderGraph.getLayout(rasterID); this._passID = cclegacy.rendering.getPassID(this._layoutName); - const depthStencilAttachment = new DepthStencilAttachment(); - depthStencilAttachment.format = Format.DEPTH_STENCIL; + const depAtt = new DepthStencilAttachment(); + depAtt.format = Format.DEPTH_STENCIL; const colors: ColorAttachment[] = []; const colorTexs: Texture[] = []; let depthTex: Texture | null = null; @@ -781,31 +773,27 @@ class DeviceRenderPass implements RecordingInterface { } if (!swapchain) swapchain = resTex.swapchain; if (!framebuffer) framebuffer = resTex.framebuffer; - switch (rasterV.attachmentType) { - case AttachmentType.RENDER_TARGET: - { - if (!resTex.swapchain && !resTex.framebuffer) colorTexs.push(resTex.texture!); - const colorAttachment = new ColorAttachment(); - colorAttachment.format = resTex.description!.format; - colorAttachment.sampleCount = resTex.description!.sampleCount; - colorAttachment.loadOp = rasterV.loadOp; - colorAttachment.storeOp = rasterV.storeOp; - colorAttachment.barrier = device.getGeneralBarrier(new GeneralBarrierInfo( - rasterV.loadOp === LoadOp.LOAD ? AccessFlagBit.COLOR_ATTACHMENT_WRITE : AccessFlagBit.NONE, - rasterV.storeOp === StoreOp.STORE ? AccessFlagBit.COLOR_ATTACHMENT_WRITE : AccessFlagBit.NONE, - )); - const currCol = new Color(); - currCol.copy(rasterV.clearColor); - this._clearColor.push(currCol); - colors.push(colorAttachment); - } - break; - case AttachmentType.DEPTH_STENCIL: - depthStencilAttachment.depthStoreOp = rasterV.storeOp; - depthStencilAttachment.stencilStoreOp = rasterV.storeOp; - depthStencilAttachment.depthLoadOp = rasterV.loadOp; - depthStencilAttachment.stencilLoadOp = rasterV.loadOp; - depthStencilAttachment.barrier = device.getGeneralBarrier(new GeneralBarrierInfo( + if (rasterV.attachmentType === AttachmentType.RENDER_TARGET) { + if (!resTex.swapchain && !resTex.framebuffer) colorTexs.push(resTex.texture!); + const colAtt = new ColorAttachment(); + colAtt.format = resTex.description!.format; + colAtt.sampleCount = resTex.description!.sampleCount; + colAtt.loadOp = rasterV.loadOp; + colAtt.storeOp = rasterV.storeOp; + colAtt.barrier = device.getGeneralBarrier(new GeneralBarrierInfo( + rasterV.loadOp === LoadOp.LOAD ? AccessFlagBit.COLOR_ATTACHMENT_WRITE : AccessFlagBit.NONE, + rasterV.storeOp === StoreOp.STORE ? AccessFlagBit.COLOR_ATTACHMENT_WRITE : AccessFlagBit.NONE, + )); + const currCol = new Color(); + currCol.copy(rasterV.clearColor); + this._clearColor.push(currCol); + colors.push(colAtt); + } else if (rasterV.attachmentType === AttachmentType.DEPTH_STENCIL) { + depAtt.depthStoreOp = rasterV.storeOp; + depAtt.stencilStoreOp = rasterV.storeOp; + depAtt.depthLoadOp = rasterV.loadOp; + depAtt.stencilLoadOp = rasterV.loadOp; + depAtt.barrier = device.getGeneralBarrier(new GeneralBarrierInfo( rasterV.loadOp === LoadOp.LOAD ? AccessFlagBit.DEPTH_STENCIL_ATTACHMENT_WRITE : AccessFlagBit.NONE, rasterV.storeOp === StoreOp.STORE ? AccessFlagBit.DEPTH_STENCIL_ATTACHMENT_WRITE : AccessFlagBit.NONE, )); @@ -816,11 +804,6 @@ class DeviceRenderPass implements RecordingInterface { } this._clearDepth = rasterV.clearColor.x; this._clearStencil = rasterV.clearColor.y; - break; - case AttachmentType.SHADING_RATE: - // noop - break; - default: } } if (colors.length === 0) { @@ -835,7 +818,7 @@ class DeviceRenderPass implements RecordingInterface { renderPassInfo.colorAttachments = colors; const depth = swapchain ? swapchain.depthStencilTexture : depthTex; if (depth) { - renderPassInfo.depthStencilAttachment = depthStencilAttachment; + renderPassInfo.depthStencilAttachment = depAtt; } this._renderPass = device.createRenderPass(renderPassInfo); this._createFramebuffer( @@ -910,31 +893,19 @@ class DeviceRenderPass implements RecordingInterface { const submodel = profiler.subModels[0]; const pass = submodel.passes[0]; const ia = submodel.inputAssembler; - const device = context.device; - const pso = PipelineStateManager.getOrCreatePipelineState( - device, - pass, - submodel.shaders[0], - renderPass, - ia, - ); - profilerViewport.width = rect.width; profilerViewport.height = rect.height; cmdBuff.setViewport(profilerViewport); cmdBuff.setScissor(rect); - cmdBuff.bindPipelineState(pso); cmdBuff.bindDescriptorSet(SetIndex.GLOBAL, profilerDesc); - cmdBuff.bindDescriptorSet(SetIndex.MATERIAL, pass.descriptorSet); - cmdBuff.bindDescriptorSet(SetIndex.LOCAL, submodel.descriptorSet); - cmdBuff.bindInputAssembler(ia); - cmdBuff.draw(ia); + recordCommand(cmdBuff, renderPass, pass, submodel.descriptorSet, submodel.shaders[0], ia); } beginPass (): void { const tex = this.framebuffer.colorTextures[0]!; this._applyViewport(tex); const cmdBuff = context.commandBuffer; + if (this._viewport) { renderPassArea.x = this._viewport.left; renderPassArea.y = this._viewport.top; @@ -1011,41 +982,27 @@ class DeviceRenderPass implements RecordingInterface { let framebuffer: Framebuffer | null = null; const colTextures: Texture[] = []; const currFramebuffer = this._framebuffer; - const currFBDepthTex = currFramebuffer.depthStencilTexture; + const currFBDepthTex = currFramebuffer?.depthStencilTexture ?? null; let depTexture = currFramebuffer ? currFBDepthTex : null; this._processRenderLayout(pass); + const currentWidth = currFramebuffer?.width ?? 0; + const currentHeight = currFramebuffer?.height ?? 0; - const resGraph = context.resourceGraph; - const currentWidth = currFramebuffer ? currFramebuffer.width : 0; - const currentHeight = currFramebuffer ? currFramebuffer.height : 0; - - let width = 0; - let height = 0; + let [width, height] = [0, 0]; for (const [resName, rasterV] of pass.rasterViews) { - if (rasterV.attachmentType === AttachmentType.SHADING_RATE) { - continue; - } - const resId = resGraph.vertex(resName); - const resDesc = resGraph.getDesc(resId); - width = resDesc.width; - height = resDesc.height; - break; - } - // The texture inside the fbo was destroyed? - let isInsideTexDestroy = false; - for (const colTex of currFramebuffer.colorTextures) { - if (colTex?.getTextureHandle() === 0) { - isInsideTexDestroy = true; + if (rasterV.attachmentType !== AttachmentType.SHADING_RATE) { + const resDesc = context.resourceGraph.getDesc(context.resourceGraph.vertex(resName)); + width = resDesc.width; + height = resDesc.height; break; } } - if (currFBDepthTex && !isInsideTexDestroy) { - isInsideTexDestroy = currFBDepthTex.getTextureHandle() === 0; - } - const needRebuild = width !== currentWidth || height !== currentHeight || currFramebuffer.needRebuild || isInsideTexDestroy; + // The texture inside the fbo was destroyed? + const isInsideTexDestroy = currFramebuffer?.colorTextures.some((colTex) => !colTex || colTex.getTextureHandle() === 0) + || (currFBDepthTex && currFBDepthTex.getTextureHandle() === 0); + const needRebuild = width !== currentWidth || height !== currentHeight || (currFramebuffer?.needRebuild ?? false) || isInsideTexDestroy; for (const [resName, rasterV] of pass.rasterViews) { let deviceTex = context.deviceTextures.get(resName)!; - const currTex = deviceTex; if (!deviceTex) { this.visitResource(resName); deviceTex = context.deviceTextures.get(resName)!; @@ -1056,20 +1013,13 @@ class DeviceRenderPass implements RecordingInterface { const resDesc = resGraph.getDesc(resId); if (deviceTex.framebuffer && resFbo instanceof Framebuffer && (deviceTex.framebuffer !== resFbo || resFbo !== this._framebuffer)) { framebuffer = this._framebuffer = deviceTex.framebuffer = resFbo; - } else if (!currTex || (deviceTex.texture && needRebuild)) { - const gfxTex = deviceTex.texture!; - if (currTex) gfxTex.resize(resDesc.width, resDesc.height); - switch (rasterV.attachmentType) { - case AttachmentType.RENDER_TARGET: + } else if (deviceTex.texture && needRebuild) { + const gfxTex = deviceTex.texture; + gfxTex.resize(resDesc.width, resDesc.height); + if (rasterV.attachmentType === AttachmentType.RENDER_TARGET) { colTextures.push(gfxTex); - break; - case AttachmentType.DEPTH_STENCIL: + } else if (rasterV.attachmentType === AttachmentType.DEPTH_STENCIL) { depTexture = gfxTex; - break; - case AttachmentType.SHADING_RATE: - // noop - break; - default: } } } @@ -1234,14 +1184,9 @@ class DeviceRenderScene implements RecordingInterface { const pass = batch.passes[j]; if (pass.phaseID !== this._currentQueue.phaseID) continue; const shader = batch.shaders[j]; - const inputAssembler: InputAssembler = batch.inputAssembler!; - const pso = PipelineStateManager.getOrCreatePipelineState(deviceManager.gfxDevice, pass, shader, this._renderPass, inputAssembler); - context.commandBuffer.bindPipelineState(pso); - context.commandBuffer.bindDescriptorSet(SetIndex.MATERIAL, pass.descriptorSet); + const ia: InputAssembler = batch.inputAssembler!; const ds = batch.descriptorSet!; - context.commandBuffer.bindDescriptorSet(SetIndex.LOCAL, ds); - context.commandBuffer.bindInputAssembler(inputAssembler); - context.commandBuffer.draw(inputAssembler); + recordCommand(context.commandBuffer, this._renderPass, pass, ds, shader, ia); } } } @@ -1254,25 +1199,9 @@ class DeviceRenderScene implements RecordingInterface { const pass = currMat.passes[blit.passID]; pass.update(); const shader = pass.getShaderVariant(); - const devicePass = this._currentQueue.devicePass; - const screenIa: InputAssembler = this._currentQueue.blitDesc!.screenQuad!.quadIA!; - let pso: PipelineState | null = null; - if (pass !== null && shader !== null && screenIa !== null) { - pso = PipelineStateManager.getOrCreatePipelineState( - context.device, - pass, - shader, - devicePass.renderPass, - screenIa, - ); - } - if (pso) { - context.commandBuffer.bindPipelineState(pso); - context.commandBuffer.bindDescriptorSet(SetIndex.MATERIAL, pass.descriptorSet); - context.commandBuffer.bindDescriptorSet(SetIndex.LOCAL, this._currentQueue.blitDesc!.stageDesc!); - context.commandBuffer.bindInputAssembler(screenIa); - context.commandBuffer.draw(screenIa); - } + const blitDesc = this._currentQueue.blitDesc!; + const screenIa = blitDesc.screenQuad!.quadIA; + recordCommand(context.commandBuffer, this._renderPass, pass, blitDesc.stageDesc!, shader, screenIa); } protected _updateGlobal (data: RenderData, sceneId: number): void { @@ -1320,18 +1249,19 @@ class DeviceRenderScene implements RecordingInterface { const sceneCulling = context.culling; this._updateRenderData(); this._applyViewport(); + // Currently processing blit and camera first if (this.blit) { this._recordBlit(); return; } - const renderQueueQuery = sceneCulling.renderQueueQueryIndex.get(this.sceneID)!; - const renderQueue = sceneCulling.renderQueues[renderQueueQuery.renderQueueTarget]; + const rqQuery = sceneCulling.renderQueueQueryIndex.get(this.sceneID)!; + const rq = sceneCulling.renderQueues[rqQuery.renderQueueTarget]; const graphSceneData = this.sceneData!; const isProbe = bool(graphSceneData.flags & SceneFlags.REFLECTION_PROBE); - if (isProbe) renderQueue.probeQueue.applyMacro(); - renderQueue.recordCommands(context.commandBuffer, this._renderPass, graphSceneData.flags); - if (isProbe) renderQueue.probeQueue.removeMacro(); + if (isProbe) rq.probeQueue.applyMacro(); + rq.recordCommands(context.commandBuffer, this._renderPass, graphSceneData.flags); + if (isProbe) rq.probeQueue.removeMacro(); if (graphSceneData.flags & SceneFlags.GEOMETRY) { this.camera!.geometryRenderer?.render( devicePass.renderPass, @@ -1460,47 +1390,33 @@ class BlitInfo { const maxX = (renderArea.x + renderArea.width) / this._context.width; let minY = renderArea.y / this._context.height; let maxY = (renderArea.y + renderArea.height) / this._context.height; + + // Flip the minimum maximum Y value according to the sign of the Y-axis of the screen space if (this._context.root.device.capabilities.screenSpaceSignY > 0) { - const temp = maxY; - maxY = minY; - minY = temp; + [minY, maxY] = [maxY, minY]; } - let n = 0; + const vbData = new Float32Array(16); + const fillVertices = (x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4): void => { + vbData.set([x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4]); + }; switch (surfaceTransform) { - case (SurfaceTransform.IDENTITY): - n = 0; - vbData[n++] = -1.0; vbData[n++] = -1.0; vbData[n++] = minX; vbData[n++] = maxY; - vbData[n++] = 1.0; vbData[n++] = -1.0; vbData[n++] = maxX; vbData[n++] = maxY; - vbData[n++] = -1.0; vbData[n++] = 1.0; vbData[n++] = minX; vbData[n++] = minY; - vbData[n++] = 1.0; vbData[n++] = 1.0; vbData[n++] = maxX; vbData[n++] = minY; + case SurfaceTransform.IDENTITY: + fillVertices(-1, -1, minX, maxY, 1, -1, maxX, maxY, -1, 1, minX, minY, 1, 1, maxX, minY); break; - case (SurfaceTransform.ROTATE_90): - n = 0; - vbData[n++] = -1.0; vbData[n++] = -1.0; vbData[n++] = maxX; vbData[n++] = maxY; - vbData[n++] = 1.0; vbData[n++] = -1.0; vbData[n++] = maxX; vbData[n++] = minY; - vbData[n++] = -1.0; vbData[n++] = 1.0; vbData[n++] = minX; vbData[n++] = maxY; - vbData[n++] = 1.0; vbData[n++] = 1.0; vbData[n++] = minX; vbData[n++] = minY; + case SurfaceTransform.ROTATE_90: + fillVertices(-1, -1, maxX, maxY, 1, -1, maxX, minY, -1, 1, minX, maxY, 1, 1, minX, minY); break; - case (SurfaceTransform.ROTATE_180): - n = 0; - vbData[n++] = -1.0; vbData[n++] = -1.0; vbData[n++] = minX; vbData[n++] = minY; - vbData[n++] = 1.0; vbData[n++] = -1.0; vbData[n++] = maxX; vbData[n++] = minY; - vbData[n++] = -1.0; vbData[n++] = 1.0; vbData[n++] = minX; vbData[n++] = maxY; - vbData[n++] = 1.0; vbData[n++] = 1.0; vbData[n++] = maxX; vbData[n++] = maxY; + case SurfaceTransform.ROTATE_180: + fillVertices(-1, -1, minX, minY, 1, -1, maxX, minY, -1, 1, minX, maxY, 1, 1, maxX, maxY); break; - case (SurfaceTransform.ROTATE_270): - n = 0; - vbData[n++] = -1.0; vbData[n++] = -1.0; vbData[n++] = minX; vbData[n++] = minY; - vbData[n++] = 1.0; vbData[n++] = -1.0; vbData[n++] = minX; vbData[n++] = maxY; - vbData[n++] = -1.0; vbData[n++] = 1.0; vbData[n++] = maxX; vbData[n++] = minY; - vbData[n++] = 1.0; vbData[n++] = 1.0; vbData[n++] = maxX; vbData[n++] = maxY; + case SurfaceTransform.ROTATE_270: + fillVertices(-1, -1, minX, minY, 1, -1, minX, maxY, -1, 1, maxX, minY, 1, 1, maxX, maxY); break; default: - break; } - return vbData; } + private _createQuadInputAssembler (): PipelineInputAssemblerData { // create vertex buffer const inputAssemblerData = new PipelineInputAssemblerData(); diff --git a/cocos/rendering/custom/web-pipeline-types.ts b/cocos/rendering/custom/web-pipeline-types.ts index d338a0981fa..026b6bd8a14 100644 --- a/cocos/rendering/custom/web-pipeline-types.ts +++ b/cocos/rendering/custom/web-pipeline-types.ts @@ -1,6 +1,7 @@ import { Mat4, RecyclePool, IVec4Like, IMat4Like, IVec2Like, Color as CoreColor, assert, cclegacy, Quat, Vec4, Vec2, Vec3, toRadian } from '../../core'; -import { Color, CommandBuffer, DescriptorSet, Buffer, Device, PipelineState, RenderPass, Sampler, Texture, deviceManager } from '../../gfx'; +import { Color, CommandBuffer, DescriptorSet, Buffer, Device, PipelineState, RenderPass, + Sampler, Texture, deviceManager, Shader, InputAssembler } from '../../gfx'; import { IMacroPatch, Pass, RenderScene } from '../../render-scene'; import { Camera, CSMLevel, DirectionalLight, Light, LightType, Model, PCFType, PointLight, RangedDirectionalLight, Shadows, ShadowType, SphereLight, SpotLight, SubModel } from '../../render-scene/scene'; @@ -1009,6 +1010,34 @@ export class RenderQueueQuery { } } +export function recordCommand ( + cmdBuffer: CommandBuffer, + _renderPass: RenderPass, + pass: Pass, + localDesc: DescriptorSet, + shader: Shader | null, + ia: InputAssembler | null, +): void { + let pso!: PipelineState; + if (shader && ia) { + pso = PipelineStateManager.getOrCreatePipelineState( + deviceManager.gfxDevice, + pass, + shader, + _renderPass, + ia, + ); + } + if (pso) { + const _ia = ia!; + cmdBuffer.bindPipelineState(pso); + cmdBuffer.bindDescriptorSet(SetIndex.MATERIAL, pass.descriptorSet); + cmdBuffer.bindDescriptorSet(SetIndex.LOCAL, localDesc); + cmdBuffer.bindInputAssembler(_ia); + cmdBuffer.draw(_ia); + } +} + export class RenderQueue { probeQueue: ProbeHelperQueue = new ProbeHelperQueue(); opaqueQueue: RenderDrawQueue = new RenderDrawQueue(); diff --git a/cocos/rendering/pipeline-scene-data.ts b/cocos/rendering/pipeline-scene-data.ts index 314539ec16a..56b25656fac 100644 --- a/cocos/rendering/pipeline-scene-data.ts +++ b/cocos/rendering/pipeline-scene-data.ts @@ -37,6 +37,7 @@ import { Skin } from '../render-scene/scene/skin'; import { Model } from '../render-scene/scene/model'; import { PostSettings } from '../render-scene/scene/post-settings'; import { MeshRenderer } from '../3d/framework/mesh-renderer'; +import type { LightProbes } from '../gi/light-probe'; const GEOMETRY_RENDERER_TECHNIQUE_COUNT = 6; @@ -110,7 +111,7 @@ export class PipelineSceneData { public octree: Octree = new Octree(); public skin: Skin = new Skin(); public postSettings: PostSettings = new PostSettings(); - public lightProbes = legacyCC.internal.LightProbes ? new legacyCC.internal.LightProbes() : null; + public lightProbes: LightProbes = legacyCC.internal.LightProbes ? new legacyCC.internal.LightProbes() : null; /** * @en The list for valid punctual Lights, only available after the scene culling of the current frame. diff --git a/cocos/rendering/scene-culling.ts b/cocos/rendering/scene-culling.ts index 1cd156c2d2d..92fc2ce5776 100644 --- a/cocos/rendering/scene-culling.ts +++ b/cocos/rendering/scene-culling.ts @@ -23,7 +23,7 @@ */ import { Model } from '../render-scene/scene/model'; import { Camera, CameraUsage, SkyBoxFlagValue } from '../render-scene/scene/camera'; -import { Vec3, Pool, geometry, cclegacy } from '../core'; +import { Vec3, Pool, geometry, cclegacy, warnID } from '../core'; import { PipelineUBO } from './pipeline-ubo'; import { IRenderObject, UBOShadowEnum } from './define'; import { ShadowType, CSMOptimizationMode } from '../render-scene/scene/shadows'; @@ -175,7 +175,7 @@ export function sceneCulling (sceneData: PipelineSceneData, pipelineUBO: Pipelin if (skybox.enabled && skybox.model) { renderObjects.push(getRenderObject(skybox.model, camera)); } else if (camera.cameraUsage !== CameraUsage.EDITOR && camera.cameraUsage !== CameraUsage.SCENE_VIEW) { - cclegacy.warnID(15100, camera.name); + warnID(15100, camera.name!); } } diff --git a/cocos/spine/skeleton-data.ts b/cocos/spine/skeleton-data.ts index 97d391adc6d..d872fd1ccc2 100644 --- a/cocos/spine/skeleton-data.ts +++ b/cocos/spine/skeleton-data.ts @@ -23,7 +23,7 @@ */ import { EDITOR_NOT_IN_PREVIEW } from 'internal:constants'; -import { CCString, Enum, error } from '../core'; +import { CCString, Enum, error, murmurhash2_32_gc } from '../core'; import SkeletonCache from './skeleton-cache'; import { Skeleton } from './skeleton'; import spine from './lib/spine-core'; @@ -207,12 +207,14 @@ export class SkeletonData extends Asset { } return null; } - const spData = spine.wasmUtil.querySpineSkeletonDataByUUID(this._uuid); + + const uuid = this.mergedUUID(); + const spData = spine.wasmUtil.querySpineSkeletonDataByUUID(uuid); if (spData) { this._skeletonCache = spData; } else if (this._skeletonJson) { this._skeletonCache = spine.wasmUtil.createSpineSkeletonDataWithJson(this.skeletonJsonStr, this._atlasText); - spine.wasmUtil.registerSpineSkeletonDataWithUUID(this._skeletonCache, this._uuid); + spine.wasmUtil.registerSpineSkeletonDataWithUUID(this._skeletonCache, uuid); } else { const rawData = new Uint8Array(this._nativeAsset); const byteSize = rawData.length; @@ -220,7 +222,7 @@ export class SkeletonData extends Asset { const wasmMem = spine.wasmUtil.wasm.HEAPU8.subarray(ptr, ptr + byteSize); wasmMem.set(rawData); this._skeletonCache = spine.wasmUtil.createSpineSkeletonDataWithBinary(byteSize, this._atlasText); - spine.wasmUtil.registerSpineSkeletonDataWithUUID(this._skeletonCache, this._uuid); + spine.wasmUtil.registerSpineSkeletonDataWithUUID(this._skeletonCache, uuid); } return this._skeletonCache; @@ -268,13 +270,18 @@ export class SkeletonData extends Asset { } return null; } + + private mergedUUID (): string { + return this._uuid + murmurhash2_32_gc(this._atlasText, 668).toString(); + } + /** * @en Destroy skeleton data. * @zh 销毁 skeleton data。 */ public destroy (): boolean { SkeletonCache.sharedCache.destroyCachedAnimations(this._uuid); - spine.wasmUtil.destroySpineSkeletonDataWithUUID(this._uuid); + spine.wasmUtil.destroySpineSkeletonDataWithUUID(this.mergedUUID()); return super.destroy(); } } diff --git a/cocos/spine/skeleton.ts b/cocos/spine/skeleton.ts index e65cda2573a..e16e6972785 100644 --- a/cocos/spine/skeleton.ts +++ b/cocos/spine/skeleton.ts @@ -643,7 +643,6 @@ export class Skeleton extends UIRenderer { @override @type(Material) @displayOrder(0) - @displayName('CustomMaterial') get customMaterial (): Material | null { return this._customMaterial; } @@ -820,6 +819,8 @@ export class Skeleton extends UIRenderer { if (this._skeletonInfo !== skeletonInfo) { this._destroySkeletonInfo(this._skeletonCache); this._skeletonInfo = this._skeletonCache!.createSkeletonInfo(this._skeletonData!); + } + if (this._skeletonInfo) { this._skeleton = this._skeletonInfo.skeleton!; } } else { @@ -908,7 +909,8 @@ export class Skeleton extends UIRenderer { logID(7511); return null; } - const animation = this._skeleton.data.findAnimation(name); + const skeleton = this._skeleton; + const animation = skeleton ? skeleton.data.findAnimation(name) : null; if (!animation) { logID(7509, name); return null; @@ -1017,6 +1019,7 @@ export class Skeleton extends UIRenderer { * @param skinName @en The name of skin. @zh 皮肤名称。 */ public setSkin (name: string): void { + if (!name) return; if (this._skeleton) this._skeleton.setSkinByName(name); this._instance!.setSkin(name); if (this.isAnimationCached()) { diff --git a/cocos/ui/scroll-view.ts b/cocos/ui/scroll-view.ts index c50eedd7f79..164212f68bb 100644 --- a/cocos/ui/scroll-view.ts +++ b/cocos/ui/scroll-view.ts @@ -78,6 +78,18 @@ const eventMap = { 'scroll-began': 12, }; +const _moveDeltaOptions = { + anchor: v2(), + applyToHorizontal: false, + applyToVertical: false, +}; + +const assignMoveDeltaOption = (x: number, y: number, applyToHorizontal: boolean, applyToVertical: boolean): void => { + _moveDeltaOptions.anchor.set(x, y); + _moveDeltaOptions.applyToHorizontal = applyToHorizontal; + _moveDeltaOptions.applyToVertical = applyToVertical; +}; + /** * @en * Enum for ScrollView event type. @@ -324,10 +336,11 @@ export class ScrollView extends ViewGroup { @displayOrder(0) @tooltip('i18n:scrollview.horizontal_bar') get horizontalScrollBar (): ScrollBar | null { - if (this._horizontalScrollBar && !this._horizontalScrollBar.isValid) { + const horizontalScrollBar = this._horizontalScrollBar; + if (horizontalScrollBar && !horizontalScrollBar.isValid) { errorID(4303, 'horizontal', this.node.name); } - return this._horizontalScrollBar; + return horizontalScrollBar; } set horizontalScrollBar (value: ScrollBar | null) { @@ -366,10 +379,11 @@ export class ScrollView extends ViewGroup { @displayOrder(1) @tooltip('i18n:scrollview.vertical_bar') get verticalScrollBar (): ScrollBar | null { - if (this._verticalScrollBar && !this._verticalScrollBar.isValid) { + const verticalScrollBar = this._verticalScrollBar; + if (verticalScrollBar && !verticalScrollBar.isValid) { errorID(4303, 'vertical', this.node.name); } - return this._verticalScrollBar; + return verticalScrollBar; } set verticalScrollBar (value: ScrollBar | null) { @@ -486,14 +500,11 @@ export class ScrollView extends ViewGroup { * ``` */ public scrollToBottom (timeInSecond?: number, attenuated = true): void { - const moveDelta = this._calculateMovePercentDelta({ - anchor: new Vec2(0, 0), - applyToHorizontal: false, - applyToVertical: true, - }); + assignMoveDeltaOption(0, 0, false, true); + const moveDelta = this._calculateMovePercentDelta(_moveDeltaOptions); if (timeInSecond) { - this._startAutoScroll(moveDelta, timeInSecond, attenuated !== false); + this._startAutoScroll(moveDelta, timeInSecond, attenuated); } else { this._moveContent(moveDelta, true); } @@ -516,14 +527,11 @@ export class ScrollView extends ViewGroup { * ``` */ public scrollToTop (timeInSecond?: number, attenuated = true): void { - const moveDelta = this._calculateMovePercentDelta({ - anchor: new Vec2(0, 1), - applyToHorizontal: false, - applyToVertical: true, - }); + assignMoveDeltaOption(0, 1, false, true); + const moveDelta = this._calculateMovePercentDelta(_moveDeltaOptions); if (timeInSecond) { - this._startAutoScroll(moveDelta, timeInSecond, attenuated !== false); + this._startAutoScroll(moveDelta, timeInSecond, attenuated); } else { this._moveContent(moveDelta); } @@ -546,14 +554,11 @@ export class ScrollView extends ViewGroup { * ``` */ public scrollToLeft (timeInSecond?: number, attenuated = true): void { - const moveDelta = this._calculateMovePercentDelta({ - anchor: new Vec2(0, 0), - applyToHorizontal: true, - applyToVertical: false, - }); + assignMoveDeltaOption(0, 0, true, false); + const moveDelta = this._calculateMovePercentDelta(_moveDeltaOptions); if (timeInSecond) { - this._startAutoScroll(moveDelta, timeInSecond, attenuated !== false); + this._startAutoScroll(moveDelta, timeInSecond, attenuated); } else { this._moveContent(moveDelta); } @@ -576,14 +581,11 @@ export class ScrollView extends ViewGroup { * ``` */ public scrollToRight (timeInSecond?: number, attenuated = true): void { - const moveDelta = this._calculateMovePercentDelta({ - anchor: new Vec2(1, 0), - applyToHorizontal: true, - applyToVertical: false, - }); + assignMoveDeltaOption(1, 0, true, false); + const moveDelta = this._calculateMovePercentDelta(_moveDeltaOptions); if (timeInSecond) { - this._startAutoScroll(moveDelta, timeInSecond, attenuated !== false); + this._startAutoScroll(moveDelta, timeInSecond, attenuated); } else { this._moveContent(moveDelta); } @@ -606,14 +608,11 @@ export class ScrollView extends ViewGroup { * ``` */ public scrollToTopLeft (timeInSecond?: number, attenuated = true): void { - const moveDelta = this._calculateMovePercentDelta({ - anchor: new Vec2(0, 1), - applyToHorizontal: true, - applyToVertical: true, - }); + assignMoveDeltaOption(0, 1, true, true); + const moveDelta = this._calculateMovePercentDelta(_moveDeltaOptions); if (timeInSecond) { - this._startAutoScroll(moveDelta, timeInSecond, attenuated !== false); + this._startAutoScroll(moveDelta, timeInSecond, attenuated); } else { this._moveContent(moveDelta); } @@ -636,14 +635,11 @@ export class ScrollView extends ViewGroup { * ``` */ public scrollToTopRight (timeInSecond?: number, attenuated = true): void { - const moveDelta = this._calculateMovePercentDelta({ - anchor: new Vec2(1, 1), - applyToHorizontal: true, - applyToVertical: true, - }); + assignMoveDeltaOption(1, 1, true, true); + const moveDelta = this._calculateMovePercentDelta(_moveDeltaOptions); if (timeInSecond) { - this._startAutoScroll(moveDelta, timeInSecond, attenuated !== false); + this._startAutoScroll(moveDelta, timeInSecond, attenuated); } else { this._moveContent(moveDelta); } @@ -666,14 +662,11 @@ export class ScrollView extends ViewGroup { * ``` */ public scrollToBottomLeft (timeInSecond?: number, attenuated = true): void { - const moveDelta = this._calculateMovePercentDelta({ - anchor: new Vec2(0, 0), - applyToHorizontal: true, - applyToVertical: true, - }); + assignMoveDeltaOption(0, 0, true, true); + const moveDelta = this._calculateMovePercentDelta(_moveDeltaOptions); if (timeInSecond) { - this._startAutoScroll(moveDelta, timeInSecond, attenuated !== false); + this._startAutoScroll(moveDelta, timeInSecond, attenuated); } else { this._moveContent(moveDelta); } @@ -696,14 +689,11 @@ export class ScrollView extends ViewGroup { * ``` */ public scrollToBottomRight (timeInSecond?: number, attenuated = true): void { - const moveDelta = this._calculateMovePercentDelta({ - anchor: new Vec2(1, 0), - applyToHorizontal: true, - applyToVertical: true, - }); + assignMoveDeltaOption(1, 0, true, true); + const moveDelta = this._calculateMovePercentDelta(_moveDeltaOptions); if (timeInSecond) { - this._startAutoScroll(moveDelta, timeInSecond, attenuated !== false); + this._startAutoScroll(moveDelta, timeInSecond, attenuated); } else { this._moveContent(moveDelta); } @@ -732,7 +722,7 @@ export class ScrollView extends ViewGroup { public scrollToOffset (offset: Vec2, timeInSecond?: number, attenuated = true): void { const maxScrollOffset = this.getMaxScrollOffset(); - const anchor = new Vec2(0, 0); + const anchor = v2(); // if maxScrollOffset is 0, then always align the content's top left origin to the top left corner of its parent if (maxScrollOffset.x === 0) { anchor.x = 0; @@ -806,14 +796,11 @@ export class ScrollView extends ViewGroup { * ``` */ public scrollToPercentHorizontal (percent: number, timeInSecond: number, attenuated: boolean): void { - const moveDelta = this._calculateMovePercentDelta({ - anchor: new Vec2(percent, 0), - applyToHorizontal: true, - applyToVertical: false, - }); + assignMoveDeltaOption(percent, 0, true, false); + const moveDelta = this._calculateMovePercentDelta(_moveDeltaOptions); if (timeInSecond) { - this._startAutoScroll(moveDelta, timeInSecond, attenuated !== false); + this._startAutoScroll(moveDelta, timeInSecond, attenuated); } else { this._moveContent(moveDelta); } @@ -842,11 +829,8 @@ export class ScrollView extends ViewGroup { * ``` */ public scrollTo (anchor: Vec2, timeInSecond?: number, attenuated?: boolean): void { - const moveDelta = this._calculateMovePercentDelta({ - anchor: new Vec2(anchor), - applyToHorizontal: true, - applyToVertical: true, - }); + assignMoveDeltaOption(anchor.x, anchor.y, true, true); + const moveDelta = this._calculateMovePercentDelta(_moveDeltaOptions); if (timeInSecond) { this._startAutoScroll(moveDelta, timeInSecond, attenuated); @@ -873,11 +857,8 @@ export class ScrollView extends ViewGroup { * ``` */ public scrollToPercentVertical (percent: number, timeInSecond?: number, attenuated?: boolean): void { - const moveDelta = this._calculateMovePercentDelta({ - anchor: new Vec2(0, percent), - applyToHorizontal: false, - applyToVertical: true, - }); + assignMoveDeltaOption(0, percent, false, true); + const moveDelta = this._calculateMovePercentDelta(_moveDeltaOptions); if (timeInSecond) { this._startAutoScroll(moveDelta, timeInSecond, attenuated); @@ -912,7 +893,7 @@ export class ScrollView extends ViewGroup { this._setContentPosition(position); } - private _setContentPosition (position: Vec3): void { + private _setContentPosition (position: Readonly): void { if (!this._content) { return; } @@ -993,20 +974,26 @@ export class ScrollView extends ViewGroup { } public onEnable (): void { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + if (!EDITOR_NOT_IN_PREVIEW) { - this._registerEvent(); - if (this._content) { - this._content.on(NodeEventType.SIZE_CHANGED, this._calculateBoundary, this); - this._content.on(NodeEventType.TRANSFORM_CHANGED, this._scaleChanged, this); - if (this.view) { - this.view.node.on(NodeEventType.TRANSFORM_CHANGED, this._scaleChanged, this); - this.view.node.on(NodeEventType.SIZE_CHANGED, this._calculateBoundary, this); + self._registerEvent(); + const content = this._content; + if (content) { + content.on(NodeEventType.SIZE_CHANGED, self._calculateBoundary, self); + content.on(NodeEventType.TRANSFORM_CHANGED, self._scaleChanged, self); + + const view = self.view; + if (view) { + view.node.on(NodeEventType.TRANSFORM_CHANGED, self._scaleChanged, self); + view.node.on(NodeEventType.SIZE_CHANGED, self._calculateBoundary, self); } } - this._calculateBoundary(); + self._calculateBoundary(); } - this._updateScrollBarState(); + self._updateScrollBarState(); } public update (dt: number): void { @@ -1023,107 +1010,129 @@ export class ScrollView extends ViewGroup { } public onDisable (): void { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + if (!EDITOR_NOT_IN_PREVIEW) { - this._unregisterEvent(); - if (this._content) { - this._content.off(NodeEventType.SIZE_CHANGED, this._calculateBoundary, this); - this._content.off(NodeEventType.TRANSFORM_CHANGED, this._scaleChanged, this); - if (this.view) { - this.view.node.off(NodeEventType.TRANSFORM_CHANGED, this._scaleChanged, this); - this.view.node.off(NodeEventType.SIZE_CHANGED, this._calculateBoundary, this); + self._unregisterEvent(); + + const content = self.content; + if (content) { + content.off(NodeEventType.SIZE_CHANGED, self._calculateBoundary, self); + content.off(NodeEventType.TRANSFORM_CHANGED, self._scaleChanged, self); + + const view = self.view; + if (view) { + view.node.off(NodeEventType.TRANSFORM_CHANGED, self._scaleChanged, self); + view.node.off(NodeEventType.SIZE_CHANGED, self._calculateBoundary, self); } } } - this._deltaAmount.set(0, 0); - this._hideScrollBar(); - this.stopAutoScroll(); + self._deltaAmount.set(0, 0); + self._hideScrollBar(); + self.stopAutoScroll(); } // private methods protected _registerEvent (): void { - this.node.on(NodeEventType.TOUCH_START, this._onTouchBegan, this, true); - this.node.on(NodeEventType.TOUCH_MOVE, this._onTouchMoved, this, true); - this.node.on(NodeEventType.TOUCH_END, this._onTouchEnded, this, true); - this.node.on(NodeEventType.TOUCH_CANCEL, this._onTouchCancelled, this, true); - this.node.on(NodeEventType.MOUSE_WHEEL, this._onMouseWheel, this, true); + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + const node = self.node; + node.on(NodeEventType.TOUCH_START, self._onTouchBegan, self, true); + node.on(NodeEventType.TOUCH_MOVE, self._onTouchMoved, self, true); + node.on(NodeEventType.TOUCH_END, self._onTouchEnded, self, true); + node.on(NodeEventType.TOUCH_CANCEL, self._onTouchCancelled, self, true); + node.on(NodeEventType.MOUSE_WHEEL, self._onMouseWheel, self, true); - this.node.on(XrUIPressEventType.XRUI_HOVER_ENTERED, this._xrHoverEnter, this); - this.node.on(XrUIPressEventType.XRUI_HOVER_EXITED, this._xrHoverExit, this); + node.on(XrUIPressEventType.XRUI_HOVER_ENTERED, self._xrHoverEnter, self); + node.on(XrUIPressEventType.XRUI_HOVER_EXITED, self._xrHoverExit, self); - input.on(InputEventType.HANDLE_INPUT, this._dispatchEventHandleInput, this); - input.on(InputEventType.GAMEPAD_INPUT, this._dispatchEventHandleInput, this); + input.on(InputEventType.HANDLE_INPUT, self._dispatchEventHandleInput, self); + input.on(InputEventType.GAMEPAD_INPUT, self._dispatchEventHandleInput, self); } protected _unregisterEvent (): void { - this.node.off(NodeEventType.TOUCH_START, this._onTouchBegan, this, true); - this.node.off(NodeEventType.TOUCH_MOVE, this._onTouchMoved, this, true); - this.node.off(NodeEventType.TOUCH_END, this._onTouchEnded, this, true); - this.node.off(NodeEventType.TOUCH_CANCEL, this._onTouchCancelled, this, true); - this.node.off(NodeEventType.MOUSE_WHEEL, this._onMouseWheel, this, true); + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + const node = self.node; + node.off(NodeEventType.TOUCH_START, self._onTouchBegan, self, true); + node.off(NodeEventType.TOUCH_MOVE, self._onTouchMoved, self, true); + node.off(NodeEventType.TOUCH_END, self._onTouchEnded, self, true); + node.off(NodeEventType.TOUCH_CANCEL, self._onTouchCancelled, self, true); + node.off(NodeEventType.MOUSE_WHEEL, self._onMouseWheel, self, true); - this.node.off(XrUIPressEventType.XRUI_HOVER_ENTERED, this._xrHoverEnter, this); - this.node.off(XrUIPressEventType.XRUI_HOVER_EXITED, this._xrHoverExit, this); - input.off(InputEventType.HANDLE_INPUT, this._dispatchEventHandleInput, this); - input.off(InputEventType.GAMEPAD_INPUT, this._dispatchEventHandleInput, this); + node.off(XrUIPressEventType.XRUI_HOVER_ENTERED, self._xrHoverEnter, self); + node.off(XrUIPressEventType.XRUI_HOVER_EXITED, self._xrHoverExit, self); + input.off(InputEventType.HANDLE_INPUT, self._dispatchEventHandleInput, self); + input.off(InputEventType.GAMEPAD_INPUT, self._dispatchEventHandleInput, self); } protected _onMouseWheel (event: EventMouse, captureListeners?: Node[]): void { - if (!this.enabledInHierarchy) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + + if (self.enabledInHierarchy) { return; } - if (this._hasNestedViewGroup(event, captureListeners)) { + if (self._hasNestedViewGroup(event, captureListeners)) { return; } const deltaMove = new Vec3(); const wheelPrecision = -0.1; const scrollY = event.getScrollY(); - if (this.vertical) { + if (self.vertical) { deltaMove.set(0, scrollY * wheelPrecision, 0); - } else if (this.horizontal) { + } else if (self.horizontal) { deltaMove.set(scrollY * wheelPrecision, 0, 0); } - this._mouseWheelEventElapsedTime = 0; - this._deltaAmount.add(deltaMove); + self._mouseWheelEventElapsedTime = 0; + self._deltaAmount.add(deltaMove); - if (!this._stopMouseWheel) { - this._handlePressLogic(); - this.schedule(this._checkMouseWheel, 1.0 / 60); - this._stopMouseWheel = true; + if (!self._stopMouseWheel) { + self._handlePressLogic(); + self.schedule(this._checkMouseWheel, 1.0 / 60); + self._stopMouseWheel = true; } - this._stopPropagationIfTargetIsMe(event); + self._stopPropagationIfTargetIsMe(event); } protected _onTouchBegan (event: EventTouch, captureListeners?: Node[]): void { - if (!this.enabledInHierarchy || !this._content) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + + if (!self.enabledInHierarchy || !self._content) { return; } - if (this._hasNestedViewGroup(event, captureListeners)) { + if (self._hasNestedViewGroup(event, captureListeners)) { return; } - this._handlePressLogic(); + self._handlePressLogic(); - this._touchMoved = false; - this._stopPropagationIfTargetIsMe(event); + self._touchMoved = false; + self._stopPropagationIfTargetIsMe(event); } protected _onTouchMoved (event: EventTouch, captureListeners?: Node[]): void { - if (!this.enabledInHierarchy || !this._content) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + + if (!self.enabledInHierarchy || !self._content) { return; } - if (this._hasNestedViewGroup(event, captureListeners)) { + if (self._hasNestedViewGroup(event, captureListeners)) { return; } const touch = event.touch!; - this._handleMoveLogic(touch); + self._handleMoveLogic(touch); // Do not prevent touch events in inner nodes - if (!this.cancelInnerEvents) { + if (!self.cancelInnerEvents) { return; } @@ -1131,73 +1140,81 @@ export class ScrollView extends ViewGroup { deltaMove.subtract(touch.getUIStartLocation(_tempVec2_1)); // FIXME: touch move delta should be calculated by DPI. if (deltaMove.length() > 7) { - if (!this._touchMoved && event.target !== this.node) { + if (!self._touchMoved && event.target !== self.node) { // Simulate touch cancel for target node const cancelEvent = new EventTouch(event.getTouches(), event.bubbles, SystemEventType.TOUCH_CANCEL); cancelEvent.touch = event.touch; cancelEvent.simulate = true; (event.target as Node).dispatchEvent(cancelEvent); - this._touchMoved = true; + self._touchMoved = true; } } - this._stopPropagationIfTargetIsMe(event); + self._stopPropagationIfTargetIsMe(event); } protected _onTouchEnded (event: EventTouch, captureListeners?: Node[]): void { - if (!this.enabledInHierarchy || !this._content || !event) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + + if (!self.enabledInHierarchy || !self._content || !event) { return; } - if (this._hasNestedViewGroup(event, captureListeners)) { + if (self._hasNestedViewGroup(event, captureListeners)) { return; } - this._dispatchEvent(ScrollViewEventType.TOUCH_UP); + self._dispatchEvent(ScrollViewEventType.TOUCH_UP); const touch = event.touch!; - this._handleReleaseLogic(touch); + self._handleReleaseLogic(touch); - if (this._touchMoved) { + if (self._touchMoved) { event.propagationStopped = true; } else { - this._stopPropagationIfTargetIsMe(event); + self._stopPropagationIfTargetIsMe(event); } } protected _onTouchCancelled (event: EventTouch, captureListeners?: Node[]): void { - if (!this.enabledInHierarchy || !this._content) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + + if (!self.enabledInHierarchy || !self._content) { return; } - if (this._hasNestedViewGroup(event, captureListeners)) { + if (self._hasNestedViewGroup(event, captureListeners)) { return; } // Filter touch cancel event send from self if (event && !event.simulate) { - const touch = event.touch!; - this._handleReleaseLogic(touch); + self._handleReleaseLogic(event.touch!); } - this._stopPropagationIfTargetIsMe(event); + self._stopPropagationIfTargetIsMe(event); } protected _calculateBoundary (): void { - if (this._content && this.view) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + + if (self._content && self.view) { // refresh content size - const layout = this._content.getComponent(Layout); + const layout = self._content.getComponent(Layout); if (layout && layout.enabledInHierarchy) { layout.updateLayout(); } - const viewTrans = this.view; + const viewTrans = self.view; const anchorX = viewTrans.width * viewTrans.anchorX; const anchorY = viewTrans.height * viewTrans.anchorY; - this._leftBoundary = -anchorX; - this._bottomBoundary = -anchorY; + self._leftBoundary = -anchorX; + self._bottomBoundary = -anchorY; - this._rightBoundary = this._leftBoundary + viewTrans.width; - this._topBoundary = this._bottomBoundary + viewTrans.height; + self._rightBoundary = self._leftBoundary + viewTrans.width; + self._topBoundary = self._bottomBoundary + viewTrans.height; - this._moveContentToTopLeft(viewTrans.contentSize); + self._moveContentToTopLeft(viewTrans.contentSize); } } @@ -1209,16 +1226,14 @@ export class ScrollView extends ViewGroup { if (captureListeners) { // captureListeners are arranged from child to parent for (const listener of captureListeners) { - const item = listener; - - if (this.node === item) { + if (this.node === listener) { if (event.target && (event.target as Node).getComponent(ViewGroup)) { return true; } return false; } - if (item.getComponent(ViewGroup)) { + if (listener.getComponent(ViewGroup)) { return true; } } @@ -1261,13 +1276,12 @@ export class ScrollView extends ViewGroup { const originalMoveLength = deltaMove.length(); let factor = targetDelta.length() / originalMoveLength; - targetDelta.add(deltaMove); if (this.brake > 0 && factor > 7) { factor = Math.sqrt(factor); - const clonedDeltaMove = deltaMove.clone(); - clonedDeltaMove.multiplyScalar(factor); - targetDelta.set(clonedDeltaMove); + targetDelta.set(deltaMove); + targetDelta.multiplyScalar(factor + 1); + } else { targetDelta.add(deltaMove); } @@ -1289,19 +1303,22 @@ export class ScrollView extends ViewGroup { } protected _startAutoScroll (deltaMove: Vec3, timeInSecond: number, attenuated = false): void { - const adjustedDeltaMove = this._flattenVectorByDirection(deltaMove); - - this._autoScrolling = true; - this._autoScrollTargetDelta = adjustedDeltaMove; - this._autoScrollAttenuate = attenuated; - Vec3.copy(this._autoScrollStartPosition, this._getContentPosition()); - this._autoScrollTotalTime = timeInSecond; - this._autoScrollAccumulatedTime = 0; - this._autoScrollBraking = false; - this._isScrollEndedWithThresholdEventFired = false; - this._autoScrollBrakingStartPosition.set(0, 0, 0); - - const currentOutOfBoundary = this._getHowMuchOutOfBoundary(); + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + + const adjustedDeltaMove = self._flattenVectorByDirection(deltaMove); + + self._autoScrolling = true; + self._autoScrollTargetDelta = adjustedDeltaMove; + self._autoScrollAttenuate = attenuated; + Vec3.copy(self._autoScrollStartPosition, self._getContentPosition()); + self._autoScrollTotalTime = timeInSecond; + self._autoScrollAccumulatedTime = 0; + self._autoScrollBraking = false; + self._isScrollEndedWithThresholdEventFired = false; + self._autoScrollBrakingStartPosition.set(0, 0, 0); + + const currentOutOfBoundary = self._getHowMuchOutOfBoundary(); if (!currentOutOfBoundary.equals(Vec3.ZERO, EPSILON)) { this._autoScrollCurrentlyOutOfBoundary = true; } @@ -1331,10 +1348,9 @@ export class ScrollView extends ViewGroup { } protected _flattenVectorByDirection (vector: Vec3): Vec3 { - const result = vector; - result.x = this.horizontal ? result.x : 0; - result.y = this.vertical ? result.y : 0; - return result; + if (!this.horizontal) vector.x = 0; + if (!this.vertical) vector.y = 0; + return vector; } protected _moveContent (deltaMove: Vec3, canStartBounceBack?: boolean): void { @@ -1494,24 +1510,30 @@ export class ScrollView extends ViewGroup { } protected _updateScrollBarState (): void { - if (!this._content || !this.view) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + + if (!self._content || !self.view) { return; } - const viewTrans = this.view; - const uiTrans = this._content._uiProps.uiTransformComp!; - if (this._verticalScrollBar && this._verticalScrollBar.isValid) { + const viewTrans = self.view; + const uiTrans = self._content._uiProps.uiTransformComp!; + + const verticalScrollBar = self._verticalScrollBar; + if (verticalScrollBar && verticalScrollBar.isValid) { if (uiTrans.height < viewTrans.height || approx(uiTrans.height, viewTrans.height)) { - this._verticalScrollBar.hide(); + verticalScrollBar.hide(); } else { - this._verticalScrollBar.show(); + verticalScrollBar.show(); } } - if (this._horizontalScrollBar && this._horizontalScrollBar.isValid) { + const horizontalScrollBar = self._horizontalScrollBar; + if (horizontalScrollBar && horizontalScrollBar.isValid) { if (uiTrans.width < viewTrans.width || approx(uiTrans.width, viewTrans.width)) { - this._horizontalScrollBar.hide(); + horizontalScrollBar.hide(); } else { - this._horizontalScrollBar.show(); + horizontalScrollBar.show(); } } } @@ -1534,21 +1556,23 @@ export class ScrollView extends ViewGroup { } protected _handleReleaseLogic (touch: Touch): void { - this._getLocalAxisAlignDelta(this._deltaPos, touch); - this._gatherTouchMove(this._deltaPos); - this._processInertiaScroll(); + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; - if (this._scrolling) { - this._scrolling = false; - if (!this._autoScrolling) { - this._dispatchEvent(ScrollViewEventType.SCROLL_ENDED); + self._getLocalAxisAlignDelta(self._deltaPos, touch); + self._gatherTouchMove(self._deltaPos); + self._processInertiaScroll(); + + if (self._scrolling) { + self._scrolling = false; + if (!self._autoScrolling) { + self._dispatchEvent(ScrollViewEventType.SCROLL_ENDED); } } } protected _getLocalAxisAlignDelta (out: Vec3, touch: Touch): void { const uiTransformComp = this.node._uiProps.uiTransformComp; - const vec = new Vec3(); if (uiTransformComp) { touch.getUILocation(_tempVec2); @@ -1557,96 +1581,100 @@ export class ScrollView extends ViewGroup { _tempVec3_1.set(_tempVec2_1.x, _tempVec2_1.y, 0); uiTransformComp.convertToNodeSpaceAR(_tempVec3, _tempVec3); uiTransformComp.convertToNodeSpaceAR(_tempVec3_1, _tempVec3_1); - Vec3.subtract(vec, _tempVec3, _tempVec3_1); + Vec3.subtract(out, _tempVec3, _tempVec3_1); } - - out.set(vec); } protected _scrollChildren (deltaMove: Vec3): void { - this._clampDelta(deltaMove); + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + + self._clampDelta(deltaMove); const realMove = deltaMove; let outOfBoundary: Vec3; - if (this.elastic) { - outOfBoundary = this._getHowMuchOutOfBoundary(); + if (self.elastic) { + outOfBoundary = self._getHowMuchOutOfBoundary(); realMove.x *= outOfBoundary.x === 0 ? 1 : 0.5; realMove.y *= outOfBoundary.y === 0 ? 1 : 0.5; } - if (!this.elastic) { - outOfBoundary = this._getHowMuchOutOfBoundary(realMove); + if (!self.elastic) { + outOfBoundary = self._getHowMuchOutOfBoundary(realMove); realMove.add(outOfBoundary); } let verticalScrollEventType: ScrollViewEventType = ScrollViewEventType.NONE; let horizontalScrollEventType: ScrollViewEventType = ScrollViewEventType.NONE; - if (this._content) { - const { anchorX, anchorY, width, height } = this._content._uiProps.uiTransformComp!; - const pos = this._content.position || Vec3.ZERO; + if (self._content) { + const { anchorX, anchorY, width, height } = self._content._uiProps.uiTransformComp!; + const pos = self._content.position || Vec3.ZERO; - if (this.vertical) { + if (self.vertical) { if (realMove.y > 0) { // up const icBottomPos = pos.y - anchorY * height; - if (icBottomPos + realMove.y >= this._bottomBoundary) { + if (icBottomPos + realMove.y >= self._bottomBoundary) { verticalScrollEventType = ScrollViewEventType.SCROLL_TO_BOTTOM; } } else if (realMove.y < 0) { // down const icTopPos = pos.y - anchorY * height + height; - if (icTopPos + realMove.y <= this._topBoundary) { + if (icTopPos + realMove.y <= self._topBoundary) { verticalScrollEventType = ScrollViewEventType.SCROLL_TO_TOP; } } } - if (this.horizontal) { + if (self.horizontal) { if (realMove.x < 0) { // left const icRightPos = pos.x - anchorX * width + width; - if (icRightPos + realMove.x <= this._rightBoundary) { + if (icRightPos + realMove.x <= self._rightBoundary) { horizontalScrollEventType = ScrollViewEventType.SCROLL_TO_RIGHT; } } else if (realMove.x > 0) { // right const icLeftPos = pos.x - anchorX * width; - if (icLeftPos + realMove.x >= this._leftBoundary) { + if (icLeftPos + realMove.x >= self._leftBoundary) { horizontalScrollEventType = ScrollViewEventType.SCROLL_TO_LEFT; } } } } - this._moveContent(realMove, false); + self._moveContent(realMove, false); - if ((this.horizontal && realMove.x !== 0) || (this.vertical && realMove.y !== 0)) { - if (!this._scrolling) { - this._scrolling = true; - this._dispatchEvent(ScrollViewEventType.SCROLL_BEGAN); + if ((self.horizontal && realMove.x !== 0) || (self.vertical && realMove.y !== 0)) { + if (!self._scrolling) { + self._scrolling = true; + self._dispatchEvent(ScrollViewEventType.SCROLL_BEGAN); } - this._dispatchEvent(ScrollViewEventType.SCROLLING); + self._dispatchEvent(ScrollViewEventType.SCROLLING); } if (verticalScrollEventType !== ScrollViewEventType.NONE) { - this._dispatchEvent(verticalScrollEventType); + self._dispatchEvent(verticalScrollEventType); } if (horizontalScrollEventType !== ScrollViewEventType.NONE) { - this._dispatchEvent(horizontalScrollEventType); + self._dispatchEvent(horizontalScrollEventType); } } protected _handlePressLogic (): void { - if (this._autoScrolling) { - this._dispatchEvent(ScrollViewEventType.SCROLL_ENDED); + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + + if (self._autoScrolling) { + self._dispatchEvent(ScrollViewEventType.SCROLL_ENDED); } - this._autoScrolling = false; - this._isBouncing = false; + self._autoScrolling = false; + self._isBouncing = false; - this._touchMovePreviousTimestamp = getTimeInMilliseconds(); - this._touchMoveDisplacements.length = 0; - this._touchMoveTimeDeltas.length = 0; + self._touchMovePreviousTimestamp = getTimeInMilliseconds(); + self._touchMoveDisplacements.length = 0; + self._touchMoveTimeDeltas.length = 0; - this._onScrollBarTouchBegan(); + self._onScrollBarTouchBegan(); } protected _clampDelta (out: Vec3): void { @@ -1663,50 +1691,56 @@ export class ScrollView extends ViewGroup { } protected _gatherTouchMove (delta: Vec3): void { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + const clampDt = delta.clone(); - this._clampDelta(clampDt); + self._clampDelta(clampDt); - while (this._touchMoveDisplacements.length >= NUMBER_OF_GATHERED_TOUCHES_FOR_MOVE_SPEED) { - this._touchMoveDisplacements.shift(); - this._touchMoveTimeDeltas.shift(); + while (self._touchMoveDisplacements.length >= NUMBER_OF_GATHERED_TOUCHES_FOR_MOVE_SPEED) { + self._touchMoveDisplacements.shift(); + self._touchMoveTimeDeltas.shift(); } - this._touchMoveDisplacements.push(clampDt); + self._touchMoveDisplacements.push(clampDt); const timeStamp = getTimeInMilliseconds(); - this._touchMoveTimeDeltas.push((timeStamp - this._touchMovePreviousTimestamp) / 1000); - this._touchMovePreviousTimestamp = timeStamp; + self._touchMoveTimeDeltas.push((timeStamp - self._touchMovePreviousTimestamp) / 1000); + self._touchMovePreviousTimestamp = timeStamp; } protected _startBounceBackIfNeeded (): boolean { - if (!this.elastic) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + + if (!self.elastic) { return false; } - const bounceBackAmount = this._getHowMuchOutOfBoundary(); - this._clampDelta(bounceBackAmount); + const bounceBackAmount = self._getHowMuchOutOfBoundary(); + self._clampDelta(bounceBackAmount); if (bounceBackAmount.equals(Vec3.ZERO, EPSILON)) { return false; } - const bounceBackTime = Math.max(this.bounceDuration, 0); - this._startAutoScroll(bounceBackAmount, bounceBackTime, true); + const bounceBackTime = Math.max(self.bounceDuration, 0); + self._startAutoScroll(bounceBackAmount, bounceBackTime, true); - if (!this._isBouncing) { + if (!self._isBouncing) { if (bounceBackAmount.y > 0) { - this._dispatchEvent(ScrollViewEventType.BOUNCE_TOP); + self._dispatchEvent(ScrollViewEventType.BOUNCE_TOP); } if (bounceBackAmount.y < 0) { - this._dispatchEvent(ScrollViewEventType.BOUNCE_BOTTOM); + self._dispatchEvent(ScrollViewEventType.BOUNCE_BOTTOM); } if (bounceBackAmount.x > 0) { - this._dispatchEvent(ScrollViewEventType.BOUNCE_RIGHT); + self._dispatchEvent(ScrollViewEventType.BOUNCE_RIGHT); } if (bounceBackAmount.x < 0) { - this._dispatchEvent(ScrollViewEventType.BOUNCE_LEFT); + self._dispatchEvent(ScrollViewEventType.BOUNCE_LEFT); } - this._isBouncing = true; + self._isBouncing = true; } return true; @@ -1730,58 +1764,64 @@ export class ScrollView extends ViewGroup { } protected _isNecessaryAutoScrollBrake (): boolean { - if (this._autoScrollBraking) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + + if (self._autoScrollBraking) { return true; } - if (this._isOutOfBoundary()) { - if (!this._autoScrollCurrentlyOutOfBoundary) { - this._autoScrollCurrentlyOutOfBoundary = true; - this._autoScrollBraking = true; - Vec3.copy(this._autoScrollBrakingStartPosition, this._getContentPosition()); + if (self._isOutOfBoundary()) { + if (!self._autoScrollCurrentlyOutOfBoundary) { + self._autoScrollCurrentlyOutOfBoundary = true; + self._autoScrollBraking = true; + Vec3.copy(self._autoScrollBrakingStartPosition, self._getContentPosition()); return true; } } else { - this._autoScrollCurrentlyOutOfBoundary = false; + self._autoScrollCurrentlyOutOfBoundary = false; } return false; } protected _processAutoScrolling (dt): void { - const isAutoScrollBrake = this._isNecessaryAutoScrollBrake(); + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + + const isAutoScrollBrake = self._isNecessaryAutoScrollBrake(); const brakingFactor = isAutoScrollBrake ? OUT_OF_BOUNDARY_BREAKING_FACTOR : 1; - this._autoScrollAccumulatedTime += dt * (1 / brakingFactor); + self._autoScrollAccumulatedTime += dt * (1 / brakingFactor); - let percentage = Math.min(1, this._autoScrollAccumulatedTime / this._autoScrollTotalTime); - if (this._autoScrollAttenuate) { + let percentage = Math.min(1, self._autoScrollAccumulatedTime / self._autoScrollTotalTime); + if (self._autoScrollAttenuate) { percentage = quintEaseOut(percentage); } - const clonedAutoScrollTargetDelta = this._autoScrollTargetDelta.clone(); + const clonedAutoScrollTargetDelta = self._autoScrollTargetDelta.clone(); clonedAutoScrollTargetDelta.multiplyScalar(percentage); - const clonedAutoScrollStartPosition = this._autoScrollStartPosition.clone(); + const clonedAutoScrollStartPosition = self._autoScrollStartPosition.clone(); clonedAutoScrollStartPosition.add(clonedAutoScrollTargetDelta); let reachedEnd = Math.abs(percentage - 1) <= EPSILON; - const fireEvent = Math.abs(percentage - 1) <= this.getScrollEndedEventTiming(); - if (fireEvent && !this._isScrollEndedWithThresholdEventFired) { - this._dispatchEvent(ScrollViewEventType.SCROLL_ENG_WITH_THRESHOLD); - this._isScrollEndedWithThresholdEventFired = true; + const fireEvent = Math.abs(percentage - 1) <= self.getScrollEndedEventTiming(); + if (fireEvent && !self._isScrollEndedWithThresholdEventFired) { + self._dispatchEvent(ScrollViewEventType.SCROLL_ENG_WITH_THRESHOLD); + self._isScrollEndedWithThresholdEventFired = true; } - if (this.elastic) { + if (self.elastic) { const brakeOffsetPosition = clonedAutoScrollStartPosition.clone(); - brakeOffsetPosition.subtract(this._autoScrollBrakingStartPosition); + brakeOffsetPosition.subtract(self._autoScrollBrakingStartPosition); if (isAutoScrollBrake) { brakeOffsetPosition.multiplyScalar(brakingFactor); } - clonedAutoScrollStartPosition.set(this._autoScrollBrakingStartPosition); + clonedAutoScrollStartPosition.set(self._autoScrollBrakingStartPosition); clonedAutoScrollStartPosition.add(brakeOffsetPosition); } else { const moveDelta = clonedAutoScrollStartPosition.clone(); - moveDelta.subtract(this.getContentPosition()); - const outOfBoundary = this._getHowMuchOutOfBoundary(moveDelta); + moveDelta.subtract(self.getContentPosition()); + const outOfBoundary = self._getHowMuchOutOfBoundary(moveDelta); if (!outOfBoundary.equals(Vec3.ZERO, EPSILON)) { clonedAutoScrollStartPosition.add(outOfBoundary); reachedEnd = true; @@ -1789,52 +1829,55 @@ export class ScrollView extends ViewGroup { } if (reachedEnd) { - this._autoScrolling = false; + self._autoScrolling = false; } const deltaMove = clonedAutoScrollStartPosition.clone(); - deltaMove.subtract(this._getContentPosition()); - this._clampDelta(deltaMove); - this._moveContent(deltaMove, reachedEnd); - this._dispatchEvent(ScrollViewEventType.SCROLLING); + deltaMove.subtract(self._getContentPosition()); + self._clampDelta(deltaMove); + self._moveContent(deltaMove, reachedEnd); + self._dispatchEvent(ScrollViewEventType.SCROLLING); - if (!this._autoScrolling) { - this._isBouncing = false; - this._scrolling = false; - this._dispatchEvent(ScrollViewEventType.SCROLL_ENDED); + if (!self._autoScrolling) { + self._isBouncing = false; + self._scrolling = false; + self._dispatchEvent(ScrollViewEventType.SCROLL_ENDED); } } protected _checkMouseWheel (dt: number): void { - const currentOutOfBoundary = this._getHowMuchOutOfBoundary(); + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + + const currentOutOfBoundary = self._getHowMuchOutOfBoundary(); const maxElapsedTime = 0.1; if (!currentOutOfBoundary.equals(Vec3.ZERO, EPSILON)) { - this._processInertiaScroll(); - if (this._scrolling) { - this._scrolling = false; - if (!this._autoScrolling) { - this._dispatchEvent(ScrollViewEventType.SCROLL_ENDED); + self._processInertiaScroll(); + if (self._scrolling) { + self._scrolling = false; + if (!self._autoScrolling) { + self._dispatchEvent(ScrollViewEventType.SCROLL_ENDED); } } - this.unschedule(this._checkMouseWheel); - this._stopMouseWheel = false; + self.unschedule(self._checkMouseWheel); + self._stopMouseWheel = false; return; } - this._mouseWheelEventElapsedTime += dt; + self._mouseWheelEventElapsedTime += dt; // mouse wheel event is ended - if (this._mouseWheelEventElapsedTime > maxElapsedTime) { - this._onScrollBarTouchEnded(); - if (this._scrolling) { - this._scrolling = false; - if (!this._autoScrolling) { - this._dispatchEvent(ScrollViewEventType.SCROLL_ENDED); + if (self._mouseWheelEventElapsedTime > maxElapsedTime) { + self._onScrollBarTouchEnded(); + if (self._scrolling) { + self._scrolling = false; + if (!self._autoScrolling) { + self._dispatchEvent(ScrollViewEventType.SCROLL_ENDED); } } - this.unschedule(this._checkMouseWheel); - this._stopMouseWheel = false; + self.unschedule(self._checkMouseWheel); + self._stopMouseWheel = false; } } @@ -1842,22 +1885,24 @@ export class ScrollView extends ViewGroup { const anchor = options.anchor; const applyToHorizontal = options.applyToHorizontal; const applyToVertical = options.applyToVertical; - this._calculateBoundary(); + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + self._calculateBoundary(); anchor.clampf(Vec2.ZERO, Vec2.ONE); - let bottomDelta = this._getContentBottomBoundary() - this._bottomBoundary; + let bottomDelta = self._getContentBottomBoundary() - self._bottomBoundary; bottomDelta = -bottomDelta; - let leftDelta = this._getContentLeftBoundary() - this._leftBoundary; + let leftDelta = self._getContentLeftBoundary() - self._leftBoundary; leftDelta = -leftDelta; const moveDelta = new Vec3(); - if (this._content && this.view) { + if (self._content && self.view) { let totalScrollDelta = 0; - const uiTrans = this._content._uiProps.uiTransformComp!; + const uiTrans = self._content._uiProps.uiTransformComp!; const contentSize = uiTrans.contentSize; - const scrollSize = this.view.contentSize; + const scrollSize = self.view.contentSize; if (applyToHorizontal) { totalScrollDelta = contentSize.width - scrollSize.width; moveDelta.x = leftDelta - totalScrollDelta * anchor.x; @@ -1872,17 +1917,20 @@ export class ScrollView extends ViewGroup { } protected _moveContentToTopLeft (scrollViewSize: Size): void { - let bottomDelta = this._getContentBottomBoundary() - this._bottomBoundary; + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + + let bottomDelta = self._getContentBottomBoundary() - self._bottomBoundary; bottomDelta = -bottomDelta; const moveDelta = new Vec3(); let totalScrollDelta = 0; - let leftDelta = this._getContentLeftBoundary() - this._leftBoundary; + let leftDelta = self._getContentLeftBoundary() - self._leftBoundary; leftDelta = -leftDelta; // 是否限制在上视区上边 - if (this._content) { - const uiTrans = this._content._uiProps.uiTransformComp!; + if (self._content) { + const uiTrans = self._content._uiProps.uiTransformComp!; const contentSize = uiTrans.contentSize; if (contentSize.height < scrollViewSize.height) { totalScrollDelta = contentSize.height - scrollViewSize.height; @@ -1896,9 +1944,9 @@ export class ScrollView extends ViewGroup { } } - this._updateScrollBarState(); - this._moveContent(moveDelta); - this._adjustContentOutOfBoundary(); + self._updateScrollBarState(); + self._moveContent(moveDelta); + self._adjustContentOutOfBoundary(); } protected _scaleChanged (value: TransformBit): void { @@ -1944,26 +1992,29 @@ export class ScrollView extends ViewGroup { } protected _xrThumbStickMove (event: Vec2): void { - if (!this.enabledInHierarchy) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + + if (!self.enabledInHierarchy) { return; } const deltaMove = new Vec3(); const wheelPrecision = -62.5; const scrollY = event.y; - if (this.vertical) { + if (self.vertical) { deltaMove.set(0, scrollY * wheelPrecision, 0); - } else if (this.horizontal) { + } else if (self.horizontal) { deltaMove.set(scrollY * wheelPrecision, 0, 0); } - this._mouseWheelEventElapsedTime = 0; - this._deltaAmount.add(deltaMove); + self._mouseWheelEventElapsedTime = 0; + self._deltaAmount.add(deltaMove); - if (!this._stopMouseWheel) { - this._handlePressLogic(); - this.schedule(this._checkMouseWheel, 1.0 / 60, NaN, 0); - this._stopMouseWheel = true; + if (!self._stopMouseWheel) { + self._handlePressLogic(); + self.schedule(self._checkMouseWheel, 1.0 / 60, NaN, 0); + self._stopMouseWheel = true; } } } diff --git a/cocos/ui/view.ts b/cocos/ui/view.ts index d4d2266fe2c..ad676ab24db 100644 --- a/cocos/ui/view.ts +++ b/cocos/ui/view.ts @@ -34,6 +34,7 @@ import { visibleRect, cclegacy, errorID, screen, macro, System, assert } from '. import { Orientation } from '../../pal/screen-adapter/enum-type'; import { director } from '../game/director'; import { settings, SettingsCategory } from '../core/settings'; +import type { Root } from '../root'; /** * @en View represents the game window.
@@ -576,8 +577,11 @@ export class View extends Eventify(System) { return out; } - // Convert location in Cocos screen coordinate to location in UI space - private _convertToUISpace (point): void { + /** + * Convert location in Cocos screen coordinate to location in UI space + * @engineInternal + */ + public _convertToUISpace (point: Vec2): void { const viewport = this._viewportRect$; point.x = (point.x - viewport.x) / this._scaleX$; point.y = (point.y - viewport.y) / this._scaleY$; @@ -585,7 +589,7 @@ export class View extends Eventify(System) { private _updateAdaptResult$ (width: number, height: number, windowId?: number): void { // The default invalid windowId is 0 - cclegacy.director.root.resize(width, height, (windowId === undefined || windowId === 0) ? 1 : windowId); + (cclegacy.director.root as Root).resize(width, height, (windowId === undefined || windowId === 0) ? 1 : windowId); // Frame size changed, do resize works const w = this._designResolutionSize.width; const h = this._designResolutionSize.height; diff --git a/editor/engine-features/render-config.json b/editor/engine-features/render-config.json index cd974d1c154..da68694325c 100644 --- a/editor/engine-features/render-config.json +++ b/editor/engine-features/render-config.json @@ -46,7 +46,7 @@ "description": "i18n:ENGINE.features.base_3d.description", "enginePlugin": true, "isNativeModule": true, - "readonly": true, + "readonly": false, "dependencies": [ "meshopt" ], @@ -62,7 +62,7 @@ "label": "i18n:ENGINE.features.base_2d.label", "description": "i18n:ENGINE.features.base_2d.description", "category": "2d", - "readonly": true, + "readonly": false, "enginePlugin": true }, "xr": { @@ -76,6 +76,9 @@ "label": "i18n:ENGINE.features.ui.label", "description": "i18n:ENGINE.features.ui.description", "category": "2d", + "dependencies": [ + "2d" + ], "enginePlugin": true }, "particle": { @@ -83,12 +86,18 @@ "label": "i18n:ENGINE.features.particle.label", "description": "i18n:ENGINE.features.particle.description", "category": "3d", + "dependencies": [ + "3d" + ], "enginePlugin": true }, "physics": { "label": "i18n:ENGINE.features.physics.label", "description": "i18n:ENGINE.features.physics.description", "category": "3d", + "dependencies": [ + "3d" + ], "options": { "physics-ammo": { "label": "i18n:ENGINE.features.physics_ammo.label", @@ -116,6 +125,9 @@ "label": "i18n:ENGINE.features.physics_2d.label", "description": "i18n:ENGINE.features.physics_2d.description", "category": "2d", + "dependencies": [ + "2d" + ], "options": { "physics-2d-box2d": { "label": "i18n:ENGINE.features.physics_2d_box2d.label", @@ -134,14 +146,20 @@ "label": "i18n:ENGINE.features.intersection_2d.label", "description": "i18n:ENGINE.features.intersection_2d.description", "enginePlugin": true, - "category": "2d" + "category": "2d", + "dependencies": [ + "2d" + ] }, "primitive": { "default": true, "label": "i18n:ENGINE.features.primitives.label", "description": "i18n:ENGINE.features.primitives.description", "enginePlugin": true, - "category": "3d" + "category": "3d", + "dependencies": [ + "3d" + ] }, "profiler": { "default": true, @@ -155,7 +173,10 @@ "description": "i18n:ENGINE.features.occlusion_query.description", "cmakeConfig": "USE_OCCLUSION_QUERY", "enginePlugin": false, - "category": "3d" + "category": "3d", + "dependencies": [ + "3d" + ] }, "geometry-renderer": { "default": false, @@ -163,7 +184,10 @@ "description": "i18n:ENGINE.features.geometry_renderer.description", "cmakeConfig": "USE_GEOMETRY_RENDERER", "enginePlugin": true, - "category": "3d" + "category": "3d", + "dependencies": [ + "3d" + ] }, "debug-renderer": { "default": false, @@ -171,14 +195,20 @@ "description": "i18n:ENGINE.features.debug_renderer.description", "cmakeConfig": "USE_DEBUG_RENDERER", "enginePlugin": false, - "category": "3d" + "category": "3d", + "dependencies": [ + "3d" + ] }, "particle-2d": { "default": true, "label": "i18n:ENGINE.features.particle_2d.label", "description": "i18n:ENGINE.features.particle_2d.description", "enginePlugin": true, - "category": "2d" + "category": "2d", + "dependencies": [ + "2d" + ] }, "audio": { "default": true, @@ -228,21 +258,30 @@ "label": "i18n:ENGINE.features.terrain.label", "description": "i18n:ENGINE.features.terrain.description", "enginePlugin": true, - "category": "3d" + "category": "3d", + "dependencies": [ + "3d" + ] }, "light-probe": { "default": true, "label": "i18n:ENGINE.features.light_probe.label", "description": "i18n:ENGINE.features.light_probe.description", "enginePlugin": true, - "category": "3d" + "category": "3d", + "dependencies": [ + "3d" + ] }, "tiled-map": { "default": true, "label": "i18n:ENGINE.features.tiled_map.label", "description": "i18n:ENGINE.features.tiled_map.description", "enginePlugin": true, - "category": "2d" + "category": "2d", + "dependencies": [ + "2d" + ] }, "spine": { "default": true, @@ -251,7 +290,10 @@ "cmakeConfig": "USE_SPINE", "isNativeModule": true, "enginePlugin": true, - "category": "2d" + "category": "2d", + "dependencies": [ + "2d" + ] }, "dragon-bones": { "default": true, @@ -259,7 +301,10 @@ "description": "i18n:ENGINE.features.dragon_bones.description", "cmakeConfig": "USE_DRAGONBONES", "enginePlugin": true, - "category": "2d" + "category": "2d", + "dependencies": [ + "2d" + ] }, "marionette": { "default": true, @@ -304,7 +349,8 @@ "graphics": { "label": "i18n:ENGINE.features.graphics.label", "description": "i18n:ENGINE.features.graphics.description", - "checkable": true + "checkable": true, + "required": true }, "2d": { "label": "i18n:ENGINE.features.categories.2d.label", @@ -327,4 +373,4 @@ "checkable": true } } -} \ No newline at end of file +} diff --git a/editor/engine-features/schema.json b/editor/engine-features/schema.json index 7ec9a2c7164..2c98729797d 100644 --- a/editor/engine-features/schema.json +++ b/editor/engine-features/schema.json @@ -55,6 +55,9 @@ }, "label": { "type": "string" + }, + "required": { + "type": "boolean" } }, "type": "object" @@ -406,4 +409,3 @@ }, "type": "object" } - diff --git a/editor/engine-features/types.ts b/editor/engine-features/types.ts index b2b463f51fd..2f6a7114b94 100644 --- a/editor/engine-features/types.ts +++ b/editor/engine-features/types.ts @@ -104,4 +104,5 @@ export interface CategoryInfo { label?: string; description?: string; checkable?: boolean; + required?: boolean; } diff --git a/editor/i18n/en/localization.js b/editor/i18n/en/localization.js index 7dc087d9334..89406e3b88d 100755 --- a/editor/i18n/en/localization.js +++ b/editor/i18n/en/localization.js @@ -8,7 +8,12 @@ const version = pkg.version.replace(/(^\d+\.\d+)\..*$/, (str, a) => { const url = 'https://docs.cocos.com/creator'; module.exports = link(mixin({ - + common: { + 'attribute': { + 'title': 'Attribute: ', + 'description': 'Description: ', + }, + }, classes: { 'cc': { 'animation': { @@ -315,14 +320,6 @@ module.exports = link(mixin({ csmTransitionRange: 'CSM layers transition range(in NDC space: value range is 0 to 1)', }, sprite: { - gray_scale: 'Whether turn on grayscale rendering mode', - sprite_frame: 'Sprite Frame image to use', - atlas: 'Atlas that the image belongs to', - type: - 'Rendering mode:
- Simple: Modifying the size will stretch the image as a whole, which is suitable for sequence frame animation and normal images.
' + - '- Sliced: When changing the size, the four corners will not stretch, which is suitable for UI buttons and panel backgrounds.
' + - '- Tiled : When changing the size, the original size image will continue to be tiled.
' + - '- Filled : set a certain starting position and direction of filling, and the picture can be cropped and displayed at a certain ratio.', original_size: "Use the Image's original size as the Node size?", edit_button: 'Edit', select_button: 'Select In Atlas', @@ -334,10 +331,6 @@ module.exports = link(mixin({ fill_range: 'The normalizad value indicates how much of the sprite we want to show', src_blend_factor: 'The source image blend mode', dst_blend_factor: 'The destination image blend mode', - size_mode: - 'Set the size of the node on which the Sprite component is on.
CUSTOM for setting width and height manually;
TRIMMED to use image size with transparent pixels trimmed;
RAW to use image size without trimming.', - trim: - "Whether to render transparent pixels around image in node's bounding box.
If you check this option the bounding box will not include transparent pixels around the image.", }, UIOpacity: { opacity: 'The value between 0 to 255 showing the transparency of the object', @@ -664,22 +657,6 @@ module.exports = link(mixin({ url: 'A given URL to be loaded by the Webview,
it should have a http or https prefix.', webviewEvents: "The Webview's event callback ,
it will be triggered when certain Webview event occurs.", }, - richtext: { - string: 'Text of the RichText, you could use BBcode in the string', - horizontal_align: 'Horizontal alignment', - vertical_align: 'Vertical alignment', - font_size: 'Font size, in points', - font: 'Custom TTF font of Rich Text', - font_family: 'Custom System font of Rich Text', - use_system_font: 'Using system font', - cache_mode: 'The cache mode of label. This mode only supports system fonts.', - max_width: 'The maximize width of RichText, pass 0 means not limit the maximize width.', - line_height: 'Line height, in points', - image_atlas: - 'The image atlas for the img tag. For each src value in the img tag,
there should be a valid sprite frame in the image atlas.', - handleTouchEvent: - 'Once checked, the Rich Text will block all input events (mouse and touch) within the bounding box of the node,
preventing the input from penetrating into the underlying node.', - }, UICoordinateTracker: { target: 'Target node', camera: 'The 3D camera representing the original coordinate system.', diff --git a/editor/i18n/en/modules/ui.js b/editor/i18n/en/modules/ui.js index 3a14a00f68b..c4724350a81 100644 --- a/editor/i18n/en/modules/ui.js +++ b/editor/i18n/en/modules/ui.js @@ -25,16 +25,16 @@ module.exports = { 'horizontalAlign': { displayName: 'Horizontal Alignment', tooltip: 'Horizontal alignment mode.', - tooltip_left: 'Align Left', - tooltip_right: 'Align Right', - tooltip_center: 'Align Center', + tooltip_left: 'Align Left.', + tooltip_right: 'Align Right.', + tooltip_center: 'Align Center.', }, 'verticalAlign': { displayName: 'Vertical Alignment', tooltip: 'Vertical alignment mode.', - tooltip_top: 'Align Top', - tooltip_bottom: 'Align Bottom', - tooltip_center: 'Align Center', + tooltip_top: 'Align Top.', + tooltip_bottom: 'Align Bottom.', + tooltip_center: 'Align Center.', }, 'fontSize': { displayName: 'Font Size', @@ -61,7 +61,8 @@ module.exports = { }, 'useSystemFont': { displayName: 'System Fonts', - tooltip: 'Whether to use system default fonts. The referenced font asset would be dereferenced once this option was checked.', + tooltip: 'Whether to use system default fonts. ' + + '
The referenced font asset would be dereferenced once this option was checked.', }, 'fontFamily': { displayName: 'Font Family', @@ -76,7 +77,7 @@ module.exports = { tooltip: 'Text cache modes:
' + '1. NONE: No cache,draw once.
' + '2. BITMAP: Text is added as a static image to the dynamic atlas for batch merging, but its content cannot be dynamically modified frequently.
' + - '3. CHAR: Split the text into characters and cache the character texture into a character atlas for reuse, ' + + '3. CHAR: Split the text into characters and cache the character texture into a character atlas for reuse,
' + 'which is suitable for text content with repeated character content and frequently updated.', }, 'isBold': { @@ -125,6 +126,115 @@ module.exports = { }, }, }, + 'RichText': { + properties: { + 'string': { + displayName: 'string', + tooltip: 'Text of the RichText, you could use BBcode in the string.', + }, + 'horizontalAlign': { + displayName: 'Horizontal Alignment', + tooltip: 'Horizontal alignment mode.', + tooltip_left: 'Align Left.', + tooltip_right: 'Align Right.', + tooltip_center: 'Align Center.', + }, + 'verticalAlign': { + displayName: 'Vertical Alignment', + tooltip: 'Vertical alignment mode.', + tooltip_top: 'Align Top.', + tooltip_bottom: 'Align Bottom.', + tooltip_center: 'Align Center.', + }, + 'fontSize': { + displayName: 'Font Size', + tooltip: 'Font size, in points.', + }, + 'fontColor': { + displayName: 'Color', + tooltip: 'Default text color for rich text. ' + + '
It takes effect when the text content does not have a color parameter set. ' + + '
Color cascading is not supported yet.', + }, + 'fontFamily': { + displayName: 'Font Family', + tooltip: 'Font names.', + }, + 'font': { + displayName: 'Font', + tooltip: 'Custom TTF font of Rich Text.', + }, + 'useSystemFont': { + displayName: 'System Fonts', + tooltip: 'Whether to use system default fonts. ' + + '
The referenced font asset would be dereferenced once this option was checked.', + }, + 'cacheMode': { + displayName: 'Cache Mode', + tooltip: 'Text cache modes:
' + + '1. NONE: No cache,draw once.
' + + '2. BITMAP: Text is added as a static image to the dynamic atlas for batch merging, but its content cannot be dynamically modified frequently.
' + + '3. CHAR: Split the text into characters and cache the character texture into a character atlas for reuse,
' + + 'which is suitable for text content with repeated character content and frequently updated.', + }, + 'maxWidth': { + displayName: 'Max Width', + tooltip: 'The maximize width of RichText, pass 0 means not limit the maximize width.', + }, + 'lineHeight': { + displayName: 'Line Height', + tooltip: 'Line height, in points.', + }, + 'imageAtlas': { + displayName: 'Image Atlas', + tooltip: 'The image atlas for the img tag. ' + + '
For each src value in the img tag, ' + + '
there should be a valid sprite frame in the image atlas.', + }, + 'handleTouchEvent': { + displayName: 'Block input events', + tooltip: 'Once checked, the Rich Text will block all input events (mouse and touch) within the bounding box of the node, ' + + '
preventing the input from penetrating into the underlying node.', + }, + }, + }, + 'Sprite': { + properties: { + __extends__: 'classes.cc.UIRenderer.properties', + 'grayscale': { + displayName: 'Grayscale', + tooltip: 'Whether turn on grayscale rendering mode.', + }, + 'spriteAtlas': { + displayName: 'Sprite Atlas', + tooltip: 'Atlas that the image belongs to.', + }, + 'sprite_frame': { + displayName: 'Sprite Frame', + tooltip: 'Sprite Frame image to use.', + }, + 'type': { + displayName: 'Type', + tooltip: 'Rendering mode:' + + '
- Simple: Modifying the size will stretch the image as a whole, which is suitable for sequence frame animation and normal images.
' + + '- Sliced: When changing the size, the four corners will not stretch, which is suitable for UI buttons and panel backgrounds.
' + + '- Tiled : When changing the size, the original size image will continue to be tiled.
' + + '- Filled : set a certain starting position and direction of filling, and the picture can be cropped and displayed at a certain ratio.', + }, + 'size_mode': { + displayName: 'Size Mode', + tooltip: 'Set the size of the node on which the Sprite component is on. ' + + '
CUSTOM for setting width and height manually;' + + '
TRIMMED to use image size with transparent pixels trimmed; ' + + '
RAW to use image size without trimming.', + }, + 'trim': { + displayName: 'Trim', + tooltip: "Whether to render transparent pixels around image in node's bounding box. " + + "
If you check this option the bounding box will not include transparent pixels around the image.", + }, + }, + }, }, }, }; diff --git a/editor/i18n/zh/assets.js b/editor/i18n/zh/assets.js index 6f4298b47a3..ee26f01507d 100644 --- a/editor/i18n/zh/assets.js +++ b/editor/i18n/zh/assets.js @@ -152,6 +152,7 @@ module.exports = { isRGBE: '作为 RGBE 格式', isRGBETip: '作为 RGBE 格式', flipGreenChannel: '翻转绿色通道', + flipGreenChannelTip: '是否翻转绿色通道', }, spriteFrame: { packable: 'Packable', diff --git a/editor/i18n/zh/localization.js b/editor/i18n/zh/localization.js index c1e84f49f52..6b0adfc164e 100755 --- a/editor/i18n/zh/localization.js +++ b/editor/i18n/zh/localization.js @@ -8,7 +8,12 @@ const version = pkg.version.replace(/(^\d+\.\d+)\..*$/, (str, a) => { const url = 'https://docs.cocos.com/creator'; module.exports = link(mixin({ - + common: { + 'attribute': { + 'title': '属性:', + 'description': '描述:', + }, + }, classes: { 'cc': { 'animation': { @@ -305,14 +310,6 @@ module.exports = link(mixin({ csmTransitionRange: '级联阴影层级过渡范围(NDC空间: 取值范围为 0 ~ 1)', }, sprite: { - gray_scale: '是否开启灰度渲染模式', - atlas: '图片资源所属的 Atlas 图集资源', - sprite_frame: '渲染 Sprite 使用的 Sprite Frame 图片资源', - type: - '渲染模式:
- 普通(Simple):修改尺寸会整体拉伸图像,适用于序列帧动画和普通图像
' + - '- 九宫格 Sliced 修改尺寸时四个角的区域不会拉伸,适用于 UI 按钮和面板背景
' + - '- 平铺 Tiled 修改尺寸时会不断平铺原始大小的图片
' + - '- 填充 Filled 设置一定的填充起始位置和方向,能够以一定比率剪裁显示图片', original_size: '是否使用图片资源的原始尺寸作为 Sprite 节点的 size', edit_button: '编辑', select_button: '选择', @@ -324,9 +321,6 @@ module.exports = link(mixin({ fill_range: '填充总量,取值范围 0 ~ 1 指定显示图像范围的百分比', src_blend_factor: '混合显示两张图片时,源图片的取值模式', dst_blend_factor: '混合显示两张图片时,目标图片的取值模式', - size_mode: - '指定 Sprite 所在节点的尺寸
CUSTOM 表示自定义尺寸
TRIMMED 表示取原始图片剪裁透明像素后的尺寸
RAW 表示取原始图片未剪裁的尺寸', - trim: '节点约束框内是否包括透明像素区域,勾选此项会去除节点约束框内的透明区域', }, UIOpacity: { opacity: '设置物体的不透明度,取值范围为 0 ~ 255', @@ -648,20 +642,6 @@ module.exports = link(mixin({ url: '指定一个 URL 地址,这个地址以 http 或者 https 开头,请填写一个有效的 URL 地址。', webviewEvents: 'Webview 的回调事件,当网页加载过程中,加载完成后或者加载出错时都会回调此函数', }, - richtext: { - string: '富文本的内容字符串, 你可以在里面使用 BBCode 来指定特定文本的样式', - horizontal_align: '水平对齐方式', - vertical_align: '竖直对齐方式', - font_size: '字体大小, 单位是 point', - font: '富文本定制字体', - font_family: '富文本定制系统字体', - use_system_font: '是否使用系统字体', - cache_mode: '文本缓存模式, 该模式只支持系统字体', - max_width: '富文本的最大宽度, 传 0 的话意味着必须手动换行.', - line_height: '字体行高, 单位是 point', - image_atlas: '对于 img 标签里面的 src 属性名称,都需要在 image atlas 里面找到一个有效的 sprite frame,否则 img tag 会判定为无效', - handleTouchEvent: '选中此选项后,rich text 将阻止节点边界框中的所有输入事件(鼠标和触摸),从而防止输入事件穿透到底层节点', - }, UICoordinateTracker: { target: '目标对象', camera: '照射相机', diff --git a/editor/i18n/zh/modules/ui.js b/editor/i18n/zh/modules/ui.js index ca1306685c0..4e7afd6879f 100644 --- a/editor/i18n/zh/modules/ui.js +++ b/editor/i18n/zh/modules/ui.js @@ -25,16 +25,16 @@ module.exports = { 'horizontalAlign': { displayName: '水平对齐', tooltip: '文字水平对齐模式。', - tooltip_left: '左对齐', - tooltip_right: '右对齐', - tooltip_center: '居中对齐', + tooltip_left: '左对齐。', + tooltip_right: '右对齐。', + tooltip_center: '居中对齐。', }, 'verticalAlign': { displayName: '竖直对齐', tooltip: '文字竖直对齐模式。', - tooltip_top: '上对齐', - tooltip_bottom: '下对齐', - tooltip_center: '居中对齐', + tooltip_top: '上对齐。', + tooltip_bottom: '下对齐。', + tooltip_center: '居中对齐。', }, 'fontSize': { displayName: '字体大小', @@ -46,14 +46,14 @@ module.exports = { }, 'spacingX': { displayName: '水平间距', - tooltip: '文本字符之间的间距。仅在使用位图字体时生效', + tooltip: '文本字符之间的间距。仅在使用位图字体时生效。', }, 'overflow': { displayName: '溢出处理', tooltip: '文字排版模式,包括以下三种:
' + - '1. CLAMP: 节点约束框之外的文字会被截断
' + - '2. SHRINK: 自动根据节点约束框缩小文字
' + - '3. RESIZE: 根据文本内容自动更新节点的 height 属性.', + '1. CLAMP: 节点约束框之外的文字会被截断。
' + + '2. SHRINK: 自动根据节点约束框缩小文字。
' + + '3. RESIZE: 根据文本内容自动更新节点的 height 属性。', }, 'enableWrapText': { displayName: '自动换行', @@ -74,9 +74,9 @@ module.exports = { 'cacheMode': { displayName: '缓存模式', tooltip: '文本缓存模式,包括以下三种:
' + - '1. NONE: 不做任何缓存,文本内容进行一次绘制
' + - '2. BITMAP: 将文本作为静态图像加入动态图集进行批次合并,但是不能频繁动态修改文本内容
' + - '3. CHAR: 将文本拆分为字符并且把字符纹理缓存到一张字符图集中进行复用,适用于字符内容重复并且频繁更新的文本内容', + '1. NONE: 不做任何缓存,文本内容进行一次绘制。
' + + '2. BITMAP: 将文本作为静态图像加入动态图集进行批次合并,但是不能频繁动态修改文本内容。
' + + '3. CHAR: 将文本拆分为字符并且把字符纹理缓存到一张字符图集中进行复用,适用于字符内容重复并且频繁更新的文本内容。', }, 'isBold': { displayName: '粗体', @@ -124,6 +124,105 @@ module.exports = { }, }, }, + 'RichText': { + properties: { + 'string': { + tooltip: '显示的富文本内容字符串。', + }, + 'horizontalAlign': { + displayName: '水平对齐', + tooltip: '文字水平对齐模式。', + tooltip_left: '左对齐。', + tooltip_right: '右对齐。', + tooltip_center: '居中对齐。', + }, + 'verticalAlign': { + displayName: '竖直对齐', + tooltip: '文字竖直对齐模式。', + tooltip_top: '上对齐。', + tooltip_bottom: '下对齐。', + tooltip_center: '居中对齐。', + }, + 'fontSize': { + displayName: '字体大小', + tooltip: '文字尺寸,以点为单位。', + }, + 'fontColor': { + displayName: '颜色', + tooltip: '富文本默认文字颜色。在文本内容没有设置颜色参数时生效。暂不支持颜色级联。', + }, + 'fontFamily': { + displayName: '字体族', + tooltip: '文字字体名字。', + }, + 'font': { + displayName: '字体', + tooltip: '使用的字体资源。', + }, + 'useSystemFont': { + displayName: '系统字体', + tooltip: '是否使用系统默认字体,选中此项会将引用的字体资产置空。', + }, + 'cacheMode': { + displayName: '缓存模式', + tooltip: '文本缓存模式,包括以下三种:
' + + '1. NONE: 不做任何缓存,文本内容进行一次绘制。
' + + '2. BITMAP: 将文本作为静态图像加入动态图集进行批次合并,但是不能频繁动态修改文本内容。
' + + '3. CHAR: 将文本拆分为字符并且把字符纹理缓存到一张字符图集中进行复用,适用于字符内容重复并且频繁更新的文本内容。', + }, + 'maxWidth': { + displayName: '缓存模式', + tooltip: '富文本的最大宽度, 传 0 的话意味着必须手动换行。', + }, + 'lineHeight': { + displayName: '行高', + tooltip: '文字行高,以点为单位。', + }, + 'imageAtlas': { + displayName: '图集', + tooltip: '对于 img 标签里面的 src 属性名称,' + + '
都需要在 image atlas 里面找到一个有效的 sprite frame,' + + '
否则 img tag 会判定为无效。', + }, + 'handleTouchEvent': { + displayName: '阻止输入事件', + tooltip: '选中此选项后,rich text 将阻止节点边界框中的所有输入事件(鼠标和触摸),' + + '
从而防止输入事件穿透到底层节点。', + }, + }, + }, + 'Sprite': { + properties: { + __extends__: 'classes.cc.UIRenderer.properties', + 'grayscale': { + displayName: 'Grayscale', + tooltip: '是否开启灰度渲染模式', + }, + 'spriteAtlas': { + displayName: 'Sprite Atlas', + tooltip: '图片资源所属的 Atlas 图集资源', + }, + 'sprite_frame': { + displayName: 'Sprite Frame', + tooltip: '渲染 Sprite 使用的 Sprite Frame 图片资源', + }, + 'type': { + displayName: 'Type', + tooltip: '渲染模式:
- 普通(Simple):修改尺寸会整体拉伸图像,适用于序列帧动画和普通图像
' + + '- 九宫格 Sliced 修改尺寸时四个角的区域不会拉伸,适用于 UI 按钮和面板背景
' + + '- 平铺 Tiled 修改尺寸时会不断平铺原始大小的图片
' + + '- 填充 Filled 设置一定的填充起始位置和方向,能够以一定比率剪裁显示图片', + }, + 'size_mode': { + displayName: 'Size Mode', + tooltip: '指定 Sprite 所在节点的尺寸
CUSTOM 表示自定义尺寸
TRIMMED 表示取原始图片剪裁透明像素后的尺寸
RAW 表示取原始图片未剪裁的尺寸', + }, + 'trim': { + displayName: 'Trim', + tooltip: '节点约束框内是否包括透明像素区域,勾选此项会去除节点约束框内的透明区域', + }, + }, + }, }, }, }; diff --git a/editor/inspector/components/label.js b/editor/inspector/components/label.js index 1a5683e1eee..efb6f44f579 100644 --- a/editor/inspector/components/label.js +++ b/editor/inspector/components/label.js @@ -1,4 +1,4 @@ -const { getName, setHidden, isMultipleInvalid } = require('../utils/prop'); +const { setTooltip, setHidden, isMultipleInvalid, createRadioGroup, setLabel } = require('../utils/prop'); const { template, $, update, close } = require('./base'); const fontStyles = ['isBold', 'isItalic', 'isUnderline']; exports.template = template; @@ -50,42 +50,6 @@ ui-tab { } `; -/** - * - * @param {object} options - * @param {any[]} options.enumList - * @param {string} options.tooltip - * @param {(elementName: string) => string}options.getIconName - * @param {(event: CustomEvent) => {}} options.onChange - * @returns - */ -function createRadioGroup(options) { - const { enumList, getIconName, onChange, tooltip: rawTooltip } = options; - const $radioGroup = document.createElement('ui-radio-group'); - $radioGroup.setAttribute('slot', 'content'); - $radioGroup.addEventListener('change', (e) => { - onChange(e); - }); - - for (let index = 0; index < enumList.length; index++) { - const element = enumList[index]; - const icon = document.createElement('ui-icon'); - const button = document.createElement('ui-radio-button'); - - const iconName = getIconName(element.name); - const tooltip = `${rawTooltip}_${element.name.toLocaleLowerCase()}`; - - icon.value = iconName; - button.appendChild(icon); - button.value = element.value; - button.setAttribute('tooltip', tooltip); - - $radioGroup.appendChild(button); - } - - return $radioGroup; -} - exports.ready = function() { this.elements = { horizontalAlign: { @@ -94,8 +58,7 @@ exports.ready = function() { prop.dump = dump; const label = document.createElement('ui-label'); label.setAttribute('slot', 'label'); - label.value = getName(dump); - label.setAttribute('tooltip', dump.tooltip); + setLabel(dump, label); const content = createRadioGroup({ enumList: dump.enumList, @@ -139,8 +102,7 @@ exports.ready = function() { prop.dump = dump; const label = document.createElement('ui-label'); label.setAttribute('slot', 'label'); - label.value = getName(dump); - label.setAttribute('tooltip', dump.tooltip); + setLabel(dump, label); const content = createRadioGroup({ enumList: dump.enumList, @@ -197,7 +159,7 @@ exports.ready = function() { const label = document.createElement('ui-label'); label.innerHTML = styleDisplayNames[index]; label.setAttribute('key', style); - label.setAttribute('tooltip', this.dump.value[style].tooltip); + setTooltip(this.dump.value[style], label); label.classList.add('fontStyle', styleClassNames[index]); label.addEventListener('click', () => { prop.dump = this.dump.value[style]; @@ -242,6 +204,5 @@ exports.ready = function() { setHidden(true, element); }, }, - }; }; diff --git a/editor/inspector/components/rich-text.js b/editor/inspector/components/rich-text.js index 775fa94265d..66fbc9ca027 100644 --- a/editor/inspector/components/rich-text.js +++ b/editor/inspector/components/rich-text.js @@ -5,7 +5,54 @@ exports.$ = $; exports.update = update; exports.close = close; -const { setHidden, isMultipleInvalid } = require('../utils/prop'); +const { setHidden, isMultipleInvalid, getName, createRadioGroup, setLabel } = require('../utils/prop'); + +const fontStyles = ['isBold', 'isItalic', 'isUnderline']; + +exports.style = ` +ui-tab { + flex: none; +} + +.fontStyleParent { + display:flex +} + +.fontStyle:nth-child(2) { + margin-left: 5px; + margin-right: 5px; +} + +.fontStyle { + height: 20px; + width: 42px; + text-align: center; + line-height: 20px; + border: calc(var(--size-normal-border) * 1px) solid var(--color-default-border-weaker); + border-radius: calc(var(--size-normal-radius) * 1px); +} + +.fontStyle.invalid { + background-color: var(--color-default-fill); +} + +.fontStyle.select { + background-color: var(--color-info-fill-important); + border-color: var(--color-focus-border-emphasis); +} + +.fontStyle.italic { + font-style: italic; +} + +.fontStyle.bold { + font-weight: bold; +} + +.fontStyle.underline { + text-decoration-line: underline; +} +`; exports.ready = function() { this.elements = { @@ -14,14 +61,98 @@ exports.ready = function() { setHidden(isMultipleInvalid(dump.useSystemFont) || !dump.useSystemFont.value, element); }, }, - cacheMode: { + font: { update(element, dump) { - this.elements.fontFamily.update.call(this, element, dump); + setHidden(isMultipleInvalid(dump.useSystemFont) || !!dump.useSystemFont.value, element); }, }, - font: { + horizontalAlign: { + create(dump) { + const prop = document.createElement('ui-prop'); + prop.dump = dump; + const label = document.createElement('ui-label'); + label.setAttribute('slot', 'label'); + setLabel(dump, label); + + const content = createRadioGroup({ + enumList: dump.enumList, + tooltip: dump.tooltip, + getIconName: (elName) => { + const iconName = elName.toLocaleLowerCase(); + if (iconName === 'center') { + return `align-h-${iconName}`; + } + return `align-${iconName}`; + }, + onChange: (event) => { + const value = Number(event.target.value); + if (Number.isFinite(value) && value !== -1) { + dump.value = value; + if (dump.values) { + dump.values.forEach((_, index) => dump.values[index] = dump.value); + } + prop.dispatch('change-dump'); + prop.dispatch('confirm-dump'); + } + }, + }); + + prop.appendChild(label); + prop.appendChild(content); + return prop; + }, update(element, dump) { - setHidden(isMultipleInvalid(dump.useSystemFont) || !!dump.useSystemFont.value, element); + const radioGroup = element.querySelector('ui-radio-group'); + if (isMultipleInvalid(dump.horizontalAlign)) { + radioGroup.value = -1; + } else { + radioGroup.value = dump.horizontalAlign.value; + } + }, + }, + verticalAlign: { + create(dump) { + const prop = document.createElement('ui-prop'); + prop.dump = dump; + const label = document.createElement('ui-label'); + label.setAttribute('slot', 'label'); + setLabel(dump, label); + + const content = createRadioGroup({ + enumList: dump.enumList, + tooltip: dump.tooltip, + getIconName: (elementName) => { + const iconName = elementName.toLocaleLowerCase(); + if (iconName === 'center') { + return `align-v-${iconName}`; + } + return `align-${iconName}`; + }, + onChange: (e) => { + const enumVal = Number(e.target.value); + if (!Number.isFinite(enumVal) || enumVal === -1) { + return; + } + dump.value = enumVal; + if (dump.values) { + dump.values.forEach((_, index) => (dump.values[index] = dump.value)); + } + prop.dispatch('change-dump'); + prop.dispatch('confirm-dump'); + }, + }); + + prop.appendChild(label); + prop.appendChild(content); + return prop; + }, + update(element, dump) { + const radioGroup = element.querySelector('ui-radio-group'); + if (isMultipleInvalid(dump.verticalAlign)) { + radioGroup.value = -1; + } else { + radioGroup.value = dump.verticalAlign.value; + } }, }, }; diff --git a/editor/inspector/contributions/node.css b/editor/inspector/contributions/node.css index dafbd66fd7e..00f70fc3a46 100644 --- a/editor/inspector/contributions/node.css +++ b/editor/inspector/contributions/node.css @@ -120,11 +120,7 @@ margin-right: 4px; } -.container > .body .component .component-header ui-drag-item { - flex: 1; -} - -.container > .body .component .component-header .name { +.container > .body .component .component-header ui-drag-item, .container > .body .component .component-header .name { flex: 1; overflow: hidden; text-overflow: ellipsis; diff --git a/editor/inspector/contributions/node.js b/editor/inspector/contributions/node.js index ada2f489c1d..d22430299a8 100644 --- a/editor/inspector/contributions/node.js +++ b/editor/inspector/contributions/node.js @@ -6,7 +6,7 @@ const Profile = require('@base/electron-profile'); const { throttle } = require('lodash'); const utils = require('./utils'); const { trackEventWithTimer } = require('../utils/metrics'); -const { injectionStyle } = require('../utils/prop'); +const { injectionStyle, setLabel } = require('../utils/prop'); // ipc messages protocol const messageProtocol = { @@ -473,6 +473,7 @@ exports.$ = { nodeRotation: '.container > .body > .node > .rotation', nodeScale: '.container > .body > .node > .scale', nodeMobility: '.container > .body > .node > .mobility', + nodeLayer: '.container > .body > .node > .layer > ui-label', nodeLayerSelect: '.container > .body > .node > .layer .layer-select', nodeLayerButton: '.container > .body > .node > .layer .layer-edit', @@ -594,7 +595,7 @@ const Elements = { }, set(val) { panel._readyToUpdate = val; - if (val) { + if (val && panel.throttleUpdate) { panel.throttleUpdate(); } }, @@ -1178,6 +1179,7 @@ const Elements = { panel.$.nodeRotation.render(panel.dump.rotation); panel.$.nodeScale.render(panel.dump.scale); panel.$.nodeMobility.render(panel.dump.mobility); + setLabel(panel.dump.layer, panel.$.nodeLayer); // 查找需要渲染的 component 列表 const componentList = []; diff --git a/editor/inspector/utils/prop.js b/editor/inspector/utils/prop.js index ec76e9f7d52..65cfa580b52 100644 --- a/editor/inspector/utils/prop.js +++ b/editor/inspector/utils/prop.js @@ -351,14 +351,15 @@ exports.getName = function(dump) { return ''; } - if (dump.displayName) { - if (dump.displayName.startsWith(i18nPrefix)) { - const key = dump.displayName.substring(i18nPrefix.length); + if (typeof dump.displayName === 'string') { + const displayName = dump.displayName.trim(); + if (displayName.startsWith(i18nPrefix)) { + const key = displayName.substring(i18nPrefix.length); if (Editor.I18n.t(key)) { - return dump.displayName; + return displayName; } - } else { - return dump.displayName; + } else if (displayName) { + return displayName; } } @@ -557,6 +558,42 @@ exports.disconnectGroup = function(panel) { } }; +/** + * Create ui-radio-group according to configuration + * @param {object} options + * @param {any[]} options.enumList + * @param {string} options.tooltip + * @param {(elementName: string) => string}options.getIconName + * @param {(event: CustomEvent) => {}} options.onChange + * @returns + */ +exports.createRadioGroup = function(options) { + const { enumList, getIconName, onChange, tooltip: rawTooltip } = options; + const $radioGroup = document.createElement('ui-radio-group'); + $radioGroup.setAttribute('slot', 'content'); + $radioGroup.addEventListener('change', (e) => { + onChange(e); + }); + + for (let index = 0; index < enumList.length; index++) { + const element = enumList[index]; + const icon = document.createElement('ui-icon'); + const button = document.createElement('ui-radio-button'); + + const iconName = getIconName(element.name); + const tooltip = `${rawTooltip}_${element.name.toLocaleLowerCase()}`; + + icon.value = iconName; + button.appendChild(icon); + button.value = element.value; + button.setAttribute('tooltip', tooltip); + + $radioGroup.appendChild(button); + } + + return $radioGroup; +}; + exports.injectionStyle = ` ui-prop, ui-section { margin-top: 4px; } @@ -564,8 +601,8 @@ ui-section { margin-top: 4px; } ui-prop > ui-section, ui-prop > ui-prop, ui-section > ui-prop[slot="header"], -ui-prop [slot="content"] ui-prop { - margin-top: 0; +ui-prop [slot="content"] ui-prop { + margin-top: 0; margin-left: 0; } ui-prop[ui-section-config] + ui-section.config, @@ -577,3 +614,51 @@ ui-prop[ui-section-config]:last-child { border-bottom: solid 1px var(--color-normal-fill-emphasis); } `; + +/** + * Obtain the api document path and obtain the className through the cc.xxx.properties configured by i18n. + * If it does not exist, the ui-link component will not be added. + * @param dump + */ +function getDocsURL(dump) { + const mathResults = dump.displayName && dump.displayName.match(/(?<=cc\.\s*)(\w+)/); + const className = mathResults && mathResults.length > 0 ? mathResults[0] : ''; + if (!className) { + return ''; + } + return `${Editor.App.urls.api}/class/${className}?id=${dump.name}`; +} + +exports.setTooltip = function(dump, $label, name) { + if (!name) { + name = exports.getName(dump); + } + if (dump.tooltip) { + let tooltipValid = true; + if (dump.tooltip.startsWith(i18nPrefix)) { + const key = dump.tooltip.substring(i18nPrefix.length); + if (!Editor.I18n.t(key)) { + tooltipValid = false; + } + } + + if (tooltipValid) { + const url = getDocsURL(dump); + const attributeTitle = ``; + const attributeName = url ? ` + + + `.trim() : ``; + + $label.setAttribute('tooltip', ` +
${attributeTitle}${attributeName}
`.trim() + ); + } + } +}; + +exports.setLabel = function(dump, $label) { + const name = exports.getName(dump); + $label.value = name; + exports.setTooltip(dump, $label, name); +}; diff --git a/pal/screen-adapter/web/screen-adapter.ts b/pal/screen-adapter/web/screen-adapter.ts index f1e83723aab..d85634a315c 100644 --- a/pal/screen-adapter/web/screen-adapter.ts +++ b/pal/screen-adapter/web/screen-adapter.ts @@ -386,7 +386,7 @@ class ScreenAdapter extends EventTarget { // if (!this.handleResizeEvent) { // return; // } - this._resizeFrame(); + this._updateFrame(); }); const notifyOrientationChange = (orientation): void => { diff --git a/templates/fb-instant-games/index.ejs b/templates/fb-instant-games/index.ejs index 91fa5eceb1d..3e278ac692e 100644 --- a/templates/fb-instant-games/index.ejs +++ b/templates/fb-instant-games/index.ejs @@ -11,6 +11,7 @@ + diff --git a/templates/web-desktop/index.ejs b/templates/web-desktop/index.ejs index 3ec8ce452f0..4da4ae84340 100644 --- a/templates/web-desktop/index.ejs +++ b/templates/web-desktop/index.ejs @@ -7,15 +7,16 @@ + - + - + diff --git a/templates/web-mobile/index.ejs b/templates/web-mobile/index.ejs index 91fa5eceb1d..3e278ac692e 100644 --- a/templates/web-mobile/index.ejs +++ b/templates/web-mobile/index.ejs @@ -11,6 +11,7 @@ +