diff --git a/e2e/README.md b/e2e/README.md new file mode 100644 index 0000000000..43f73bef3e --- /dev/null +++ b/e2e/README.md @@ -0,0 +1,43 @@ +### Note: Require install git-lfs +We use [git-lfs](https://git-lfs.com/) (Install by official website) to manage baseline images for e2e tests, so it's necessary to install it, ignore if already installed. +### 1. Create a case page in the e2e/case directory +You can refer to e2e/case/animator-play.ts. +### 2. Configure your e2e test in e2e/config.ts +The threshold is color difference threshold (from 0 to 1). Less more precise. +### 3. Debug your test cases: +#### Launch the Case page: + +``` +npm run e2e:case +``` + +After successfully launching the case page, run: + +``` +git lfs pull +``` +Pull image from github, then run + +``` +npm run e2e:debug +``` + +Open the Cypress client for debugging. +Cypress will capture screenshots of your case pages. +Review the screenshots in e2e/downloads folder, store them in the e2e/fixtures/originImage directory if there are no issues, then rerun the test cases. If the test cases pass, the debugging is complete. + +### 4. Run the complete e2e tests: +``` +npm run e2e +``` +Note: The e2e testing framework for this project is Cypress. For detailed usage instructions, please refer to https://www.cypress.io/. + + +### Add new e2e case + +1. modify `config.ts` based on the new test case. +2. run `npm run e2e:debug` + +the new image of test case for comparison will be present under directory `e2e/downloads`, you need to copy it into directory `e2e/fixtures/originImage`. + + diff --git a/e2e/case/project-loader.ts b/e2e/case/project-loader.ts new file mode 100644 index 0000000000..bd61cb1d02 --- /dev/null +++ b/e2e/case/project-loader.ts @@ -0,0 +1,29 @@ +/** + * @title Project loader + * @category Advance + */ +import { Logger, WebGLEngine, AssetType, Camera } from "@galacean/engine"; +import { ShaderLab } from "@galacean/engine-shader-lab"; +import { registerIncludes } from "@galacean/engine-toolkit"; +import { initScreenshot, updateForE2E } from './.mockForE2E'; + +// Create ShaderLab +const shaderLab = new ShaderLab(); +registerIncludes(); + +Logger.enable(); +WebGLEngine.create({ canvas: "canvas", shaderLab }).then( (engine) => { + engine.canvas.resizeByClientSize(2); + engine.resourceManager + .load({ + type: AssetType.Project, + url: "https://mdn.alipayobjects.com/oasis_be/afts/file/A*o15SSopTBh0AAAAAAAAAAAAADkp5AQ/project.json" + }).then(() => { + updateForE2E(engine); + + const cameraEntity = + engine.sceneManager.activeScene.findEntityByName('Camera'); + const camera = cameraEntity.getComponent(Camera) + initScreenshot(engine, camera) + }) +}); diff --git a/e2e/config.ts b/e2e/config.ts index 4d82268092..75c2789e0f 100644 --- a/e2e/config.ts +++ b/e2e/config.ts @@ -226,5 +226,12 @@ export const E2E_CONFIG = { caseFileName: "text-typed", threshold: 0.4 } - } + }, + Other: { + ProjectLoader: { + category: "Advance", + caseFileName: "project-loader", + threshold: 0.4 + } + }, }; diff --git a/e2e/fixtures/originImage/Advance_project-loader.jpg b/e2e/fixtures/originImage/Advance_project-loader.jpg new file mode 100644 index 0000000000..8f32a54ef7 --- /dev/null +++ b/e2e/fixtures/originImage/Advance_project-loader.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:12af410f9c34b96e3e7e8f0ae56478da524bd8ada15af7c2fcef0fd0bca3e5ab +size 571023 diff --git a/e2e/package.json b/e2e/package.json index 2e2f4f3784..24621262c0 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,7 +1,7 @@ { "name": "@galacean/engine-e2e", "private": true, - "version": "1.3.15", + "version": "1.3.20", "license": "MIT", "scripts": { "case": "vite serve .dev --config .dev/vite.config.js", @@ -9,7 +9,7 @@ }, "files": [], "dependencies": { - "@galacean/engine-toolkit": "^1.0.0-beta.1", + "@galacean/engine-toolkit": "^1.3.9", "@galacean/engine-shader-lab": "workspace:*", "@galacean/engine": "workspace:*", "@galacean/engine-core": "workspace:*", diff --git a/package.json b/package.json index b8079b9d20..60064a2552 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-root", - "version": "1.3.15", + "version": "1.3.20", "packageManager": "pnpm@9.3.0", "private": true, "scripts": { diff --git a/packages/core/package.json b/packages/core/package.json index 6cfee070bc..470a3a9d55 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-core", - "version": "1.3.15", + "version": "1.3.20", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/core/src/Background.ts b/packages/core/src/Background.ts index a94d66fe68..38e71a5276 100644 --- a/packages/core/src/Background.ts +++ b/packages/core/src/Background.ts @@ -1,5 +1,5 @@ import { Color, Vector2, Vector3 } from "@galacean/engine-math"; -import { CompareFunction, Material, ModelMesh, Shader } from "."; +import { CompareFunction, ContentRestorer, Material, ModelMesh, Shader } from "."; import { Engine } from "./Engine"; import { BackgroundMode } from "./enums/BackgroundMode"; import { BackgroundTextureFillMode } from "./enums/BackgroundTextureFillMode"; @@ -134,7 +134,20 @@ export class Background { } private _initMesh(engine: Engine): void { - this._mesh = this._createPlane(engine); + const mesh = (this._mesh = this._createPlane(engine)); + engine.resourceManager.addContentRestorer( + new (class extends ContentRestorer { + constructor() { + super(mesh); + } + restoreContent() { + mesh.setPositions(mesh.getPositions()); + mesh.setUVs(mesh.getUVs()); + mesh.setIndices(mesh.getIndices()); + mesh.uploadData(false); + } + })() + ); this._mesh._addReferCount(1); } diff --git a/packages/core/src/BasicResources.ts b/packages/core/src/BasicResources.ts index 3fcd5c72cd..db755c6426 100644 --- a/packages/core/src/BasicResources.ts +++ b/packages/core/src/BasicResources.ts @@ -171,8 +171,19 @@ export class BasicResources { const mesh = new ModelMesh(engine); mesh._addReferCount(1); mesh.setVertexElements([new VertexElement("POSITION_UV", 0, VertexElementFormat.Vector4, 0)]); - mesh.setVertexBufferBinding(new Buffer(engine, BufferBindFlag.VertexBuffer, vertices, BufferUsage.Static), 16); + const buffer = new Buffer(engine, BufferBindFlag.VertexBuffer, vertices, BufferUsage.Static, true); + mesh.setVertexBufferBinding(buffer, 16); mesh.addSubMesh(0, 3, MeshTopology.Triangles); + engine.resourceManager.addContentRestorer( + new (class extends ContentRestorer { + constructor() { + super(mesh); + } + restoreContent() { + buffer.setData(buffer.data); + } + })() + ); return mesh; } diff --git a/packages/core/src/Utils.ts b/packages/core/src/Utils.ts index 8c2c8b03bf..f312853312 100644 --- a/packages/core/src/Utils.ts +++ b/packages/core/src/Utils.ts @@ -78,6 +78,12 @@ export class Utils { return relativeUrl; } + if (!/^https?:/.test(baseUrl)) { + const fileSchema = "files://"; + baseUrl = fileSchema + baseUrl; + return new URL(relativeUrl, baseUrl).href.substring(fileSchema.length); + } + return relativeUrl ? new URL(relativeUrl, baseUrl).href : baseUrl; } diff --git a/packages/core/src/asset/Loader.ts b/packages/core/src/asset/Loader.ts index efe38d5311..47a046657f 100644 --- a/packages/core/src/asset/Loader.ts +++ b/packages/core/src/asset/Loader.ts @@ -41,5 +41,4 @@ export abstract class Loader { constructor(public readonly useCache: boolean) {} initialize?(engine: Engine, configuration: EngineConfiguration): Promise; abstract load(item: LoadItem, resourceManager: ResourceManager): AssetPromise; - request: (url: string, config: RequestConfig) => AssetPromise = request; } diff --git a/packages/core/src/asset/ResourceManager.ts b/packages/core/src/asset/ResourceManager.ts index d0591e8b69..b76de6f79b 100644 --- a/packages/core/src/asset/ResourceManager.ts +++ b/packages/core/src/asset/ResourceManager.ts @@ -4,6 +4,7 @@ import { GraphicsResource } from "./GraphicsResource"; import { Loader } from "./Loader"; import { LoadItem } from "./LoadItem"; import { ReferResource } from "./ReferResource"; +import { request, RequestConfig } from "./request"; /** * ResourceManager @@ -179,16 +180,40 @@ export class ResourceManager { this._contentRestorerPool[restorer.resource.instanceId] = restorer; } + /** + * @internal + */ + _getRemoteUrl(url: string): string { + return this._virtualPathMap[url] ?? url; + } + + /** + * @internal + */ + _requestByRemoteUrl(url: string, config: RequestConfig): AssetPromise { + return request(url, config); + } + + /** + * @internal + */ + _request(url: string, config: RequestConfig): AssetPromise { + const remoteUrl = this._getRemoteUrl(url); + return this._requestByRemoteUrl(remoteUrl, config); + } + /** * @internal */ _onSubAssetSuccess(assetBaseURL: string, assetSubPath: string, value: T): void { - const subPromiseCallback = this._subAssetPromiseCallbacks[assetBaseURL]?.[assetSubPath]; + const remoteAssetBaseURL = this._virtualPathMap[assetBaseURL] ?? assetBaseURL; + + const subPromiseCallback = this._subAssetPromiseCallbacks[remoteAssetBaseURL]?.[assetSubPath]; if (subPromiseCallback) { subPromiseCallback.resolve(value); } else { // Pending - (this._subAssetPromiseCallbacks[assetBaseURL] ||= {})[assetSubPath] = { + (this._subAssetPromiseCallbacks[remoteAssetBaseURL] ||= {})[assetSubPath] = { resolvedValue: value }; } @@ -326,10 +351,7 @@ export class ResourceManager { private _loadSingleItem(itemOrURL: LoadItem | string): AssetPromise { const item = this._assignDefaultOptions(typeof itemOrURL === "string" ? { url: itemOrURL } : itemOrURL); - - // Check url mapping - const itemURL = item.url; - let url = this._virtualPathMap[itemURL] ?? itemURL; + let { url } = item; // Not absolute and base url is set if (!Utils.isAbsoluteUrl(url) && this.baseUrl) url = Utils.resolveAbsoluteUrl(this.baseUrl, url); @@ -338,8 +360,11 @@ export class ResourceManager { const { assetBaseURL, queryPath } = this._parseURL(url); const paths = queryPath ? this._parseQueryPath(queryPath) : []; + // Get remote asset base url + const remoteAssetBaseURL = this._virtualPathMap[assetBaseURL] ?? assetBaseURL; + // Check cache - const cacheObject = this._assetUrlPool[assetBaseURL]; + const cacheObject = this._assetUrlPool[remoteAssetBaseURL]; if (cacheObject) { return new AssetPromise((resolve) => { resolve(this._getResolveResource(cacheObject, paths) as T); @@ -347,18 +372,18 @@ export class ResourceManager { } // Get asset url - let assetURL = assetBaseURL; + let remoteAssetURL = remoteAssetBaseURL; if (queryPath) { - assetURL += "?q=" + paths.shift(); + remoteAssetURL += "?q=" + paths.shift(); let index: string; while ((index = paths.shift())) { - assetURL += `[${index}]`; + remoteAssetURL += `[${index}]`; } } // Check is loading const loadingPromises = this._loadingPromises; - const loadingPromise = loadingPromises[assetURL]; + const loadingPromise = loadingPromises[remoteAssetURL]; if (loadingPromise) { return new AssetPromise((resolve, reject, setTaskCompleteProgress, setTaskDetailProgress) => { loadingPromise @@ -381,34 +406,40 @@ export class ResourceManager { // Check sub asset if (queryPath) { // Check whether load main asset - const mainPromise = loadingPromises[assetBaseURL] || this._loadMainAsset(loader, item, assetBaseURL); + const mainPromise = + loadingPromises[remoteAssetBaseURL] || this._loadMainAsset(loader, item, remoteAssetBaseURL, assetBaseURL); mainPromise.catch((e) => { - this._onSubAssetFail(assetBaseURL, queryPath, e); + this._onSubAssetFail(remoteAssetBaseURL, queryPath, e); }); - return this._createSubAssetPromiseCallback(assetBaseURL, assetURL, queryPath); + return this._createSubAssetPromiseCallback(remoteAssetBaseURL, remoteAssetURL, queryPath); } - return this._loadMainAsset(loader, item, assetBaseURL); + return this._loadMainAsset(loader, item, remoteAssetBaseURL, assetBaseURL); } - private _loadMainAsset(loader: Loader, item: LoadItem, assetBaseURL: string): AssetPromise { + private _loadMainAsset( + loader: Loader, + item: LoadItem, + remoteAssetBaseURL: string, + assetBaseURL: string + ): AssetPromise { item.url = assetBaseURL; const loadingPromises = this._loadingPromises; const promise = loader.load(item, this); - loadingPromises[assetBaseURL] = promise; + loadingPromises[remoteAssetBaseURL] = promise; promise.then( (resource: T) => { if (loader.useCache) { - this._addAsset(assetBaseURL, resource as EngineObject); + this._addAsset(remoteAssetBaseURL, resource as EngineObject); } - delete loadingPromises[assetBaseURL]; - this._releaseSubAssetPromiseCallback(assetBaseURL); + delete loadingPromises[remoteAssetBaseURL]; + this._releaseSubAssetPromiseCallback(remoteAssetBaseURL); }, () => { - delete loadingPromises[assetBaseURL]; - this._releaseSubAssetPromiseCallback(assetBaseURL); + delete loadingPromises[remoteAssetBaseURL]; + this._releaseSubAssetPromiseCallback(remoteAssetBaseURL); } ); @@ -416,12 +447,12 @@ export class ResourceManager { } private _createSubAssetPromiseCallback( - assetBaseURL: string, - assetURL: string, + remoteAssetBaseURL: string, + remoteAssetURL: string, assetSubPath: string ): AssetPromise { const loadingPromises = this._loadingPromises; - const subPromiseCallback = this._subAssetPromiseCallbacks[assetBaseURL]?.[assetSubPath]; + const subPromiseCallback = this._subAssetPromiseCallbacks[remoteAssetBaseURL]?.[assetSubPath]; const resolvedValue = subPromiseCallback?.resolvedValue; const rejectedValue = subPromiseCallback?.rejectedValue; @@ -438,19 +469,19 @@ export class ResourceManager { // Pending const promise = new AssetPromise((resolve, reject) => { - (this._subAssetPromiseCallbacks[assetBaseURL] ||= {})[assetSubPath] = { + (this._subAssetPromiseCallbacks[remoteAssetBaseURL] ||= {})[assetSubPath] = { resolve, reject }; }); - loadingPromises[assetURL] = promise; + loadingPromises[remoteAssetURL] = promise; promise.then( () => { - delete loadingPromises[assetURL]; + delete loadingPromises[remoteAssetURL]; }, - () => delete loadingPromises[assetURL] + () => delete loadingPromises[remoteAssetURL] ); return promise; @@ -538,15 +569,19 @@ export class ResourceManager { promise = Promise.resolve(obj); } else { const resourceConfig = this._editorResourceConfig[refId]; - let url = resourceConfig?.path; - if (!url) { + if (!resourceConfig) { Logger.warn(`refId:${refId} is not find in this._editorResourceConfig.`); return Promise.resolve(null); } - url = key ? `${url}${url.indexOf("?") > -1 ? "&" : "?"}q=${key}` : url; + const remoteUrl = resourceConfig.path; + const queryPath = new URL(remoteUrl).search; + let url = resourceConfig.virtualPath + queryPath; + if (key) { + url += (url.indexOf("?") > -1 ? "&" : "?") + "q=" + key; + } + promise = this.load({ url, - virtualPath: resourceConfig.virtualPath, type: resourceConfig.type }); } diff --git a/packages/core/src/particle/ParticleGenerator.ts b/packages/core/src/particle/ParticleGenerator.ts index f7e74dad42..1f3baa89c0 100644 --- a/packages/core/src/particle/ParticleGenerator.ts +++ b/packages/core/src/particle/ParticleGenerator.ts @@ -14,6 +14,7 @@ import { SetDataOptions } from "../graphic/enums/SetDataOptions"; import { VertexAttribute } from "../mesh"; import { ShaderData } from "../shader"; import { Buffer } from "./../graphic/Buffer"; +import { ParticleBufferUtils } from "./ParticleBufferUtils"; import { ParticleRenderer, ParticleUpdateFlags } from "./ParticleRenderer"; import { ParticleCurveMode } from "./enums/ParticleCurveMode"; import { ParticleGradientMode } from "./enums/ParticleGradientMode"; @@ -23,12 +24,11 @@ import { ParticleStopMode } from "./enums/ParticleStopMode"; import { ColorOverLifetimeModule } from "./modules/ColorOverLifetimeModule"; import { EmissionModule } from "./modules/EmissionModule"; import { MainModule } from "./modules/MainModule"; +import { ParticleCompositeCurve } from "./modules/ParticleCompositeCurve"; import { RotationOverLifetimeModule } from "./modules/RotationOverLifetimeModule"; import { SizeOverLifetimeModule } from "./modules/SizeOverLifetimeModule"; import { TextureSheetAnimationModule } from "./modules/TextureSheetAnimationModule"; import { VelocityOverLifetimeModule } from "./modules/VelocityOverLifetimeModule"; -import { ParticleBufferUtils } from "./ParticleBufferUtils"; -import { ParticleCompositeCurve } from "./modules/ParticleCompositeCurve"; /** * Particle Generator. @@ -121,6 +121,8 @@ export class ParticleGenerator { private _firstActiveTransformedBoundingBox = 0; @ignoreClone private _firstFreeTransformedBoundingBox = 0; + @ignoreClone + private _playStartDelay = 0; /** * Whether the particle generator is contain alive or is still creating particles. @@ -187,6 +189,8 @@ export class ParticleGenerator { if (this.useAutoRandomSeed) { this._resetGlobalRandSeed(Math.floor(Math.random() * 0xffffffff)); // 2^32 - 1 } + + this._playStartDelay = this.main.startDelay.evaluate(undefined, this.main._startDelayRand.random()); } } @@ -266,8 +270,20 @@ export class ParticleGenerator { const { main, emission } = this; const duration = main.duration; const lastPlayTime = this._playTime; + const deltaTime = elapsedTime * main.simulationSpeed; + + // Process start delay time + if (this._playStartDelay > 0) { + const remainingDelay = (this._playStartDelay -= deltaTime); + if (remainingDelay < 0) { + this._playTime -= remainingDelay; + this._playStartDelay = 0; + } else { + return; + } + } - this._playTime += elapsedTime * main.simulationSpeed; + this._playTime += deltaTime; this._retireActiveParticles(); this._freeRetiredParticles(); @@ -758,8 +774,16 @@ export class ParticleGenerator { // Start speed instanceVertices[offset + 18] = startSpeed; - // Unused, Color, size, rotation, - // instanceVertices[offset + 19] = rand.random(); + // Gravity, unused, size, rotation + switch (main.gravityModifier.mode) { + case ParticleCurveMode.Constant: + instanceVertices[offset + 19] = main.gravityModifier.constant; + break; + case ParticleCurveMode.TwoConstants: + instanceVertices[offset + 19] = main.gravityModifier.evaluate(undefined, main._gravityModifierRand.random()); + break; + } + const colorOverLifetime = this.colorOverLifetime; if (colorOverLifetime.enabled && colorOverLifetime.color.mode === ParticleGradientMode.TwoGradients) { instanceVertices[offset + 20] = colorOverLifetime._colorGradientRand.random(); diff --git a/packages/core/src/particle/ParticleRenderer.ts b/packages/core/src/particle/ParticleRenderer.ts index a03b357191..5bb7b51da4 100644 --- a/packages/core/src/particle/ParticleRenderer.ts +++ b/packages/core/src/particle/ParticleRenderer.ts @@ -233,11 +233,11 @@ export class ParticleRenderer extends Renderer { } protected override _onDestroy(): void { - super._onDestroy(); const mesh = this._mesh; if (mesh) { mesh.destroyed || this._addResourceReferCount(mesh, -1); } + super._onDestroy(); this.generator._destroy(); } diff --git a/packages/core/src/particle/modules/MainModule.ts b/packages/core/src/particle/modules/MainModule.ts index 0349aec31b..94a73610e8 100644 --- a/packages/core/src/particle/modules/MainModule.ts +++ b/packages/core/src/particle/modules/MainModule.ts @@ -1,4 +1,5 @@ import { Color, Rand, Vector3, Vector4 } from "@galacean/engine-math"; +import { TransformModifyFlags } from "../../Transform"; import { deepClone, ignoreClone } from "../../clone/CloneManager"; import { ICustomClone } from "../../clone/ComponentCloner"; import { ShaderData } from "../../shader/ShaderData"; @@ -9,10 +10,9 @@ import { ParticleScaleMode } from "../enums/ParticleScaleMode"; import { ParticleSimulationSpace } from "../enums/ParticleSimulationSpace"; import { ParticleCompositeCurve } from "./ParticleCompositeCurve"; import { ParticleCompositeGradient } from "./ParticleCompositeGradient"; -import { TransformModifyFlags } from "../../Transform"; export class MainModule implements ICustomClone { - private static _tempVector40 = new Vector4(); + private _tempVector40 = new Vector4(); private static _vector3One = new Vector3(1, 1, 1); private static readonly _positionScale = ShaderProperty.getByName("renderer_PositionScale"); @@ -63,6 +63,9 @@ export class MainModule implements ICustomClone { _maxParticleBuffer = 1000; /** @internal */ @ignoreClone + readonly _startDelayRand = new Rand(0, ParticleRandomSubSeeds.StartDelay); + /** @internal */ + @ignoreClone readonly _startSpeedRand = new Rand(0, ParticleRandomSubSeeds.StartSpeed); /** @internal */ @ignoreClone @@ -96,8 +99,6 @@ export class MainModule implements ICustomClone { private _simulationSpace = ParticleSimulationSpace.Local; @ignoreClone private _generator: ParticleGenerator; - @ignoreClone - private _gravity = new Vector3(); /** * The initial lifetime of particles when emitted. @@ -295,8 +296,7 @@ export class MainModule implements ICustomClone { case ParticleSimulationSpace.Local: shaderData.setVector3(MainModule._worldPosition, transform.worldPosition); const worldRotation = transform.worldRotationQuaternion; - const worldRotationV4 = MainModule._tempVector40; - worldRotationV4.copyFrom(worldRotation); + const worldRotationV4 = this._tempVector40.copyFrom(worldRotation); // Maybe shaderData should support Quaternion shaderData.setVector4(MainModule._worldRotation, worldRotationV4); break; case ParticleSimulationSpace.World: @@ -322,11 +322,7 @@ export class MainModule implements ICustomClone { break; } - const particleGravity = this._gravity; - const gravityModifierValue = this.gravityModifier.evaluate(undefined, this._gravityModifierRand.random()); - Vector3.scale(renderer.scene.physics.gravity, gravityModifierValue, particleGravity); - - shaderData.setVector3(MainModule._gravity, particleGravity); + shaderData.setVector3(MainModule._gravity, renderer.scene.physics.gravity); shaderData.setInt(MainModule._simulationSpace, this.simulationSpace); shaderData.setFloat(MainModule._startRotation3D, +this.startRotation3D); shaderData.setInt(MainModule._scaleMode, this.scalingMode); diff --git a/packages/core/src/shaderlib/extra/particle.vs.glsl b/packages/core/src/shaderlib/extra/particle.vs.glsl index e41918c787..dfdf5723f9 100644 --- a/packages/core/src/shaderlib/extra/particle.vs.glsl +++ b/packages/core/src/shaderlib/extra/particle.vs.glsl @@ -74,7 +74,7 @@ void main() { lifeVelocity = computeParticleLifeVelocity(normalizedAge); #endif - vec3 gravityVelocity = renderer_Gravity * age; + vec3 gravityVelocity = renderer_Gravity * a_Random0.x * age; vec4 worldRotation; if (renderer_SimulationSpace == 0) { diff --git a/packages/core/src/shaderlib/particle/texture_sheet_animation_module.glsl b/packages/core/src/shaderlib/particle/texture_sheet_animation_module.glsl index 42fb287d73..7187dfcb2f 100644 --- a/packages/core/src/shaderlib/particle/texture_sheet_animation_module.glsl +++ b/packages/core/src/shaderlib/particle/texture_sheet_animation_module.glsl @@ -20,7 +20,7 @@ vec2 computeParticleUV(in vec2 uv, in float normalizedAge) { float frame = floor(normalizedFrame * renderer_TSATillingParams.z); float tileRow = frame * renderer_TSATillingParams.x; - float floorTotalULength = floor(tileRow); + float tileRowIndex = floor(tileRow); uv.x += tileRow - tileRowIndex; uv.y += tileRowIndex * renderer_TSATillingParams.y; #endif diff --git a/packages/design/package.json b/packages/design/package.json index 49791f9f52..e23c7c29e8 100644 --- a/packages/design/package.json +++ b/packages/design/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-design", - "version": "1.3.15", + "version": "1.3.20", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/galacean/package.json b/packages/galacean/package.json index 51ec81fdab..7aeec98b99 100644 --- a/packages/galacean/package.json +++ b/packages/galacean/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine", - "version": "1.3.15", + "version": "1.3.20", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/loader/package.json b/packages/loader/package.json index 51675522b9..d2572af034 100644 --- a/packages/loader/package.json +++ b/packages/loader/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-loader", - "version": "1.3.15", + "version": "1.3.20", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/loader/src/AnimationClipLoader.ts b/packages/loader/src/AnimationClipLoader.ts index b6352a3dd5..58227e65fc 100644 --- a/packages/loader/src/AnimationClipLoader.ts +++ b/packages/loader/src/AnimationClipLoader.ts @@ -14,10 +14,12 @@ import { decode } from "./resource-deserialize"; class AnimationClipLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { - this.request(item.url, { - ...item, - type: "arraybuffer" - }) + resourceManager + // @ts-ignore + ._request(item.url, { + ...item, + type: "arraybuffer" + }) .then((data) => { return decode(data, resourceManager.engine) .then((clip) => { diff --git a/packages/loader/src/AnimatorControllerLoader.ts b/packages/loader/src/AnimatorControllerLoader.ts index 08751a9465..161d107b7f 100644 --- a/packages/loader/src/AnimatorControllerLoader.ts +++ b/packages/loader/src/AnimatorControllerLoader.ts @@ -18,10 +18,12 @@ import { class AnimatorControllerLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { - this.request(item.url, { - ...item, - type: "json" - }) + resourceManager + // @ts-ignore + ._request(item.url, { + ...item, + type: "json" + }) .then((data) => { const animatorController = new AnimatorController(resourceManager.engine); const { layers, parameters } = data; diff --git a/packages/loader/src/BufferLoader.ts b/packages/loader/src/BufferLoader.ts index c4bc38e9fe..6d427cdd29 100644 --- a/packages/loader/src/BufferLoader.ts +++ b/packages/loader/src/BufferLoader.ts @@ -1,11 +1,11 @@ -import { resourceLoader, Loader, AssetPromise, AssetType, LoadItem } from "@galacean/engine-core"; +import { resourceLoader, Loader, AssetPromise, AssetType, LoadItem, ResourceManager } from "@galacean/engine-core"; function isBase64(url) { return /^data:(.+?);base64,/.test(url); } @resourceLoader(AssetType.Buffer, ["bin", "r3bin"], false) class BufferLoader extends Loader { - load(item: LoadItem): AssetPromise { + load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { const url = item.url; if (isBase64(url)) { return new AssetPromise((resolve) => { @@ -14,7 +14,8 @@ class BufferLoader extends Loader { resolve(result.buffer); }); } - return this.request(url, { + // @ts-ignore + return resourceManager._request(url, { ...item, type: "arraybuffer" }); diff --git a/packages/loader/src/EnvLoader.ts b/packages/loader/src/EnvLoader.ts index d7c9ca7290..6b04a4c563 100644 --- a/packages/loader/src/EnvLoader.ts +++ b/packages/loader/src/EnvLoader.ts @@ -17,7 +17,9 @@ import { SphericalHarmonics3 } from "@galacean/engine-math"; class EnvLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { - this.request(item.url, { ...item, type: "arraybuffer" }) + resourceManager + // @ts-ignore + ._request(item.url, { ...item, type: "arraybuffer" }) .then((arraybuffer) => { const shArray = new Float32Array(arraybuffer, 0, 27); const shByteLength = 27 * 4; diff --git a/packages/loader/src/FontLoader.ts b/packages/loader/src/FontLoader.ts index 2357ada08a..aabc04c396 100644 --- a/packages/loader/src/FontLoader.ts +++ b/packages/loader/src/FontLoader.ts @@ -12,7 +12,9 @@ import { class FontLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { - this.request(item.url, { ...item, type: "json" }) + resourceManager + // @ts-ignore + ._request(item.url, { ...item, type: "json" }) .then((data) => { const { fontName, fontUrl } = data; diff --git a/packages/loader/src/GLTFLoader.ts b/packages/loader/src/GLTFLoader.ts index 0707d17aac..cff590de7f 100644 --- a/packages/loader/src/GLTFLoader.ts +++ b/packages/loader/src/GLTFLoader.ts @@ -37,9 +37,8 @@ export class GLTFLoader extends Loader { } override load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { - const url = item.url; const params = item.params; - const glTFResource = new GLTFResource(resourceManager.engine, url); + const glTFResource = new GLTFResource(resourceManager.engine, item.url); const context = new GLTFParserContext(glTFResource, resourceManager, { keepMeshData: false, ...params diff --git a/packages/loader/src/HDRLoader.ts b/packages/loader/src/HDRLoader.ts index 3f72080728..b31099b98a 100644 --- a/packages/loader/src/HDRLoader.ts +++ b/packages/loader/src/HDRLoader.ts @@ -1,13 +1,17 @@ import { AssetPromise, AssetType, - Loader, + ContentRestorer, + Engine, LoadItem, - resourceLoader, + Loader, ResourceManager, TextureCube, - TextureCubeFace + TextureCubeFace, + request, + resourceLoader } from "@galacean/engine-core"; +import { RequestConfig } from "@galacean/engine-core/types/asset/request"; import { Color, Vector3 } from "@galacean/engine-math"; const PI = Math.PI; @@ -82,6 +86,23 @@ class HDRLoader extends Loader { private static _temp4Vector3 = new Vector3(); private static _temp5Vector3 = new Vector3(); + /** + * @internal + */ + static _setTextureByBuffer(engine: Engine, buffer: ArrayBuffer, texture?: TextureCube) { + const bufferArray = new Uint8Array(buffer); + const { width, height, dataPosition } = HDRLoader._parseHeader(bufferArray); + const cubeSize = height >> 1; + texture ||= new TextureCube(engine, cubeSize); + const pixels = HDRLoader._readPixels(bufferArray.subarray(dataPosition), width, height); + const cubeMapData = HDRLoader._convertToCubemap(pixels, width, height, cubeSize); + for (let faceIndex = 0; faceIndex < 6; faceIndex++) { + texture.setPixelBuffer(TextureCubeFace.PositiveX + faceIndex, cubeMapData[faceIndex], 0); + } + texture.generateMipmaps(); + return texture; + } + private static _convertToCubemap( pixels: Uint8Array, inputWidth: number, @@ -376,28 +397,43 @@ class HDRLoader extends Loader { color.b *= scaleFactor; color.a *= M; } - load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { const engine = resourceManager.engine; - - this.request(item.url, { ...item, type: "arraybuffer" }) + const requestConfig = { ...item, type: "arraybuffer" } as RequestConfig; + resourceManager + // @ts-ignore + ._request(item.url, requestConfig) .then((buffer) => { - const uint8Array = new Uint8Array(buffer); - const { width, height, dataPosition } = HDRLoader._parseHeader(uint8Array); - const pixels = HDRLoader._readPixels(uint8Array.subarray(dataPosition), width, height); - const cubeSize = height >> 1; - - const cubeMapData = HDRLoader._convertToCubemap(pixels, width, height, cubeSize); - const texture = new TextureCube(engine, cubeSize); - - for (let faceIndex = 0; faceIndex < 6; faceIndex++) { - texture.setPixelBuffer(TextureCubeFace.PositiveX + faceIndex, cubeMapData[faceIndex], 0); - } - texture.generateMipmaps(); + const texture = HDRLoader._setTextureByBuffer(engine, buffer); + engine.resourceManager.addContentRestorer(new HDRContentRestorer(texture, item.url, requestConfig)); resolve(texture); }) .catch(reject); }); } } + +/** + * @internal + */ +class HDRContentRestorer extends ContentRestorer { + constructor( + resource: TextureCube, + public url: string, + public requestConfig: RequestConfig + ) { + super(resource); + } + + override restoreContent(): AssetPromise { + return new AssetPromise((resolve, reject) => { + request(this.url, this.requestConfig) + .then((buffer) => { + HDRLoader._setTextureByBuffer(this.resource.engine, buffer, this.resource); + resolve(this.resource); + }) + .catch(reject); + }); + } +} diff --git a/packages/loader/src/JSONLoader.ts b/packages/loader/src/JSONLoader.ts index 364b1159b4..ec96834331 100644 --- a/packages/loader/src/JSONLoader.ts +++ b/packages/loader/src/JSONLoader.ts @@ -1,9 +1,10 @@ -import { resourceLoader, Loader, AssetPromise, AssetType, LoadItem } from "@galacean/engine-core"; +import { resourceLoader, Loader, AssetPromise, AssetType, LoadItem, ResourceManager } from "@galacean/engine-core"; @resourceLoader(AssetType.JSON, ["json"], false) class JSONLoader extends Loader { - load(item: LoadItem): AssetPromise { - return this.request(item.url, { + load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { + // @ts-ignore + return resourceManager._request(item.url, { ...item, type: "json" }); diff --git a/packages/loader/src/KTXCubeLoader.ts b/packages/loader/src/KTXCubeLoader.ts index 8e459c50a4..023d338357 100644 --- a/packages/loader/src/KTXCubeLoader.ts +++ b/packages/loader/src/KTXCubeLoader.ts @@ -16,7 +16,8 @@ class KTXCubeLoader extends Loader { return new AssetPromise((resolve, reject) => { Promise.all( item.urls.map((url) => - this.request(url, { + // @ts-ignore + resourceManager._request(url, { ...item, type: "arraybuffer" }) diff --git a/packages/loader/src/KTXLoader.ts b/packages/loader/src/KTXLoader.ts index cdfa175a22..b25af122ed 100644 --- a/packages/loader/src/KTXLoader.ts +++ b/packages/loader/src/KTXLoader.ts @@ -13,10 +13,12 @@ import { parseSingleKTX } from "./compressed-texture"; export class KTXLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { - this.request(item.url, { - ...item, - type: "arraybuffer" - }) + resourceManager + // @ts-ignore + ._request(item.url, { + ...item, + type: "arraybuffer" + }) .then((bin) => { const parsedData = parseSingleKTX(bin); const { width, height, mipmaps, engineFormat } = parsedData; diff --git a/packages/loader/src/MaterialLoader.ts b/packages/loader/src/MaterialLoader.ts index 51d76f9ec8..162d2e0c87 100644 --- a/packages/loader/src/MaterialLoader.ts +++ b/packages/loader/src/MaterialLoader.ts @@ -35,32 +35,32 @@ function parseProperty(object: Object, key: string, value: any) { class MaterialLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { - this.request(item.url, { - ...item, - type: "json" - }) + resourceManager + // @ts-ignore + ._request(item.url, { + ...item, + type: "json" + }) .then((materialSchema: IMaterialSchema) => { const engine = resourceManager.engine; - const { shaderRef, shader } = materialSchema; - - if (shaderRef) { + const { shaderRef, shader: shaderName } = materialSchema; + const shader = Shader.find(shaderName); + if (shader) { + resolve(this._getMaterialByShader(materialSchema, shader, engine)); + } else if (shaderRef) { resolve( resourceManager // @ts-ignore .getResourceByRef(shaderRef) - .then((shaderObject) => this.getMaterialByShader(materialSchema, shaderObject, engine)) + .then((shader) => this._getMaterialByShader(materialSchema, shader, engine)) ); - } else { - // compatible with 1.2-pre version material schema - const shaderObject = Shader.find(shader); - resolve(this.getMaterialByShader(materialSchema, shaderObject, engine)); } }) .catch(reject); }); } - private getMaterialByShader(materialSchema: IMaterialSchema, shader: Shader, engine: Engine): Promise { + private _getMaterialByShader(materialSchema: IMaterialSchema, shader: Shader, engine: Engine): Promise { const { name, shaderData, macros, renderState } = materialSchema; const material = new Material(engine, shader); diff --git a/packages/loader/src/MeshLoader.ts b/packages/loader/src/MeshLoader.ts index 73ff264273..e183057a3e 100644 --- a/packages/loader/src/MeshLoader.ts +++ b/packages/loader/src/MeshLoader.ts @@ -13,10 +13,12 @@ import { decode } from "./resource-deserialize"; class MeshLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { - this.request(item.url, { - ...item, - type: "arraybuffer" - }) + resourceManager + // @ts-ignore + ._request(item.url, { + ...item, + type: "arraybuffer" + }) .then((data) => { return decode(data, resourceManager.engine); }) diff --git a/packages/loader/src/PrefabLoader.ts b/packages/loader/src/PrefabLoader.ts index 8eaeb792ef..f2009b8f0c 100644 --- a/packages/loader/src/PrefabLoader.ts +++ b/packages/loader/src/PrefabLoader.ts @@ -9,12 +9,15 @@ export class PrefabLoader extends Loader { const engine = resourceManager.engine; return new AssetPromise((resolve, reject) => { - this.request(item.url, { - ...item, - type: "json" - }).then((data) => { - PrefabParser.parse(engine, item.url, data).then(resolve).catch(reject); - }); + resourceManager + // @ts-ignore + ._request(item.url, { + ...item, + type: "json" + }) + .then((data) => { + PrefabParser.parse(engine, item.url, data).then(resolve).catch(reject); + }); }); } } diff --git a/packages/loader/src/PrimitiveMeshLoader.ts b/packages/loader/src/PrimitiveMeshLoader.ts index 59d4e58863..91c2ad2c84 100644 --- a/packages/loader/src/PrimitiveMeshLoader.ts +++ b/packages/loader/src/PrimitiveMeshLoader.ts @@ -5,65 +5,72 @@ import { Loader, ModelMesh, PrimitiveMesh, + ResourceManager, resourceLoader } from "@galacean/engine-core"; @resourceLoader(AssetType.PrimitiveMesh, ["mesh"], false) class PrimitiveMeshLoader extends Loader { - load(item: LoadItem, { engine }): AssetPromise { - return this.request(item.url, { - ...item, - type: "json" - }).then((data) => { - switch (data.type) { - case PrimitiveMeshType.Sphere: - return PrimitiveMesh.createSubdivisionSurfaceSphere(engine, data.sphereRadius, data.sphereStep); - case PrimitiveMeshType.Capsule: - return PrimitiveMesh.createCapsule( - engine, - data.capsuleRadius, - data.capsuleHeight, - data.capsuleRadialSegments, - data.capsuleHeightSegments - ); - case PrimitiveMeshType.Cone: - return PrimitiveMesh.createCone( - engine, - data.coneRadius, - data.coneHeight, - data.coneRadialSegment, - data.coneHeightSegment - ); - case PrimitiveMeshType.Cuboid: - return PrimitiveMesh.createCuboid(engine, data.cuboidWidth, data.cuboidHeight, data.cuboidDepth); - case PrimitiveMeshType.Cylinder: - return PrimitiveMesh.createCylinder( - engine, - data.cylinderRadiusTop, - data.cylinderRadiusBottom, - data.cylinderHeight, - data.cylinderRadialSegment, - data.cylinderHeightSegment - ); - case PrimitiveMeshType.Plane: - return PrimitiveMesh.createPlane( - engine, - data.planeWidth, - data.planeHeight, - data.planeHorizontalSegments, - data.planeVerticalSegments - ); - case PrimitiveMeshType.Torus: - return PrimitiveMesh.createTorus( - engine, - data.torusRadius, - data.torusTubeRadius, - data.torusRadialSegments, - data.torusTubularSegments, - data.torusArc - ); - } - }); + load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { + const { engine } = resourceManager; + return ( + resourceManager + // @ts-ignore + ._request(item.url, { + ...item, + type: "json" + }) + .then((data) => { + switch (data.type) { + case PrimitiveMeshType.Sphere: + return PrimitiveMesh.createSubdivisionSurfaceSphere(engine, data.sphereRadius, data.sphereStep); + case PrimitiveMeshType.Capsule: + return PrimitiveMesh.createCapsule( + engine, + data.capsuleRadius, + data.capsuleHeight, + data.capsuleRadialSegments, + data.capsuleHeightSegments + ); + case PrimitiveMeshType.Cone: + return PrimitiveMesh.createCone( + engine, + data.coneRadius, + data.coneHeight, + data.coneRadialSegment, + data.coneHeightSegment + ); + case PrimitiveMeshType.Cuboid: + return PrimitiveMesh.createCuboid(engine, data.cuboidWidth, data.cuboidHeight, data.cuboidDepth); + case PrimitiveMeshType.Cylinder: + return PrimitiveMesh.createCylinder( + engine, + data.cylinderRadiusTop, + data.cylinderRadiusBottom, + data.cylinderHeight, + data.cylinderRadialSegment, + data.cylinderHeightSegment + ); + case PrimitiveMeshType.Plane: + return PrimitiveMesh.createPlane( + engine, + data.planeWidth, + data.planeHeight, + data.planeHorizontalSegments, + data.planeVerticalSegments + ); + case PrimitiveMeshType.Torus: + return PrimitiveMesh.createTorus( + engine, + data.torusRadius, + data.torusTubeRadius, + data.torusRadialSegments, + data.torusTubularSegments, + data.torusArc + ); + } + }) + ); } } diff --git a/packages/loader/src/ProjectLoader.ts b/packages/loader/src/ProjectLoader.ts index 9c9c54ff12..392ea0b179 100644 --- a/packages/loader/src/ProjectLoader.ts +++ b/packages/loader/src/ProjectLoader.ts @@ -14,7 +14,9 @@ class ProjectLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { const { engine } = resourceManager; return new AssetPromise((resolve, reject) => { - this.request(item.url, { ...item, type: "json" }) + resourceManager + // @ts-ignore + ._request(item.url, { ...item, type: "json" }) .then((data) => { // @ts-ignore engine.resourceManager.initVirtualResources(data.files); diff --git a/packages/loader/src/SceneLoader.ts b/packages/loader/src/SceneLoader.ts index 70c851a8e6..7d191991d3 100644 --- a/packages/loader/src/SceneLoader.ts +++ b/packages/loader/src/SceneLoader.ts @@ -21,7 +21,9 @@ class SceneLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { const { engine } = resourceManager; return new AssetPromise((resolve, reject) => { - this.request(item.url, { ...item, type: "json" }) + resourceManager + // @ts-ignore + ._request(item.url, { ...item, type: "json" }) .then((data) => { return SceneParser.parse(engine, data).then((scene) => { const promises = []; diff --git a/packages/loader/src/ShaderChunkLoader.ts b/packages/loader/src/ShaderChunkLoader.ts index fd72e38e98..6ebfc418c9 100644 --- a/packages/loader/src/ShaderChunkLoader.ts +++ b/packages/loader/src/ShaderChunkLoader.ts @@ -6,48 +6,45 @@ import { ShaderFactory, resourceLoader, // @ts-ignore - ShaderLib + ShaderLib, + Utils } from "@galacean/engine-core"; -import { PathUtils } from "./PathUtils"; @resourceLoader("ShaderChunk", ["glsl"]) -class ShaderChunkLoader extends Loader { - load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { - const { virtualPath, url } = item; - const shaderVirtualPath = item.params?.shaderVirtualPath ?? "/"; - const chunkPath = virtualPath ?? new URL(url).pathname; +export class ShaderChunkLoader extends Loader { + private static _shaderIncludeRegex = /\s#include\s+"([./][^\\"]+)"/gm; - return this.request(url, { ...item, type: "text" }).then((code: string) => { - ShaderFactory.registerInclude(chunkPath.substring(1), code); + /** + * @internal + */ + static _loadChunksInCode(code: string, basePath: string, resourceManager: ResourceManager): Promise { + const shaderChunkPaths = new Array(); + const matches = code.matchAll(ShaderChunkLoader._shaderIncludeRegex); + for (const match of matches) { + const chunkPath = Utils.resolveAbsoluteUrl(basePath, match[1]); + if (!ShaderLib[chunkPath.substring(1)]) { + shaderChunkPaths.push(chunkPath); + } + } - return _loadChunksInCode(code, shaderVirtualPath, resourceManager); - }); + return Promise.all( + shaderChunkPaths.map((chunkPath) => { + return resourceManager.load({ + type: "ShaderChunk", + url: chunkPath + }); + }) + ); } -} -/** @internal */ -export function _loadChunksInCode( - code: string, - shaderVirtualPath: string, - resourceManager: ResourceManager -): Promise { - const shaderChunkPaths: string[] = []; - const matches = code.matchAll(PathUtils.shaderIncludeRegex); - for (const match of matches) { - const chunkPath = PathUtils.pathResolve(match[1], shaderVirtualPath); - if (!ShaderLib[chunkPath.substring(1)]) { - shaderChunkPaths.push(chunkPath); - } - } + load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { + const { url } = item; + + // @ts-ignore + return resourceManager._request(url, { ...item, type: "text" }).then((code) => { + ShaderFactory.registerInclude(url.substring(1), code); - return Promise.all( - shaderChunkPaths.map((chunkPath) => { - return resourceManager.load({ - type: "ShaderChunk", - url: chunkPath, - virtualPath: chunkPath, - params: { shaderVirtualPath } - }); - }) - ); + return ShaderChunkLoader._loadChunksInCode(code, url, resourceManager); + }); + } } diff --git a/packages/loader/src/ShaderLoader.ts b/packages/loader/src/ShaderLoader.ts index e21150ab94..e1cafbf9ff 100644 --- a/packages/loader/src/ShaderLoader.ts +++ b/packages/loader/src/ShaderLoader.ts @@ -7,30 +7,32 @@ import { Shader, resourceLoader } from "@galacean/engine-core"; -import { _loadChunksInCode } from "./ShaderChunkLoader"; +import { ShaderChunkLoader } from "./ShaderChunkLoader"; @resourceLoader(AssetType.Shader, ["gs", "gsl"]) class ShaderLoader extends Loader { private static _builtinRegex = /^\s*\/\/\s*@builtin\s+(\w+)/; load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { - const { virtualPath, url } = item; - const shaderVirtualPath = virtualPath ?? "/"; + const { url } = item; - return this.request(url, { ...item, type: "text" }).then((code: string) => { - const builtinShader = this.getBuiltinShader(code); - // TODO: delete the snippets below when breaking change version released + // @ts-ignore + return resourceManager._request(url, { ...item, type: "text" }).then((code: string) => { + const builtinShader = this._getBuiltinShader(code); if (builtinShader) { return Shader.find(builtinShader); } - return _loadChunksInCode(code, shaderVirtualPath, resourceManager).then(() => { - return Shader.create(code); + return ShaderChunkLoader._loadChunksInCode(code, url, resourceManager).then(() => { + const shader = Shader.create(code); + // @ts-ignore + shader._registerPath(url); + return shader; }); }); } - private getBuiltinShader(code: string) { + private _getBuiltinShader(code: string) { const match = code.match(ShaderLoader._builtinRegex); if (match && match[1]) return match[1]; } diff --git a/packages/loader/src/SourceFontLoader.ts b/packages/loader/src/SourceFontLoader.ts index ee8549560b..aa10cdd96a 100644 --- a/packages/loader/src/SourceFontLoader.ts +++ b/packages/loader/src/SourceFontLoader.ts @@ -12,7 +12,8 @@ import { class SourceFontLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { - const { url } = item; + // @ts-ignore + const url = resourceManager._getRemoteUrl(item.url); this._registerFont(url, url) .then(() => { const font = new Font(resourceManager.engine, url); diff --git a/packages/loader/src/SpriteAtlasLoader.ts b/packages/loader/src/SpriteAtlasLoader.ts index 270773143b..f627255dd1 100644 --- a/packages/loader/src/SpriteAtlasLoader.ts +++ b/packages/loader/src/SpriteAtlasLoader.ts @@ -27,7 +27,8 @@ class SpriteAtlasLoader extends Loader { chainPromises[i].cancel(); } }); - const configPromise = this.request(item.url, { + // @ts-ignore + const configPromise = resourceManager._request(item.url, { ...item, type: "json" }); diff --git a/packages/loader/src/SpriteLoader.ts b/packages/loader/src/SpriteLoader.ts index f1aae8e423..9d075fe806 100644 --- a/packages/loader/src/SpriteLoader.ts +++ b/packages/loader/src/SpriteLoader.ts @@ -13,14 +13,19 @@ import { @resourceLoader(AssetType.Sprite, ["sprite"], false) class SpriteLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { - return this.request(item.url, { - ...item, - type: "json" - }).then((data) => { - return data.belongToAtlas - ? this._loadFromAtlas(resourceManager, data) - : this._loadFromTexture(resourceManager, data); - }); + return ( + resourceManager + // @ts-ignore + ._request(item.url, { + ...item, + type: "json" + }) + .then((data) => { + return data.belongToAtlas + ? this._loadFromAtlas(resourceManager, data) + : this._loadFromTexture(resourceManager, data); + }) + ); } private _loadFromAtlas(resourceManager: ResourceManager, data: any): AssetPromise { diff --git a/packages/loader/src/TextLoader.ts b/packages/loader/src/TextLoader.ts index 1ea1b87feb..a13c1feec0 100644 --- a/packages/loader/src/TextLoader.ts +++ b/packages/loader/src/TextLoader.ts @@ -1,9 +1,10 @@ -import { resourceLoader, Loader, AssetPromise, AssetType, LoadItem } from "@galacean/engine-core"; +import { resourceLoader, Loader, AssetPromise, AssetType, LoadItem, ResourceManager } from "@galacean/engine-core"; @resourceLoader(AssetType.Text, ["txt"], false) class TextLoader extends Loader { - load(item: LoadItem): AssetPromise { - return this.request(item.url, { + load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { + // @ts-ignore + return resourceManager._request(item.url, { ...item, type: "text" }); diff --git a/packages/loader/src/Texture2DLoader.ts b/packages/loader/src/Texture2DLoader.ts index 71425b9a32..0c8af7dd54 100644 --- a/packages/loader/src/Texture2DLoader.ts +++ b/packages/loader/src/Texture2DLoader.ts @@ -22,7 +22,9 @@ class Texture2DLoader extends Loader { ...item, type: "image" }; - this.request(url, requestConfig) + resourceManager + // @ts-ignore + ._request(url, requestConfig) .onProgress(setTaskCompleteProgress, setTaskDetailProgress) .then((image) => { const { format, mipmap, anisoLevel, wrapModeU, wrapModeV, filterMode } = diff --git a/packages/loader/src/TextureCubeLoader.ts b/packages/loader/src/TextureCubeLoader.ts index 2977a2f93e..d90559d507 100644 --- a/packages/loader/src/TextureCubeLoader.ts +++ b/packages/loader/src/TextureCubeLoader.ts @@ -21,7 +21,8 @@ class TextureCubeLoader extends Loader { type: "image" }; - Promise.all(urls.map((url) => this.request(url, requestConfig))) + // @ts-ignore + Promise.all(urls.map((url) => resourceManager._request(url, requestConfig))) .then((images) => { const { width, height } = images[0]; diff --git a/packages/loader/src/gltf/GLTFUtils.ts b/packages/loader/src/gltf/GLTFUtils.ts index f6aa01e207..e186b6f0a3 100644 --- a/packages/loader/src/gltf/GLTFUtils.ts +++ b/packages/loader/src/gltf/GLTFUtils.ts @@ -389,31 +389,32 @@ export class GLTFUtils { const dataView = new DataView(originBuffer); - // read header + // Read header const header = { magic: dataView.getUint32(0, true), version: dataView.getUint32(UINT32_LENGTH, true), length: dataView.getUint32(2 * UINT32_LENGTH, true) }; + // Return the original buffer if it is not a glb if (header.magic !== GLB_HEADER_MAGIC) { return { originBuffer }; } - // read main data + // Read main data let chunkLength = dataView.getUint32(GLB_HEADER_LENGTH, true); let chunkType = dataView.getUint32(GLB_HEADER_LENGTH + UINT32_LENGTH, true); - // read glTF json + // Read glTF json if (chunkType !== GLB_CHUNK_TYPES.JSON) { console.error("Invalid glb chunk type. Expected 0x4E4F534A, found 0x" + chunkType.toString(16)); return null; } const glTFData = new Uint8Array(originBuffer, GLB_HEADER_LENGTH + 2 * UINT32_LENGTH, chunkLength); - const glTF: IGLTF = JSON.parse(Utils.decodeText(glTFData)); + const glTF = JSON.parse(Utils.decodeText(glTFData)); - // read all buffers + // Read all buffers const buffers: ArrayBuffer[] = []; let byteOffset = GLB_HEADER_LENGTH + 2 * UINT32_LENGTH + chunkLength; diff --git a/packages/loader/src/gltf/parser/GLTFBufferParser.ts b/packages/loader/src/gltf/parser/GLTFBufferParser.ts index 92222e836c..84a4f81b0d 100644 --- a/packages/loader/src/gltf/parser/GLTFBufferParser.ts +++ b/packages/loader/src/gltf/parser/GLTFBufferParser.ts @@ -14,14 +14,19 @@ export class GLTFBufferParser extends GLTFParser { } private _parseSingleBuffer(context: GLTFParserContext, bufferInfo: IBuffer): Promise { - const { glTFResource, contentRestorer } = context; + const { glTFResource, contentRestorer, resourceManager } = context; const url = glTFResource.url; + // @ts-ignore + const remoteUrl = resourceManager._getRemoteUrl(url); const restoreBufferRequests = contentRestorer.bufferRequests; const requestConfig = { type: "arraybuffer" }; - const absoluteUrl = Utils.resolveAbsoluteUrl(url, bufferInfo.uri); + const absoluteUrl = Utils.resolveAbsoluteUrl(remoteUrl, bufferInfo.uri); restoreBufferRequests.push(new BufferRequestInfo(absoluteUrl, requestConfig)); - const promise = request(absoluteUrl, requestConfig).onProgress(undefined, context._onTaskDetail); + const promise = resourceManager + // @ts-ignore + ._requestByRemoteUrl(absoluteUrl, requestConfig) + .onProgress(undefined, context._onTaskDetail); context._addTaskCompletePromise(promise); return promise; diff --git a/packages/loader/src/gltf/parser/GLTFSchemaParser.ts b/packages/loader/src/gltf/parser/GLTFSchemaParser.ts index 919b3f9921..ffc716702b 100644 --- a/packages/loader/src/gltf/parser/GLTFSchemaParser.ts +++ b/packages/loader/src/gltf/parser/GLTFSchemaParser.ts @@ -9,25 +9,35 @@ import { GLTFParserContext, GLTFParserType, registerGLTFParser } from "./GLTFPar @registerGLTFParser(GLTFParserType.Schema) export class GLTFSchemaParser extends GLTFParser { parse(context: GLTFParserContext): Promise { - const { glTFResource, contentRestorer } = context; + const { glTFResource, contentRestorer, resourceManager } = context; const url = glTFResource.url; const restoreBufferRequests = contentRestorer.bufferRequests; const requestConfig = { type: "arraybuffer" }; - return request(url, requestConfig) - .onProgress(undefined, context._onTaskDetail) - .then((buffer) => { - restoreBufferRequests.push(new BufferRequestInfo(url, requestConfig)); - return GLTFUtils.parseGLB(context, buffer); - }) - .then((result) => { - if (result?.glTF) { - contentRestorer.isGLB = true; - context.buffers = result.buffers; - return result.glTF; - } else { - contentRestorer.isGLB = false; - return JSON.parse(Utils.decodeText(new Uint8Array(result.originBuffer))); - } - }); + // @ts-ignore + const remoteUrl = resourceManager._getRemoteUrl(url); + return ( + resourceManager + // @ts-ignore + ._requestByRemoteUrl(remoteUrl, requestConfig) + .onProgress(undefined, context._onTaskDetail) + .then((buffer) => { + const parseResult = GLTFUtils.parseGLB(context, buffer); + // If the buffer is a GLB file, we need to restore the buffer data + if (parseResult?.glTF) { + restoreBufferRequests.push(new BufferRequestInfo(remoteUrl, requestConfig)); + } + return parseResult; + }) + .then((result) => { + if (result?.glTF) { + contentRestorer.isGLB = true; + context.buffers = result.buffers; + return result.glTF; + } else { + contentRestorer.isGLB = false; + return JSON.parse(Utils.decodeText(new Uint8Array(result.originBuffer))); + } + }) + ); } } diff --git a/packages/loader/src/ktx2/KTX2Loader.ts b/packages/loader/src/ktx2/KTX2Loader.ts index 511851d4bf..1d853e6239 100644 --- a/packages/loader/src/ktx2/KTX2Loader.ts +++ b/packages/loader/src/ktx2/KTX2Loader.ts @@ -221,7 +221,9 @@ export class KTX2Loader extends Loader { resourceManager: ResourceManager ): AssetPromise { return new AssetPromise((resolve, reject, setTaskCompleteProgress, setTaskDetailProgress) => { - this.request(item.url, { type: "arraybuffer" }) + resourceManager + // @ts-ignore + ._request(item.url, { type: "arraybuffer" }) .onProgress(setTaskCompleteProgress, setTaskDetailProgress) .then((buffer) => KTX2Loader._parseBuffer(new Uint8Array(buffer), resourceManager.engine, item.params).then( diff --git a/packages/loader/src/resource-deserialize/index.ts b/packages/loader/src/resource-deserialize/index.ts index 67531f3e90..b739322030 100644 --- a/packages/loader/src/resource-deserialize/index.ts +++ b/packages/loader/src/resource-deserialize/index.ts @@ -26,7 +26,6 @@ export function decode(arrayBuffer: ArrayBuffer, engine: Engine): Promise export * from "./resources/schema"; export * from "./resources/scene/SceneParser"; -export * from "./resources/scene/MeshLoader"; export * from "./resources/scene/EditorTextureLoader"; export * from "./resources/parser/ParserContext"; diff --git a/packages/loader/src/resource-deserialize/resources/scene/EditorTextureLoader.ts b/packages/loader/src/resource-deserialize/resources/scene/EditorTextureLoader.ts index 5a4d2ad197..793fc3e3f9 100644 --- a/packages/loader/src/resource-deserialize/resources/scene/EditorTextureLoader.ts +++ b/packages/loader/src/resource-deserialize/resources/scene/EditorTextureLoader.ts @@ -5,7 +5,9 @@ import { decode } from "../.."; export class EditorTextureLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { - this.request(item.url, { ...item, type: "arraybuffer" }) + resourceManager + // @ts-ignore + ._request(item.url, { ...item, type: "arraybuffer" }) .then((data) => { decode(data, resourceManager.engine).then((texture) => { resolve(texture); diff --git a/packages/loader/src/resource-deserialize/resources/scene/MeshLoader.ts b/packages/loader/src/resource-deserialize/resources/scene/MeshLoader.ts deleted file mode 100644 index 6618847eb9..0000000000 --- a/packages/loader/src/resource-deserialize/resources/scene/MeshLoader.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { AssetPromise, Loader, LoadItem, ModelMesh, resourceLoader, ResourceManager } from "@galacean/engine-core"; -import { decode } from "../.."; - -@resourceLoader("Mesh", ["prefab"], true) -export class MeshLoader extends Loader { - load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { - return new AssetPromise((resolve, reject) => { - this.request(item.url, { ...item, type: "arraybuffer" }) - .then((data) => { - decode(data, resourceManager.engine).then((mesh) => { - resolve(mesh); - }); - }) - .catch(reject); - }); - } -} diff --git a/packages/loader/src/resource-deserialize/resources/schema/MaterialSchema.ts b/packages/loader/src/resource-deserialize/resources/schema/MaterialSchema.ts index 6f4a73eef5..9df501328f 100644 --- a/packages/loader/src/resource-deserialize/resources/schema/MaterialSchema.ts +++ b/packages/loader/src/resource-deserialize/resources/schema/MaterialSchema.ts @@ -96,7 +96,7 @@ export interface IMaterialSchema { }; macros: Array<{ name: string; value?: string }>; renderState: IRenderState; - shaderRef: IShaderRef; + shaderRef: IAssetRef; } export enum MaterialLoaderType { diff --git a/packages/math/package.json b/packages/math/package.json index 7c75ab4757..7b67a5d553 100644 --- a/packages/math/package.json +++ b/packages/math/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-math", - "version": "1.3.15", + "version": "1.3.20", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/math/src/Color.ts b/packages/math/src/Color.ts index 018ff3ad85..cb67fa1c26 100644 --- a/packages/math/src/Color.ts +++ b/packages/math/src/Color.ts @@ -258,6 +258,19 @@ export class Color implements IClone, ICopy { return this; } + /** + * Copy to color like object. + * @param target - Color like object. + * @returns This Color like object + */ + copyTo(target: ColorLike): ColorLike { + target.r = this._r; + target.g = this._g; + target.b = this._b; + target.a = this._a; + return target; + } + /** * Copy from array like object. * @param source - Array like object diff --git a/packages/math/src/ICopy.ts b/packages/math/src/ICopy.ts index ac0f210c5e..0bf4d12122 100644 --- a/packages/math/src/ICopy.ts +++ b/packages/math/src/ICopy.ts @@ -7,4 +7,10 @@ export interface ICopy { * @returns This object */ copyFrom(source: S): T; + + /** + * Copy to target object. + * @returns This target object + */ + copyTo?(target: S): S; } diff --git a/packages/math/src/Quaternion.ts b/packages/math/src/Quaternion.ts index 0b396ae89a..112dcc12db 100644 --- a/packages/math/src/Quaternion.ts +++ b/packages/math/src/Quaternion.ts @@ -730,6 +730,19 @@ export class Quaternion implements IClone, ICopy, ICopy { return this; } + /** + * Copy to vector2 like object. + * @param target - Vector2 like object + * @returns This Vector2 like object + */ + copyTo(target: Vector2Like): Vector2Like { + target.x = this._x; + target.y = this._y; + return target; + } + /** * Copy the value of this vector from an array. * @param array - The array diff --git a/packages/math/src/Vector3.ts b/packages/math/src/Vector3.ts index a5eec72e9b..db6f2cb9f5 100644 --- a/packages/math/src/Vector3.ts +++ b/packages/math/src/Vector3.ts @@ -561,6 +561,18 @@ export class Vector3 implements IClone, ICopy { return this; } + /** + * Copy to vector3 like object. + * @param target - Vector3 like object + * @returns This Vector3 like object + */ + copyTo(target: Vector3Like): Vector3Like { + target.x = this._x; + target.y = this._y; + target.z = this._z; + return target; + } + /** * Copy the value of this vector from an array. * @param array - The array diff --git a/packages/math/src/Vector4.ts b/packages/math/src/Vector4.ts index 7328b3b09f..7884f8330d 100644 --- a/packages/math/src/Vector4.ts +++ b/packages/math/src/Vector4.ts @@ -477,6 +477,19 @@ export class Vector4 implements IClone, ICopy { return this; } + /** + * Copy to vector4 like object. + * @param target - Vector4 like object + * @returns This Vector4 like object + */ + copyTo(target: Vector4Like): Vector4Like { + target.x = this._x; + target.y = this._y; + target.z = this._z; + target.w = this._w; + return target; + } + /** * Copy the value of this vector by an array. * @param array - The array diff --git a/packages/physics-lite/package.json b/packages/physics-lite/package.json index 8613b8fca5..19a7821eb8 100644 --- a/packages/physics-lite/package.json +++ b/packages/physics-lite/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-physics-lite", - "version": "1.3.15", + "version": "1.3.20", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/physics-physx/package.json b/packages/physics-physx/package.json index b8d0d617e7..9790dc826d 100644 --- a/packages/physics-physx/package.json +++ b/packages/physics-physx/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-physics-physx", - "version": "1.3.15", + "version": "1.3.20", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/rhi-webgl/package.json b/packages/rhi-webgl/package.json index 92d28df54b..f0ef3a3fe7 100644 --- a/packages/rhi-webgl/package.json +++ b/packages/rhi-webgl/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-rhi-webgl", - "version": "1.3.15", + "version": "1.3.20", "repository": { "url": "https://github.com/galacean/engine.git" }, diff --git a/packages/shader-lab/package.json b/packages/shader-lab/package.json index eac4f58df0..369984ba6a 100644 --- a/packages/shader-lab/package.json +++ b/packages/shader-lab/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-shader-lab", - "version": "1.3.15", + "version": "1.3.20", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/xr-webxr/package.json b/packages/xr-webxr/package.json index 29f3404db3..2744932ddb 100644 --- a/packages/xr-webxr/package.json +++ b/packages/xr-webxr/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-xr-webxr", - "version": "1.3.15", + "version": "1.3.20", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/xr/package.json b/packages/xr/package.json index b4866c635d..dd353da751 100644 --- a/packages/xr/package.json +++ b/packages/xr/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-xr", - "version": "1.3.15", + "version": "1.3.20", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/xr/src/loader/XRReferenceImageLoader.ts b/packages/xr/src/loader/XRReferenceImageLoader.ts index 91f39db554..8476382721 100644 --- a/packages/xr/src/loader/XRReferenceImageLoader.ts +++ b/packages/xr/src/loader/XRReferenceImageLoader.ts @@ -5,7 +5,9 @@ import { XRReferenceImage } from "../feature/trackable/image/XRReferenceImage"; export class XRReferenceImageLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { - this.request(item.url, { ...item, type: "arraybuffer" }) + resourceManager + // @ts-ignore + ._request(item.url, { ...item, type: "arraybuffer" }) .then((data) => { decode(data, resourceManager.engine).then((referenceImage) => { resolve(referenceImage); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3c8e11a1a4..f304b354ad 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -150,7 +150,7 @@ importers: specifier: workspace:* version: link:../packages/shader-lab '@galacean/engine-toolkit': - specifier: ^1.0.0-beta.1 + specifier: ^1.3.9 version: 1.3.9(@galacean/engine@packages+galacean) dat.gui: specifier: ^0.7.9 diff --git a/tests/package.json b/tests/package.json index 0acd3edb23..956017f041 100644 --- a/tests/package.json +++ b/tests/package.json @@ -1,7 +1,7 @@ { "name": "@galacean/engine-tests", "private": true, - "version": "1.3.15", + "version": "1.3.20", "license": "MIT", "main": "dist/main.js", "module": "dist/module.js", diff --git a/tests/src/core/particle/ParticleRenderer.test.ts b/tests/src/core/particle/ParticleRenderer.test.ts new file mode 100644 index 0000000000..fd26b7270f --- /dev/null +++ b/tests/src/core/particle/ParticleRenderer.test.ts @@ -0,0 +1,94 @@ +import { WebGLEngine } from "@galacean/engine-rhi-webgl"; +import { Camera, ParticleRenderer, ModelMesh, Scene, ParticleRenderMode } from "@galacean/engine-core"; +import { beforeAll, describe, expect, it } from "vitest"; + +describe("ParticleRenderer", () => { + let engine: WebGLEngine; + let scene: Scene; + + beforeAll(async function () { + engine = await WebGLEngine.create({ + canvas: document.createElement("canvas") + }); + engine.canvas.resizeByClientSize(); + + scene = engine.sceneManager.activeScene; + const rootEntity = scene.createRootEntity("root"); + + const cameraEntity = rootEntity.createChild("Camera"); + cameraEntity.addComponent(Camera); + cameraEntity.transform.setPosition(0, 0, 10); + + engine.run(); + }); + + it("ParticleRenderer lengthScale", () => { + const renderer = scene.createRootEntity("Renderer").addComponent(ParticleRenderer); + renderer.lengthScale = 1; + expect(renderer.lengthScale).to.eq(1); + renderer.lengthScale = -0.333333; + expect(renderer.lengthScale).to.eq(-0.333333); + renderer.lengthScale = 0; + expect(renderer.lengthScale).to.eq(0); + }); + + it("ParticleRenderer velocityScale", () => { + const renderer = scene.createRootEntity("Renderer").addComponent(ParticleRenderer); + renderer.velocityScale = 1.333393; + expect(renderer.velocityScale).to.eq(1.333393); + renderer.velocityScale = -0.333333; + expect(renderer.velocityScale).to.eq(-0.333333); + renderer.velocityScale = 0; + expect(renderer.velocityScale).to.eq(0); + }); + + it("ParticleRenderer renderMode", () => { + const renderer = scene.createRootEntity("Renderer").addComponent(ParticleRenderer); + renderer.renderMode = ParticleRenderMode.None; + expect(renderer.renderMode).to.eq(ParticleRenderMode.None); + renderer.renderMode = ParticleRenderMode.Billboard; + expect(renderer.renderMode).to.eq(ParticleRenderMode.Billboard); + renderer.renderMode = ParticleRenderMode.StretchBillboard; + expect(renderer.renderMode).to.eq(ParticleRenderMode.StretchBillboard); + expect(() => { + renderer.renderMode = ParticleRenderMode.HorizontalBillboard; + }).to.throw("Not implemented"); + expect(() => { + renderer.renderMode = ParticleRenderMode.Mesh; + }).to.throw("Not implemented"); + expect(() => { + renderer.renderMode = ParticleRenderMode.VerticalBillboard; + }).to.throw("Not implemented"); + }); + + it("refCount", () => { + const renderer1 = scene.createRootEntity("Renderer").addComponent(ParticleRenderer); + const mesh = new ModelMesh(engine, "mesh"); + renderer1.mesh = mesh; + expect(mesh.refCount).to.eq(1); + renderer1.mesh = null; + expect(mesh.refCount).to.eq(0); + renderer1.mesh = mesh; + expect(mesh.refCount).to.eq(1); + renderer1.mesh = undefined; + expect(mesh.refCount).to.eq(0); + renderer1.mesh = mesh; + expect(mesh.refCount).to.eq(1); + renderer1.destroy(); + expect(mesh.refCount).to.eq(0); + + const entity2 = scene.createRootEntity("entity2"); + entity2.addComponent(ParticleRenderer).mesh = mesh; + entity2.destroy(); + expect(mesh.refCount).to.eq(0); + + const renderer2 = scene.createRootEntity("Renderer").addComponent(ParticleRenderer); + renderer2.mesh = mesh; + + mesh.destroy(); + expect(mesh.refCount).to.eq(1); + + mesh.destroy(true); + expect(mesh.refCount).to.eq(0); + }); +}); diff --git a/tests/src/math/Color.test.ts b/tests/src/math/Color.test.ts index 56afaa6d3e..0836ea5070 100644 --- a/tests/src/math/Color.test.ts +++ b/tests/src/math/Color.test.ts @@ -58,6 +58,14 @@ describe("Color test", () => { expect(Color.equals(a, out)).to.eq(true); }); + it("copyTo", () => { + const a = new Color(1, 0, 0, 1); + const out = new Color(); + + a.copyTo(out); + expect(Color.equals(a, out)).to.eq(true); + }); + it("copyFromArray", () => { const a = new Color(); const b = new Color(0, 0, 1, 1); diff --git a/tests/src/math/Quaternion.test.ts b/tests/src/math/Quaternion.test.ts index 19acf82fd8..9655517275 100644 --- a/tests/src/math/Quaternion.test.ts +++ b/tests/src/math/Quaternion.test.ts @@ -261,6 +261,13 @@ describe("Quaternion test", () => { expect(toString(a)).to.eq(toString(out)); }); + it("copyTo", () => { + const a = new Quaternion(3, 4, 5, 0); + const out = new Quaternion(); + a.copyTo(out); + expect(toString(a)).to.eq(toString(out)); + }); + it("conjugate", () => { const a = new Quaternion(1, 1, 1, 1); expect(toString(a.conjugate())).to.eq("quat(-1, -1, -1, 1)"); diff --git a/tests/src/math/Vector2.test.ts b/tests/src/math/Vector2.test.ts index 98c8920e05..48307a785b 100644 --- a/tests/src/math/Vector2.test.ts +++ b/tests/src/math/Vector2.test.ts @@ -148,6 +148,13 @@ describe("Vector2 test", () => { expect(toString(a)).to.eq(toString(out)); }); + it("copyTo", () => { + const a = new Vector2(3, 4); + const out = new Vector2(); + a.copyTo(out); + expect(toString(a)).to.eq(toString(out)); + }); + it("add", () => { const a = new Vector2(3, 4); const ret = new Vector2(1, 2); diff --git a/tests/src/math/Vector3.test.ts b/tests/src/math/Vector3.test.ts index e5bcefbc52..eee7240811 100644 --- a/tests/src/math/Vector3.test.ts +++ b/tests/src/math/Vector3.test.ts @@ -176,6 +176,13 @@ describe("Vector3 test", () => { expect(toString(a)).to.eq(toString(out)); }); + it("copyTo", () => { + const a = new Vector3(3, 4, 5); + const out = new Vector3(); + a.copyTo(out); + expect(toString(a)).to.eq(toString(out)); + }); + it("add", () => { const a = new Vector3(3, 4, 5); const ret = new Vector3(1, 2, 4); diff --git a/tests/src/math/Vector4.test.ts b/tests/src/math/Vector4.test.ts index dc0c9a78c0..698d6c72b0 100644 --- a/tests/src/math/Vector4.test.ts +++ b/tests/src/math/Vector4.test.ts @@ -158,6 +158,13 @@ describe("Vector4 test", () => { expect(toString(a)).to.eq(toString(out)); }); + it("copyTo", () => { + const a = new Vector4(3, 4, 5, 0); + const out = new Vector4(); + a.copyTo(out); + expect(toString(a)).to.eq(toString(out)); + }); + it("add", () => { const a = new Vector4(3, 4, 5, 1); const ret = new Vector4(1, 2, 4, 1);