diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 000000000..9cbbc6146 --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,11 @@ +reviews: + auto_review: + enabled: true + drafts: true + base_branches: + - "main" + - "hotfix" + - "dev" + - "feat/*" +chat: + auto_reply: true \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 44a5dbc9f..d1ebd91f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,22 @@ - 主版本号:含有破坏性更新和新特性,不在发布周期内。 --- +## 1.1.0 + +`2023-12-11` + +- Feat: Added support for direct parsing and playback of pre-compositions, and unified math library. [#3cd9c82](https://github.com/galacean/effects-runtime/commit/3cd9c8265013407f4aa9b52fe0c838e7ffecb66d) +- Fix: Solve pre composition problem in 3D plugin. [#27](https://github.com/galacean/effects-runtime/pull/27) @liuxi150 +- Fix: Errors about visible and transform when setting. [#25](https://github.com/galacean/effects-runtime/pull/25) @RGCHN +- Fix: HitTest bug in pre-composition. [#9](https://github.com/galacean/effects-runtime/pull/9) @RGCHN +- Fix: Resolved dragging issue. [#8](https://github.com/galacean/effects-runtime/pull/8) @liuxi150 +- Fix: Add id and transform setting from pre-composition item. [#5](https://github.com/galacean/effects-runtime/pull/5) @RGCHN +- Chore: Auto tigger bot review for specific branches. [#23](https://github.com/galacean/effects-runtime/pull/23) @zheeeng +- Test: Fix plugin unit test. [#28](https://github.com/galacean/effects-runtime/pull/28) @liuxi150 +- Test: Fix unit and case test problems. [#26](https://github.com/galacean/effects-runtime/pull/26) @liuxi150 +- Build: Support CHANGELOG generation script. [#4](https://github.com/galacean/effects-runtime/pull/4) @yiiqii +- Build: Add vite legacy polyfill. [#29](https://github.com/galacean/effects-runtime/pull/29) @yiiqii + ## 1.0.1 `2023-12-04` diff --git a/package.json b/package.json index 8834f1c5a..2bfe51676 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "lint:fix": "eslint --fix --ext .ts .", "test": "pnpm --filter @galacean/effects-test test", "version": "node ./scripts/update-version", + "changelog": "git fetch origin && node ./scripts/print-changelog", "check:ts": "tsc -b ./tsconfig.check.json", "clean:nm": "rimraf '{packages,web-packages,plugin-packages}/**/node_modules' && rimraf 'node_modules'", "clean:dist": "rimraf '{packages,web-packages,plugin-packages}/**/dist'", @@ -50,6 +51,7 @@ "husky": "^7.0.4", "ip": "^1.1.8", "js-yaml": "^4.1.0", + "jsdom": "^22.1.0", "lint-staged": "^11.2.6", "pnpm": "^8.7.0", "rimraf": "^3.0.2", diff --git a/packages/effects-core/package.json b/packages/effects-core/package.json index 3aad38a38..212a4109a 100644 --- a/packages/effects-core/package.json +++ b/packages/effects-core/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/effects-core", - "version": "1.0.1", + "version": "1.1.0", "description": "Galacean Effects runtime core for the web", "module": "./dist/index.mjs", "main": "./dist/index.js", @@ -50,6 +50,7 @@ "registry": "https://registry.npmjs.org" }, "dependencies": { - "@galacean/effects-specification": "1.0.0" + "@galacean/effects-specification": "1.0.0", + "@galacean/effects-math": "1.0.0" } } diff --git a/packages/effects-core/src/camera.ts b/packages/effects-core/src/camera.ts index 281681bd0..437360df2 100644 --- a/packages/effects-core/src/camera.ts +++ b/packages/effects-core/src/camera.ts @@ -1,22 +1,8 @@ -import type { vec3 } from '@galacean/effects-specification'; import * as spec from '@galacean/effects-specification'; -import { copy } from 'typedoc/dist/lib/utils'; -import type { mat4 } from './math'; -import { - mat4create, - mat4fromRotationTranslationScale, - mat4invert, - mat4multiply, - mat4perspective, - quatFromRotation, vec3MulMat4, -} from './math'; -import { deepClone } from './utils'; +import { Matrix4, Vector3, Euler, Quaternion, DEG2RAD } from '@galacean/effects-math/es/core/index'; import { Transform } from './transform'; -/** - * - */ -export interface CameraOptions { +interface CameraOptionsBase { /** * 相机近平面 */ @@ -37,6 +23,12 @@ export interface CameraOptions { * 相机的裁剪模式 */ clipMode: spec.CameraClipMode, +} + +/** + * + */ +export interface CameraOptions extends CameraOptionsBase { /** * 相机的位置 */ @@ -51,19 +43,34 @@ export interface CameraOptions { quat?: spec.vec4, } -const tmpScale: spec.vec3 = [1, 1, 1]; +export interface CameraOptionsEx extends CameraOptionsBase { + /** + * 相机的位置 + */ + position: Vector3, + /** + * 相机的旋转,欧拉角 + */ + rotation: Euler, + /** + * 相机的旋转,四元数 + */ + quat?: Quaternion, +} + +const tmpScale = new Vector3(1, 1, 1); /** * 合成的相机对象,采用透视投影 */ export class Camera { - private options: CameraOptions; - private viewMatrix: mat4 = mat4create(); - private projectionMatrix: mat4 = mat4create(); - private viewProjectionMatrix: mat4 = mat4create(); - private inverseViewMatrix: mat4 = mat4create(); - private inverseProjectionMatrix: mat4 | null; - private inverseViewProjectionMatrix: mat4 | null; + private options: CameraOptionsEx; + private viewMatrix = Matrix4.fromIdentity(); + private projectionMatrix = Matrix4.fromIdentity(); + private viewProjectionMatrix = Matrix4.fromIdentity(); + private inverseViewMatrix = Matrix4.fromIdentity(); + private inverseProjectionMatrix: Matrix4 | null; + private inverseViewProjectionMatrix: Matrix4 | null; private dirty = true; /** @@ -71,7 +78,10 @@ export class Camera { * @param name - 相机名称 * @param options */ - constructor (public name: string, options: Partial = {}) { + constructor ( + public name: string, + options: Partial = {}, + ) { const { near = 0.1, far = 20, @@ -82,7 +92,11 @@ export class Camera { rotation = [0, 0, 0], } = options; - this.options = { near, far, fov, aspect, clipMode, position, rotation }; + this.options = { + near, far, fov, aspect, clipMode, + position: Vector3.fromArray(position), + rotation: Euler.fromArray(rotation), + }; this.dirty = true; this.updateMatrix(); } @@ -162,95 +176,92 @@ export class Camera { * 设置相机的位置 * @param value */ - set position (value: spec.vec3) { - value.map((val, index) => { - if (this.options.position[index] !== val) { - this.options.position[index] = val; - this.dirty = true; - } - }); + set position (value: Vector3) { + if (!this.options.position.equals(value)) { + this.options.position.copyFrom(value); + this.dirty = true; + } } get position () { - return [...this.options.position]; + return this.options.position.clone(); } /** * 设置相机的旋转角度 * @param value */ - set rotation (value: spec.vec3) { - value.map((val, index) => { - if (this.options.rotation[index] !== val) { - this.options.rotation[index] = val; - this.dirty = true; - this.options.quat = undefined; - } - }); + set rotation (value: Euler) { + if (!this.options.rotation.equals(value)) { + this.options.rotation.copyFrom(value); + this.dirty = true; + this.options.quat = undefined; + } } get rotation () { - return [...this.options.rotation]; + return this.options.rotation.clone(); } /** * 获取相机的视图变换矩阵 * @return */ - getViewMatrix (): mat4 { + getViewMatrix (): Matrix4 { this.updateMatrix(); - return deepClone(this.viewMatrix); + return this.viewMatrix.clone(); } /** * 获取视图变换的逆矩阵 */ - getInverseViewMatrix () { + getInverseViewMatrix (): Matrix4 { this.updateMatrix(); - return deepClone(this.inverseViewMatrix); + return this.inverseViewMatrix.clone(); } /** * 获取相机的投影矩阵 * @return */ - getProjectionMatrix (): mat4 { + getProjectionMatrix (): Matrix4 { this.updateMatrix(); - return deepClone(this.projectionMatrix); + return this.projectionMatrix.clone(); } /** * 获取相机投影矩阵的逆矩阵 * @return */ - getInverseProjectionMatrix (): mat4 { + getInverseProjectionMatrix (): Matrix4 { this.updateMatrix(); - return deepClone(this.inverseProjectionMatrix); + return this.inverseProjectionMatrix?.clone() as Matrix4; } /** * 获取相机的 VP 矩阵 * @return */ - getViewProjectionMatrix (): mat4 { + getViewProjectionMatrix (): Matrix4 { this.updateMatrix(); - return deepClone(this.viewProjectionMatrix); + return this.viewProjectionMatrix.clone(); } /** * 获取相机 VP 矩阵的逆矩阵 * @return */ - getInverseViewProjectionMatrix (): mat4 { + getInverseViewProjectionMatrix (): Matrix4 { this.updateMatrix(); if (!this.inverseViewProjectionMatrix) { - this.inverseViewProjectionMatrix = mat4invert([], this.viewProjectionMatrix); + this.inverseViewProjectionMatrix = this.viewProjectionMatrix.clone(); + this.inverseViewProjectionMatrix.invert(); } - return deepClone(this.inverseViewProjectionMatrix); + return this.inverseViewProjectionMatrix.clone(); } /** @@ -258,40 +269,37 @@ export class Camera { * @param out - 结果矩阵 * @param model - 模型变换矩阵 */ - getModelViewProjection (out: mat4 | number[], model: mat4) { - return mat4multiply(out, this.viewProjectionMatrix, model); + getModelViewProjection (out: Matrix4, model: Matrix4) { + return out.multiplyMatrices(this.viewProjectionMatrix, model); } /** * 获取归一化坐标和 3D 世界坐标的换算比例 * @param z - 当前的位置 z */ - getInverseVPRatio (z: number): vec3 { - const pos: vec3 = [0, 0, 0]; - const nz = vec3MulMat4(pos, [0, 0, z], this.getViewProjectionMatrix())[2]; - - vec3MulMat4(pos, [1, 1, nz], this.getInverseViewProjectionMatrix()); + getInverseVPRatio (z: number) { + const pos = new Vector3(0, 0, z); + const mat = this.getViewProjectionMatrix(); + const { z: nz } = pos.applyMatrix(mat); - return pos; + return new Vector3(1, 1, nz).applyMatrix(mat); } /** * 设置相机的旋转四元数 * @param value - 旋转四元数 */ - setQuat (value: spec.vec4) { + setQuat (value: Quaternion) { if (this.options.quat === undefined) { - this.options.quat = value; + this.options.quat = value.clone(); this.dirty = true; } else { const quat = this.options.quat; - value.map((val, index) => { - if (quat[index] !== val) { - quat[index] = val; - this.dirty = true; - } - }); + if (!this.options.quat.equals(value)) { + this.options.quat.copyFrom(value); + this.dirty = true; + } } if (this.dirty) { this.setRotationByQuat(value); @@ -302,16 +310,17 @@ export class Camera { * 获取相机旋转对应的四元数 * @returns */ - getQuat (): spec.vec4 { + getQuat (): Quaternion { let quat = this.options.quat; if (quat === undefined) { - quat = [0, 0, 0, 1]; + quat = new Quaternion(); const { rotation } = this.options; if (rotation) { - quatFromRotation(quat, rotation[0], rotation[1], rotation[2]); + quat.setFromEuler(rotation); } + this.options.quat = quat; } @@ -322,7 +331,7 @@ export class Camera { * 获取相机内部的 options * @returns 相机 options */ - getOptions (): CameraOptions { + getOptions (): CameraOptionsEx { return this.options; } @@ -358,17 +367,20 @@ export class Camera { if (this.dirty) { const { fov, aspect, near, far, clipMode, position } = this.options; - mat4perspective(this.projectionMatrix, fov, aspect, near, far, clipMode === spec.CameraClipMode.portrait); - mat4fromRotationTranslationScale(this.inverseViewMatrix, this.getQuat(), position, tmpScale); - mat4invert(this.viewMatrix, this.inverseViewMatrix); - mat4multiply(this.viewProjectionMatrix, this.projectionMatrix, this.viewMatrix); + this.projectionMatrix.perspective( + fov * DEG2RAD, aspect, near, far, + clipMode === spec.CameraClipMode.portrait + ); + this.inverseViewMatrix.compose(position, this.getQuat(), tmpScale); + this.viewMatrix.copyFrom(this.inverseViewMatrix).invert(); + this.viewProjectionMatrix.multiplyMatrices(this.projectionMatrix, this.viewMatrix); this.inverseViewProjectionMatrix = null; this.dirty = false; } } - private setRotationByQuat (quat: spec.vec4) { - this.options.rotation = Transform.getRotation([] as unknown as spec.vec3, quat); + private setRotationByQuat (quat: Quaternion) { + Transform.getRotation(quat, this.options.rotation); } } diff --git a/packages/effects-core/src/comp-vfx-item.ts b/packages/effects-core/src/comp-vfx-item.ts index d49cb4244..243984539 100644 --- a/packages/effects-core/src/comp-vfx-item.ts +++ b/packages/effects-core/src/comp-vfx-item.ts @@ -1,10 +1,12 @@ +import { Vector2, Vector3 } from '@galacean/effects-math/es/core/index'; +import type { Ray } from '@galacean/effects-math/es'; import * as spec from '@galacean/effects-specification'; -import { VFX_ITEM_TYPE_TREE } from './constants'; -import type { CameraController } from './plugins'; -import { addItem } from './utils'; +import { CalculateItem, HitTestType } from './plugins'; +import type { CameraVFXItem, Region } from './plugins'; +import { addItem, noop } from './utils'; import type { VFXItemContent, VFXItemProps } from './vfx-item'; -import { createVFXItem, VFXItem } from './vfx-item'; -import type { Composition } from './composition'; +import { createVFXItem, Item, VFXItem } from './vfx-item'; +import type { Composition, CompositionHitTestOptions } from './composition'; export interface ItemNode { id: string, // item 的 id @@ -13,7 +15,7 @@ export interface ItemNode { parentId?: string, // 当前时刻作用在 vfxItem 的 transform 上的 parentTransform 对应元素的 id,会随父元素的销毁等生命周期变换,与 vfxItem.parenId 可能不同 } -export class CompVFXItem extends VFXItem { +export class CompVFXItem extends VFXItem { /** * 创建好的元素数组 */ @@ -23,6 +25,8 @@ export class CompVFXItem extends VFXItem { */ itemTree: ItemNode[] = []; startTime: number; + // k帧数据 + contentProps: any; override timeInms: number; /** * id和元素的映射关系Map,方便查找 @@ -35,16 +39,20 @@ export class CompVFXItem extends VFXItem { private itemsToRemove: VFXItem[] = []; private tempQueue: VFXItem[] = []; // 3D 模式下创建的场景相机 需要最后更新参数 - private extraCamera: VFXItem; + private extraCamera: CameraVFXItem; + // 预合成的原始合成id + private refId: string | undefined; override get type (): spec.ItemType { return spec.ItemType.composition; } override onConstructed (props: VFXItemProps) { - const { items = [], startTime = 0 } = props; + const { items = [], startTime = 0, content, refId } = props; + this.refId = refId; this.itemProps = items; + this.contentProps = content; const endBehavior = this.endBehavior; if ( @@ -65,16 +73,58 @@ export class CompVFXItem extends VFXItem { */ if (!this.items.length && this.composition) { for (let i = 0; i < this.itemProps.length; i++) { - const item = createVFXItem(this.itemProps[i], this.composition); + let item: VFXItem; + const itemProps = this.itemProps[i]; - if (item.id === 'extra-camera' && item.name === 'extra-camera') { + // 设置预合成作为元素时的时长、结束行为和渲染延时 + if (Item.isComposition(itemProps)) { + const refId = itemProps.content.options.refId; + const props = this.composition.refCompositionProps.get(refId); + + if (!props) { + throw new Error(`引用的Id: ${refId} 的预合成不存在`); + } + props.content = itemProps.content; + item = new CompVFXItem({ + ...props, + refId, + id: itemProps.id, + name: itemProps.name, + duration: itemProps.duration, + endBehavior: itemProps.endBehavior, + parentId: itemProps.parentId, + transform: itemProps.transform, + }, this.composition); + (item as CompVFXItem).contentProps = itemProps.content; + item.transform.parentTransform = this.transform; + this.composition.refContent.push(item as CompVFXItem); + if (item.endBehavior === spec.END_BEHAVIOR_RESTART) { + this.composition.autoRefTex = false; + } + } else { + item = createVFXItem(this.itemProps[i], this.composition); + item.transform.parentTransform = this.transform; + } + + if (VFXItem.isExtraCamera(item)) { this.extraCamera = item; } this.items.push(item); this.tempQueue.push(item); } } + // TODO: 处理k帧数据, ECS后改成 TimelineComponent + if (!this.content && this.contentProps) { + this._content = this.doCreateContent(); + } + } + + protected override doCreateContent (): CalculateItem { + const content: CalculateItem = new CalculateItem(this.contentProps, this); + content.renderData = content.getRenderData(0, true); + + return content; } override onLifetimeBegin () { @@ -92,7 +142,10 @@ export class CompVFXItem extends VFXItem { } override onItemUpdate (dt: number, lifetime: number) { - + if (this.content) { + this.content.updateTime(this.time); + this.content.getRenderData(this.content.time); + } if (!this.items) { return; } @@ -142,7 +195,17 @@ export class CompVFXItem extends VFXItem { const itemNode = this.itemTree[i]; if (itemNode && itemNode.item) { - itemNode.item.onUpdate(dt); + const item = itemNode.item; + + if ( + VFXItem.isComposition(item) && + item.ended && + item.endBehavior === spec.END_BEHAVIOR_RESTART + ) { + item.restart(); + } else { + item.onUpdate(dt); + } queue.push(...itemNode.children); } } @@ -160,7 +223,7 @@ export class CompVFXItem extends VFXItem { } } - this.extraCamera && this.extraCamera.onUpdate(dt); + this.extraCamera?.onUpdate(dt); } override onItemRemoved (composition: Composition) { @@ -206,9 +269,9 @@ export class CompVFXItem extends VFXItem { if (itemIndex > -1) { addItem(this.itemsToRemove, item); - if (item.type === VFX_ITEM_TYPE_TREE || item.type === spec.ItemType.null) { + if (VFXItem.isTree(item) || VFXItem.isNull(item)) { const willRemove = item.endBehavior === spec.END_BEHAVIOR_DESTROY_CHILDREN; - const keepParent = item.type === spec.ItemType.null && !!this.itemCacheMap.get(item.id); + const keepParent = VFXItem.isNull(item) && !!this.itemCacheMap.get(item.id); const children = this.itemCacheMap.get(item.id)?.children || []; children.forEach(cit => { @@ -222,6 +285,18 @@ export class CompVFXItem extends VFXItem { return true; } + this.items.forEach(it => { + if (VFXItem.isComposition(it)) { + const itemIndex = it.items.indexOf(item); + + if (itemIndex > -1) { + it.removeItem(item); + + return true; + } + } + }); + return false; } @@ -285,6 +360,98 @@ export class CompVFXItem extends VFXItem { } + getItemByName (name: string) { + const res: VFXItem[] = []; + + for (const item of this.items) { + if (item.name === name) { + res.push(item); + } else if (VFXItem.isComposition(item)) { + res.push(...item.getItemByName(name)); + } + } + + return res; + } + + hitTest (ray: Ray, x: number, y: number, regions: Region[], force?: boolean, options?: CompositionHitTestOptions): Region[] { + const hitPositions: Vector3[] = []; + const stop = options?.stop || noop; + const skip = options?.skip || noop; + const maxCount = options?.maxCount || this.items.length; + + for (let i = 0; i < this.items.length && regions.length < maxCount; i++) { + const item = this.items[i]; + + if (item.lifetime >= 0 && item.lifetime <= 1 && !VFXItem.isComposition(item) && !skip(item)) { + const hitParams = item.getHitTestParams(force); + + if (hitParams) { + let success = false; + const intersectPoint = new Vector3(); + + if (hitParams.type === HitTestType.triangle) { + const { triangles, backfaceCulling } = hitParams; + + for (let j = 0; j < triangles.length; j++) { + const triangle = triangles[j]; + + if (ray.intersectTriangle(triangle, intersectPoint, backfaceCulling)) { + success = true; + hitPositions.push(intersectPoint); + + break; + } + } + } else if (hitParams.type === HitTestType.box) { + const { center, size } = hitParams; + const boxMin = center.clone().addScaledVector(size, 0.5); + const boxMax = center.clone().addScaledVector(size, -0.5); + + if (ray.intersectBox({ min: boxMin, max: boxMax }, intersectPoint)) { + success = true; + hitPositions.push(intersectPoint); + } + } else if (hitParams.type === HitTestType.sphere) { + const { center, radius } = hitParams; + + if (ray.intersectSphere({ center, radius }, intersectPoint)) { + success = true; + hitPositions.push(intersectPoint); + } + } else if (hitParams.type === HitTestType.custom) { + const tempPosition = hitParams.collect(ray, new Vector2(x, y)); + + if (tempPosition && tempPosition.length > 0) { + tempPosition.forEach(pos => { + hitPositions.push(pos); + }); + success = true; + } + } + if (success) { + const region = { + compContent: this, + id: item.id, + name: item.name, + position: hitPositions[hitPositions.length - 1], + parentId: item.parentId, + hitPositions, + behavior: hitParams.behavior, + }; + + regions.push(region); + if (stop(region)) { + return regions; + } + } + } + } + } + + return regions; + } + protected override isEnded (now: number) { return now >= this.durInms; } @@ -297,10 +464,8 @@ export class CompVFXItem extends VFXItem { this.itemTree = []; const itemMap = this.itemCacheMap; const queue = this.tempQueue; - let countTime = 0; while (queue.length) { - countTime++; const item = queue.shift() as VFXItem; if (item.parentId === undefined) { @@ -345,4 +510,10 @@ export class CompVFXItem extends VFXItem { return idx > -1 ? id.substring(0, idx) : id; } + + private restart () { + this.reset(); + this.createContent(); + this.start(); + } } diff --git a/packages/effects-core/src/composition-source-manager.ts b/packages/effects-core/src/composition-source-manager.ts index a42de79d9..d9096e805 100644 --- a/packages/effects-core/src/composition-source-manager.ts +++ b/packages/effects-core/src/composition-source-manager.ts @@ -10,6 +10,7 @@ import type { Scene } from './scene'; import type { PluginSystem } from './plugin-system'; import type { Engine } from './engine'; import type { GlobalVolume } from './render'; +import type { VFXItemProps } from './vfx-item'; let listOrder = 0; @@ -29,7 +30,9 @@ export interface ContentOptions { */ export class CompositionSourceManager implements Disposable { composition?: spec.Composition; + refCompositions: Map = new Map(); sourceContent?: ContentOptions; + refCompositionProps: Map = new Map(); renderLevel?: spec.RenderLevel; pluginSystem?: PluginSystem; totalTime: number; @@ -43,7 +46,7 @@ export class CompositionSourceManager implements Disposable { scene: Scene, engine: Engine, ) { - // // 资源 + // 资源 const { jsonScene, renderLevel, textureOptions, pluginSystem, totalTime } = scene; const { compositions, imgUsage, compositionId } = jsonScene; @@ -55,15 +58,18 @@ export class CompositionSourceManager implements Disposable { // 缓存创建的Texture对象 // @ts-expect-error scene.textureOptions = cachedTextures; - const composition = compositions.find(({ id }) => id === compositionId); - cachedTextures?.forEach(tex => tex?.initialize()); + for (const comp of compositions) { + if (comp.id === compositionId) { + this.composition = comp; + } else { + this.refCompositions.set(comp.id, comp); + } + } - if (!composition) { + if (!this.composition) { throw new Error('Invalid composition id: ' + compositionId); } - listOrder = 0; - this.composition = composition; this.jsonScene = jsonScene; this.renderLevel = renderLevel; this.pluginSystem = pluginSystem; @@ -71,16 +77,16 @@ export class CompositionSourceManager implements Disposable { this.imgUsage = imgUsage ?? {}; this.textures = cachedTextures; this.mask = 0; + listOrder = 0; this.textureOptions = textureOptions; - this.sourceContent = this.getContent(); - + this.sourceContent = this.getContent(this.composition); } - private getContent (): ContentOptions { + private getContent (composition: spec.Composition): ContentOptions { // TODO: specification 中补充 globalVolume 类型 // @ts-expect-error - const { id, duration, name, endBehavior, camera, globalVolume, startTime = 0 } = this.composition as spec.Composition; - const items = this.assembleItems(); + const { id, duration, name, endBehavior, camera, globalVolume, startTime = 0 } = composition; + const items = this.assembleItems(composition); return { id, @@ -95,7 +101,7 @@ export class CompositionSourceManager implements Disposable { }; } - private assembleItems () { + private assembleItems (composition: spec.Composition) { const items: any[] = []; let mask = this.mask; @@ -103,8 +109,8 @@ export class CompositionSourceManager implements Disposable { mask = 0; } - this.composition?.items.forEach(item => { - const opt: Record = {}; + composition.items.forEach(item => { + const option: Record = {}; const { visible, renderLevel: itemRenderLevel, type } = item; if (visible === false) { @@ -114,12 +120,12 @@ export class CompositionSourceManager implements Disposable { const content = { ...item.content }; if (content) { - opt.content = { ...content }; + option.content = { ...content }; if (passRenderLevel(itemRenderLevel, this.renderLevel)) { - const renderContent = opt.content; + const renderContent = option.content; - opt.type = type; + option.type = type; if (renderContent.renderer) { renderContent.renderer = this.changeTex(renderContent.renderer); @@ -143,7 +149,7 @@ export class CompositionSourceManager implements Disposable { renderContent.renderer.shape = getGeometryByShape(renderContent.renderer.shape, split); } } else { - opt.content.renderer = { order: 0 }; + option.content.renderer = { order: 0 }; } if (renderContent.trails) { renderContent.trails = this.changeTex(renderContent.trails); @@ -158,26 +164,44 @@ export class CompositionSourceManager implements Disposable { const { refCount } = item; const { plugins = [] } = this.jsonScene as spec.JSONScene; - opt.name = name; - opt.delay = delay; - opt.id = id; + option.name = name; + option.delay = delay; + option.id = id; if (parentId) { - opt.parentId = parentId; + option.parentId = parentId; } - opt.refCount = refCount; - opt.duration = duration; - opt.listIndex = listOrder++; - opt.endBehavior = endBehavior; + option.refCount = refCount; + option.duration = duration; + option.listIndex = listOrder++; + option.endBehavior = endBehavior; if (pluginName) { - opt.pluginName = pluginName; + option.pluginName = pluginName; } else if (pn !== undefined && Number.isInteger(pn)) { - opt.pluginName = plugins[pn]; + option.pluginName = plugins[pn]; } if (transform) { - opt.transform = transform; + option.transform = transform; } - items.push(opt); + // 处理预合成的渲染顺序 + if (option.type === spec.ItemType.composition) { + const refId = (item.content as spec.CompositionContent).options.refId; + + if (!this.refCompositions.get(refId)) { + throw new Error('Invalid Ref Composition id: ' + refId); + } + if (!this.refCompositionProps.has(refId)) { + this.refCompositionProps.set(refId, this.getContent(this.refCompositions.get(refId)!) as unknown as VFXItemProps); + } + const ref = this.refCompositionProps.get(refId)!; + + ref.items.forEach((item: Record) => { + item.listIndex = listOrder++; + }); + option.items = ref.items; + + } + items.push(option); } } }); @@ -224,5 +248,7 @@ export class CompositionSourceManager implements Disposable { this.totalTime = 0; this.pluginSystem = undefined; this.sourceContent = undefined; + this.refCompositions.clear(); + this.refCompositionProps.clear(); } } diff --git a/packages/effects-core/src/composition.ts b/packages/effects-core/src/composition.ts index 8cb903cf6..a5dbca154 100644 --- a/packages/effects-core/src/composition.ts +++ b/packages/effects-core/src/composition.ts @@ -1,4 +1,5 @@ import * as spec from '@galacean/effects-specification'; +import type { Ray } from '@galacean/effects-math/es/core/index'; import { LOG_TYPE } from './config'; import type { JSONValue } from './downloader'; import type { Scene } from './scene'; @@ -15,8 +16,7 @@ import type { Texture } from './texture'; import { TextureSourceType } from './texture'; import { RenderFrame } from './render'; import { Camera } from './camera'; -import { setRayFromCamera, intersectRayTriangle, intersectRayBox, intersectRaySphere, quatFromRotation } from './math'; -import { HitTestType } from './plugins'; +import { setRayFromCamera } from './math'; import type { Region } from './plugins'; import { CompositionSourceManager } from './composition-source-manager'; @@ -36,7 +36,7 @@ export interface MessageItem { /** * */ -interface CompositionHitTestOptions { +export interface CompositionHitTestOptions { maxCount?: number, stop?: (region: Region) => boolean, skip?: (item: VFXItem) => boolean, @@ -126,6 +126,10 @@ export class Composition implements Disposable, LostHandler { * 插件系统,保存当前加载的插件对象,负责插件事件和创建插件的 Item 对象 */ readonly pluginSystem: PluginSystem; + /** + * 是否在合成结束时自动销毁引用的纹理,合成重播时不销毁 + */ + autoRefTex: boolean; /** * 当前合成名称 */ @@ -150,6 +154,14 @@ export class Composition implements Disposable, LostHandler { * 合成对象 */ readonly content: CompVFXItem; + /** + * 预合成数组 + */ + readonly refContent: CompVFXItem[] = []; + /** + * 预合成的合成属性,在 content 中会被其元素属性覆盖 + */ + refCompositionProps: Map = new Map(); /** * 合成的相机对象 */ @@ -179,7 +191,6 @@ export class Composition implements Disposable, LostHandler { // private readonly event: EventSystem; // texInfo的类型有点不明确,改成不会提前删除texture private readonly texInfo: Record; - private readonly autoRefTex: boolean; private readonly postLoaders: Plugin[] = []; private readonly handleMessageItem?: (item: MessageItem) => void; @@ -205,10 +216,11 @@ export class Composition implements Disposable, LostHandler { scene.textures = undefined; scene.consumed = true; } - const { sourceContent, pluginSystem, imgUsage, totalTime, renderLevel } = this.compositionSourceManager; + const { sourceContent, pluginSystem, imgUsage, totalTime, renderLevel, refCompositionProps } = this.compositionSourceManager; assertExist(sourceContent); + this.refCompositionProps = refCompositionProps; const vfxItem = new CompVFXItem(sourceContent as unknown as VFXItemProps, this); const imageUsage = (!reusable && imgUsage) as unknown as Record; @@ -531,10 +543,8 @@ export class Composition implements Disposable, LostHandler { * @returns 元素对象 */ getItemByName (name: string, type?: spec.ItemType) { - const items = this.content && this.content.items; - - if (items) { - return items.find(item => item.name === name && (!type || type === item.type)); + if (this.content && this.content.items) { + return this.content.getItemByName(name)[0]; } } @@ -578,13 +588,13 @@ export class Composition implements Disposable, LostHandler { } /** - * + * 获取指定位置和相机连成的射线 * @param x * @param y * @returns */ - getHitTestRay (x: number, y: number): { center: spec.vec3, direction: spec.vec3 } { - const [a, b, c, d] = this.renderFrame.editorTransform; + getHitTestRay (x: number, y: number): Ray { + const { x: a, y: b, z: c, w: d } = this.renderFrame.editorTransform; return setRayFromCamera((x - c) / a, (y - d) / b, this.camera); } @@ -608,80 +618,13 @@ export class Composition implements Disposable, LostHandler { if (this.isDestroyed) { return []; } - const hitPositions: spec.vec3[] = []; const regions: Region[] = []; - const [a, b, c, d] = this.renderFrame.editorTransform; - const ray = setRayFromCamera((x - c) / a, (y - d) / b, this.camera); - const stop = options?.stop || noop; - const skip = options?.skip || noop; - const maxCount = options?.maxCount || this.items.length; - - for (let i = 0; i < this.items.length && regions.length < maxCount; i++) { - const item = this.items[i]; - - if (item.lifetime >= 0 && item.lifetime <= 1 && !skip(item)) { - const hitParams = item.getHitTestParams(force); + const ray = this.getHitTestRay(x, y); - if (hitParams) { - let success = false; - const intersectPoint: spec.vec3 | number[] = []; - - if (hitParams.type === HitTestType.triangle) { - const { triangles, backfaceCulling } = hitParams; - - for (let j = 0; j < triangles.length; j++) { - const triangle = triangles[j]; - - intersectRayTriangle(intersectPoint, ray.center, ray.direction, triangle, backfaceCulling); - if (intersectPoint.length) { - success = true; - hitPositions.push(intersectPoint as spec.vec3); - - break; - } - } - } else if (hitParams.type === HitTestType.box) { - const { center, size } = hitParams; - - intersectRayBox(intersectPoint, ray.center, ray.direction, center, size); - if (intersectPoint.length) { - success = true; - hitPositions.push(intersectPoint as spec.vec3); - } - } else if (hitParams.type === HitTestType.sphere) { - const { center, radius } = hitParams; - - intersectRaySphere(intersectPoint, ray.center, ray.direction, center, radius); - if (intersectPoint.length) { - success = true; - hitPositions.push(intersectPoint as spec.vec3); - } - } else if (hitParams.type === HitTestType.custom) { - const tempPosition = hitParams.collect(ray, [x, y]); - - if (tempPosition && tempPosition.length > 0) { - hitPositions.push(...tempPosition); - success = true; - } - } - if (success) { - const region = { - id: item.id, - name: item.name, - position: hitPositions[hitPositions.length - 1], - parentId: item.parentId, - hitPositions, - behavior: hitParams.behavior, - }; - - regions.push(region); - if (stop(region)) { - return regions; - } - } - } - } - } + this.content.hitTest(ray, x, y, regions, force, options); + this.refContent.forEach(ref => { + ref.hitTest(ray, x, y, regions, force, options); + }); return regions; } @@ -768,7 +711,15 @@ export class Composition implements Disposable, LostHandler { * @param item - 需要销毁的 item */ destroyItem (item: VFXItem) { - if (this.content.removeItem(item)) { + // 预合成元素销毁时销毁其中的item + if (item.type == spec.ItemType.composition) { + if (item.endBehavior !== spec.END_BEHAVIOR_FREEZE) { + (item as CompVFXItem).items.forEach(it => this.pluginSystem.plugins.forEach(loader => loader.onCompositionItemRemoved(this, it))); + } + } else { + this.content.removeItem(item); + // 预合成中的元素移除 + this.refContent.forEach(content => content.removeItem(item)); this.pluginSystem.plugins.forEach(loader => loader.onCompositionItemRemoved(this, item)); } } @@ -827,6 +778,7 @@ export class Composition implements Disposable, LostHandler { textures.forEach(tex => tex.dispose = textureDisposes[tex.id]); } this.compositionSourceManager.dispose(); + this.refCompositionProps.clear(); } /** @@ -837,7 +789,7 @@ export class Composition implements Disposable, LostHandler { * @param dy - y偏移量 */ setEditorTransform (scale: number, dx: number, dy: number) { - this.renderFrame.editorTransform = [scale, scale, dx, dy]; + this.renderFrame.editorTransform.set(scale, scale, dx, dy); } /** @@ -853,24 +805,46 @@ export class Composition implements Disposable, LostHandler { } /** - * 设置合成在 3D 坐标轴上相对移动 + * 设置合成在 3D 坐标轴上相对当前的位移 */ translate (x: number, y: number, z: number) { this.content.translate(x, y, z); } + + /** + * 设置合成在 3D 坐标轴上相对原点的位移 + */ + setPosition (x: number, y: number, z: number) { + this.content.setPosition(x, y, z); + } + /** - * 设置合成在 3D 坐标轴上相对旋转(角度) + * 设置合成在 3D 坐标轴上相对当前的旋转(角度) */ rotate (x: number, y: number, z: number) { this.content.rotate(x, y, z); } + /** - * 设置合成在 3D 坐标轴上相对缩放 + * 设置合成在 3D 坐标轴上的相对原点的旋转(角度) + */ + setRotation (x: number, y: number, z: number) { + this.content.setRotation(x, y, z); + } + /** + * 设置合成在 3D 坐标轴上相对当前的缩放 */ scale (x: number, y: number, z: number) { this.content.scale(x, y, z); } + /** + * 设置合成在 3D 坐标轴上的缩放 + */ + setScale (x: number, y: number, z: number) { + this.content.setScale(x, y, z); + } + /** * 卸载贴图纹理方法,减少内存 */ diff --git a/packages/effects-core/src/constants.ts b/packages/effects-core/src/constants.ts index dadc7830a..a90450dec 100644 --- a/packages/effects-core/src/constants.ts +++ b/packages/effects-core/src/constants.ts @@ -2,9 +2,6 @@ import type * as spec from '@galacean/effects-specification'; export const SPRITE_VERTEX_STRIDE = 6; -// TODO: 待 spec 修改升级后移除 -export const VFX_ITEM_TYPE_TREE = 'tree' as spec.ItemType; - export const SEMANTIC_PRE_COLOR_ATTACHMENT_0 = 'PRE_COLOR_0'; export const SEMANTIC_PRE_COLOR_ATTACHMENT_SIZE_0 = 'PRE_COLOR_SIZE_0'; export const SEMANTIC_MAIN_PRE_COLOR_ATTACHMENT_0 = 'PRE_MAIN_COLOR_0'; @@ -14,8 +11,6 @@ export const PLAYER_OPTIONS_ENV_EDITOR = 'editor'; export const FILTER_NAME_NONE = 'none'; -export const DEG2RAD = 0.017453292519943295; - export const HELP_LINK = { 'Filter not imported': 'https://galacean.antgroup.com/effects/#/user/gasrv4ka5sacrwpg', 'Item duration can\'t be less than 0': 'https://galacean.antgroup.com/effects/#/user/gasrv4ka5sacrwpg', diff --git a/packages/effects-core/src/filters/bloom.ts b/packages/effects-core/src/filters/bloom.ts index b7d385932..8a4c781c2 100644 --- a/packages/effects-core/src/filters/bloom.ts +++ b/packages/effects-core/src/filters/bloom.ts @@ -1,5 +1,5 @@ import type * as spec from '@galacean/effects-specification'; -import type { vec2 } from '@galacean/effects-specification'; +import { Vector2 } from '@galacean/effects-math/es/core/index'; import type { Composition } from '../composition'; import type { FilterDefine, FilterShaderDefine } from '../filter'; import { glContext } from '../gl'; @@ -7,7 +7,7 @@ import type { ValueGetter } from '../math'; import { createValueGetter, nearestPowerOfTwo } from '../math'; import type { PluginSystem } from '../plugin-system'; import type { Renderer, RenderPassOptions } from '../render'; -import { getTextureSize, GPUCapability, RenderPass, RenderTargetHandle } from '../render'; +import { getTextureSize, RenderPass, RenderTargetHandle } from '../render'; import { bloomMixVert, bloomThresholdVert } from '../shader'; import type { Texture } from '../texture'; import { TextureLoadAction } from '../texture'; @@ -84,7 +84,7 @@ export function registerBloomFilter (filter: spec.FilterParams, composition: Com thresholdPass.fragShader = bloomThresholdVert; thresholdPass.preDefaultPassAttachment = preDefaultPassColorAttachment; - const gaussianHPass = new BloomGaussianPass(renderer, 'H', [gaussian.step, 0], composition.pluginSystem, gaussian.shader, { + const gaussianHPass = new BloomGaussianPass(renderer, 'H', new Vector2(gaussian.step, 0), composition.pluginSystem, gaussian.shader, { name: 'gaussianH', viewport, attachments: [{ texture: blurInterMedia }], @@ -94,7 +94,7 @@ export function registerBloomFilter (filter: spec.FilterParams, composition: Com }, blurTarget); gaussianHPass.preDefaultPassAttachment = preDefaultPassColorAttachment; - const gaussianVPass = new BloomGaussianPass(renderer, 'V', [0, gaussian.step], composition.pluginSystem, gaussian.shader, { + const gaussianVPass = new BloomGaussianPass(renderer, 'V', new Vector2(0, gaussian.step), composition.pluginSystem, gaussian.shader, { name: 'gaussianV', viewport, attachments: [{ texture: blurTarget }], @@ -172,7 +172,7 @@ class ThresholdPass extends RenderPass { } class BloomGaussianPass extends RenderPass { - uTexStep: vec2; + uTexStep: Vector2; uBlurSource: Texture; prePassTexture: Texture; preDefaultPassTexture: Texture; @@ -181,7 +181,7 @@ class BloomGaussianPass extends RenderPass { type: 'H' | 'V'; //高斯模糊的方向 可选'V'和'H' preDefaultPassAttachment: RenderTargetHandle; // 滤镜前的pass attachment - constructor (renderer: Renderer, type: 'H' | 'V', uTexStep: vec2, pluginSystem: PluginSystem, fragShader: string, + constructor (renderer: Renderer, type: 'H' | 'V', uTexStep: Vector2, pluginSystem: PluginSystem, fragShader: string, option: RenderPassOptions, uBlurSource?: Texture) { super(renderer, option); this.uTexStep = uTexStep; diff --git a/packages/effects-core/src/filters/camera-move.ts b/packages/effects-core/src/filters/camera-move.ts index 4a5cf12ac..71e927632 100644 --- a/packages/effects-core/src/filters/camera-move.ts +++ b/packages/effects-core/src/filters/camera-move.ts @@ -1,13 +1,13 @@ import * as spec from '@galacean/effects-specification'; +import { Vector3 } from '@galacean/effects-math/es/core/index'; import { Camera } from '../camera'; import type { Composition } from '../composition'; import type { FilterDefine, FilterShaderDefine } from '../filter'; import { glContext } from '../gl'; -import { createValueGetter, vecAdd } from '../math'; import { cameraMoveVert, copyFrag } from '../shader'; import { TextureLoadAction } from '../texture'; import { CopyPass } from './gaussian'; -import { GPUCapability } from '../render'; +import { createValueGetter } from '../math'; export function createCameraMoveShader (): FilterShaderDefine[] { return [ @@ -28,9 +28,8 @@ export function registerCameraMoveFilter (filter: spec.FilterParams, composition } const pos = createValueGetter(params); const camera = new Camera('camera_move'); - const cameraPos: spec.vec3 = [0, 0, 0]; + const cameraPos = new Vector3(); const renderer = composition.renderer; - const textureFilter = renderer.engine.gpuCapability.level === 2 ? glContext.LINEAR : glContext.NEAREST; const cameraPass = new CopyPass(renderer, { name: 'cameraCopyPass', @@ -55,10 +54,13 @@ export function registerCameraMoveFilter (filter: spec.FilterParams, composition camera.copy(composition.camera); const trans = pos.getValue(p); - vecAdd(cameraPos, composition.camera.position, [-trans[0], -trans[1], -trans[2]]); + cameraPos.addVectors( + composition.camera.position, + new Vector3(-trans[0], -trans[1], -trans[2]) + ); camera.position = cameraPos; - return camera.getViewProjectionMatrix(); + return camera.getViewProjectionMatrix().toArray() as spec.mat4; }, }, }, diff --git a/packages/effects-core/src/filters/gaussian.ts b/packages/effects-core/src/filters/gaussian.ts index 3b849cd5a..b7e2e9df7 100644 --- a/packages/effects-core/src/filters/gaussian.ts +++ b/packages/effects-core/src/filters/gaussian.ts @@ -1,4 +1,5 @@ import type * as spec from '@galacean/effects-specification'; +import { Vector2 } from '@galacean/effects-math/es/core/index'; import type { Composition } from '../composition'; import type { FilterDefine, FilterShaderDefine } from '../filter'; import { glContext } from '../gl'; @@ -15,7 +16,7 @@ import { cloneSpriteMesh } from './utils'; /****************************************************************************************/ class GaussianPass extends RenderPass { - uTexStep: spec.vec2; + uTexStep: Vector2; uBlurSource: Texture; prePassTexture: Texture; preDefaultPassTexture: Texture; @@ -26,7 +27,7 @@ class GaussianPass extends RenderPass { mframeBuffer: FrameBuffer; - constructor (renderer: Renderer, type: 'H' | 'V', uTexStep: spec.vec2, pluginSystem: PluginSystem, fragShader: string, option: RenderPassOptions) { + constructor (renderer: Renderer, type: 'H' | 'V', uTexStep: Vector2, pluginSystem: PluginSystem, fragShader: string, option: RenderPassOptions) { super(renderer, option); this.uTexStep = uTexStep; this.pluginSystem = pluginSystem; @@ -176,7 +177,7 @@ export function registerGaussianFilter (filter: spec.FilterParams, composition: // 使用一个attachment对象保存滤镜前的pass渲染结果,传递到后续滤镜pass使用 const preDefaultPassColorAttachment = new RenderTargetHandle(engine, {}); - const gaussianHPass = new GaussianPass(renderer, 'H', [0, step], composition.pluginSystem, shader, { + const gaussianHPass = new GaussianPass(renderer, 'H', new Vector2(0, step), composition.pluginSystem, shader, { name: 'gaussianH', viewport, attachments: [{ texture: gaussianTextureH }], @@ -186,7 +187,7 @@ export function registerGaussianFilter (filter: spec.FilterParams, composition: }); gaussianHPass.preDefaultPassAttachment = preDefaultPassColorAttachment; - const gaussianVPass = new GaussianPass(renderer, 'V', [step, 0], composition.pluginSystem, shader, { + const gaussianVPass = new GaussianPass(renderer, 'V', new Vector2(step, 0), composition.pluginSystem, shader, { name: 'gaussianV', viewport, attachments: [{ texture: gaussianTextureV }], diff --git a/packages/effects-core/src/index.ts b/packages/effects-core/src/index.ts index dd3c965e9..cdfdeb8cd 100644 --- a/packages/effects-core/src/index.ts +++ b/packages/effects-core/src/index.ts @@ -21,6 +21,7 @@ export { getStandardComposition, getStandardItem, } from '@galacean/effects-specification/dist/fallback'; +export * as math from '@galacean/effects-math/es/core/index'; export * from './gl'; export * from './constants'; export * from './config'; diff --git a/packages/effects-core/src/material/material.ts b/packages/effects-core/src/material/material.ts index 9fea905e4..4fd0159de 100644 --- a/packages/effects-core/src/material/material.ts +++ b/packages/effects-core/src/material/material.ts @@ -1,4 +1,4 @@ -import type { mat3, mat4, vec2, vec3, vec4 } from '@galacean/effects-specification'; +import type { Matrix3, Matrix4, Quaternion, Vector2, Vector3, Vector4 } from '@galacean/effects-math/es/core/index'; import type { GlobalUniforms, Renderer, ShaderWithSource } from '../render'; import type { Texture } from '../texture'; import type { DestroyOptions, Disposable } from '../utils'; @@ -269,37 +269,49 @@ export abstract class Material implements Disposable { * 获取 Material 的 vec2 类型的 uniform 数据 * @param name */ - abstract getVector2 (name: string): vec2 | null; + abstract getVector2 (name: string): Vector2 | null; /** * 设置 vec2 类型的 uniform 的数据 * @param name - uniform 名称 * @param value - 要设置的 uniform 数据 */ - abstract setVector2 (name: string, value: vec2): void; + abstract setVector2 (name: string, value: Vector2): void; /** * 获取 Material 的 vec3 类型的 uniform 数据 * @param name */ - abstract getVector3 (name: string): vec3 | null; + abstract getVector3 (name: string): Vector3 | null; /** * 设置 vec3 类型的 uniform 的数据 * @param name - uniform 名称 * @param value - 要设置的 uniform 数据 */ - abstract setVector3 (name: string, value: vec3): void; + abstract setVector3 (name: string, value: Vector3): void; /** * 获取 Material 的 vec4 类型的 uniform 数据 * @param name */ - abstract getVector4 (name: string): vec4 | null; + abstract getVector4 (name: string): Vector4 | null; /** * 设置 vec4 类型的 uniform 的数据 * @param name - uniform 名称 * @param value - 要设置的 uniform 数据 */ - abstract setVector4 (name: string, value: vec4): void; + abstract setVector4 (name: string, value: Vector4): void; + + /** + * 获取 Material 的 Quaternion 类型的 uniform 数据 + * @param name + */ + abstract getQuaternion (name: string): Quaternion | null; + /** + * 设置 Quaternion 类型的 uniform 的数据 + * @param name - uniform 名称 + * @param value - 要设置的 uniform 数据 + */ + abstract setQuaternion (name: string, value: Quaternion): void; /** * 获取 Material 的 vec4 数组类型的 uniform 数据 @@ -311,25 +323,25 @@ export abstract class Material implements Disposable { * @param name - uniform 名称 * @param value - 要设置的 uniform 数据 */ - abstract setVector4Array (name: string, value: vec4[]): void; + abstract setVector4Array (name: string, value: Vector4[]): void; /** * 获取 Material 的 mat4 类型的 uniform 数据 * @param name */ - abstract getMatrix (name: string): mat4 | null; + abstract getMatrix (name: string): Matrix4 | null; /** * 设置 mat4 类型的 uniform 的数据 * @param name - uniform 名称 * @param value - 要设置的 uniform 数据 */ - abstract setMatrix (name: string, value: mat4): void; + abstract setMatrix (name: string, value: Matrix4): void; /** * 设置 mat3 类型的 uniform 的数据 * @param name - uniform 名称 * @param value - 要设置的 uniform 数据 */ - abstract setMatrix3 (name: string, value: mat3): void; + abstract setMatrix3 (name: string, value: Matrix3): void; /** * 获取 Material 的 mat4 数组类型的 uniform 数据 @@ -341,7 +353,7 @@ export abstract class Material implements Disposable { * @param name - uniform 名称 * @param array - 要设置的 uniform 数据 */ - abstract setMatrixArray (name: string, array: mat4[]): void; + abstract setMatrixArray (name: string, array: Matrix4[]): void; /** * 设置 mat 数组类型的 uniform 的数据,传入 number 数组 * @param name - uniform 名称 diff --git a/packages/effects-core/src/material/types.ts b/packages/effects-core/src/material/types.ts index 6d0369c85..d3c63661f 100644 --- a/packages/effects-core/src/material/types.ts +++ b/packages/effects-core/src/material/types.ts @@ -1,4 +1,5 @@ import type * as spec from '@galacean/effects-specification'; +import type { Matrix3, Matrix4, Vector2, Vector3, Vector4 } from '@galacean/effects-math/es/core/index'; import type { Texture } from '../texture'; import type { DestroyOptions } from '../utils'; @@ -73,7 +74,7 @@ export interface MaterialDataBlockDestroyOptions { * }); * mtl2 use the same program with mtl0 */ -export type UniformValueDataType = spec.TypedArray | number | number[] | Texture | Texture[] | number[][]; +export type UniformValueDataType = spec.TypedArray | number | number[] | Texture | Texture[] | number[][] | Vector2 | Vector3 | Vector4 | Matrix3 | Matrix4; export type UniformStruct = Record; // 支持结构体Uniform数据 diff --git a/packages/effects-core/src/math/index.ts b/packages/effects-core/src/math/index.ts index 24af51706..8afd58095 100644 --- a/packages/effects-core/src/math/index.ts +++ b/packages/effects-core/src/math/index.ts @@ -1,9 +1,4 @@ -export * from './utils'; export * from './float16array-wrapper'; -export * from './matrix'; -export * from './quat'; -export * from './raycast'; export * from './translate'; -export * from './types'; +export * from './utils'; export * from './value-getter'; -export * from './vec'; diff --git a/packages/effects-core/src/math/matrix.ts b/packages/effects-core/src/math/matrix.ts deleted file mode 100644 index f2415497c..000000000 --- a/packages/effects-core/src/math/matrix.ts +++ /dev/null @@ -1,437 +0,0 @@ -import type { vec3, vec4 } from '@galacean/effects-specification'; -import type { mat3, mat4 } from './types'; - -const d2r = Math.PI / 180; - -// TODO 抽成class -export function mat3FromQuat (out: mat3, quat: vec4): mat3 { - const [x, y, z, w] = quat; - const x2 = x + x; - const y2 = y + y; - const z2 = z + z; - const xx = x * x2; - const yx = y * x2; - const yy = y * y2; - const zx = z * x2; - const zy = z * y2; - const zz = z * z2; - const wx = w * x2; - const wy = w * y2; - const wz = w * z2; - - out[0] = 1 - yy - zz; - out[3] = yx - wz; - out[6] = zx + wy; - out[1] = yx + wz; - out[4] = 1 - xx - zz; - out[7] = zy - wx; - out[2] = zx - wy; - out[5] = zy + wx; - out[8] = 1 - xx - yy; - - return out; -} - -export function mat4create (): mat4 { - return new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]) as unknown as mat4; - // return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; -} - -export function mat4ToIdentityMatrix (m: mat4) { - m[0] = 1.0; - m[1] = 0.0; - m[2] = 0.0; - m[3] = 0.0; - m[4] = 0.0; - m[5] = 1.0; - m[6] = 0.0; - m[7] = 0.0; - m[8] = 0.0; - m[9] = 0.0; - m[10] = 1.0; - m[11] = 0.0; - m[12] = 0.0; - m[13] = 0.0; - m[14] = 0.0; - m[15] = 1.0; -} - -export function isIdentityMatrix (m: mat4 | number[]): boolean { - return m[0] === 1.0 && - m[1] === 0.0 && - m[2] === 0.0 && - m[3] === 0.0 && - m[4] === 0.0 && - m[5] === 1.0 && - m[6] === 0.0 && - m[7] === 0.0 && - m[8] === 0.0 && - m[9] === 0.0 && - m[10] === 1.0 && - m[11] === 0.0 && - m[12] === 0.0 && - m[13] === 0.0 && - m[14] === 0.0 && - m[15] === 1.0; -} - -/** - * 根据位移、旋转、缩放和锚点计算模型矩阵,锚点影响旋转中心 - * @param out - 结果矩阵 - * @param q - 四元数表示的旋转量 - * @param v - 位移向量 - * @param s - 缩放向量 - * @param a - 锚点向量 - * @returns - */ -export function mat4fromRotationTranslationScale (out: mat4 | number[], q: vec4, v: vec3, s: vec3, a?: vec3) { - const l = a ? -a[0] : 0; - const m = a ? -a[1] : 0; - const n = a ? -a[2] : 0; - const x = q[0]; - const y = q[1]; - const z = q[2]; - const w = q[3]; - const x2 = x + x; - const y2 = y + y; - const z2 = z + z; - const xx = x * x2; - const xy = x * y2; - const xz = x * z2; - const yy = y * y2; - const yz = y * z2; - const zz = z * z2; - const wx = w * x2; - const wy = w * y2; - const wz = w * z2; - const sx = s[0]; - const sy = s[1]; - const sz = s[2]; - - const r0 = (1 - (yy + zz)) * sx; - const r1 = (xy + wz) * sx; - const r2 = (xz - wy) * sx; - const r4 = (xy - wz) * sy; - const r5 = (1 - (xx + zz)) * sy; - const r6 = (yz + wx) * sy; - const r8 = (xz + wy) * sz; - const r9 = (yz - wx) * sz; - const r10 = (1 - (xx + yy)) * sz; - - out[0] = r0; - out[1] = r1; - out[2] = r2; - out[3] = 0; - out[4] = r4; - out[5] = r5; - out[6] = r6; - out[7] = 0; - out[8] = r8; - out[9] = r9; - out[10] = r10; - out[11] = 0; - out[12] = l * r0 + m * r4 + n * r8 - l + v[0]; - out[13] = l * r1 + m * r5 + n * r9 - m + v[1]; - out[14] = l * r2 + m * r6 + n * r10 - n + v[2]; - out[15] = 1; - - return out; - -} - -export function mat4invert (out: mat4 | number[], a: mat4): mat4 { - if (isIdentityMatrix(a)) { - return mat4Clone(out, a); - } - const a00 = a[0]; - const a01 = a[1]; - const a02 = a[2]; - const a03 = a[3]; - const a10 = a[4]; - const a11 = a[5]; - const a12 = a[6]; - const a13 = a[7]; - const a20 = a[8]; - const a21 = a[9]; - const a22 = a[10]; - const a23 = a[11]; - const a30 = a[12]; - const a31 = a[13]; - const a32 = a[14]; - const a33 = a[15]; - const b00 = a00 * a11 - a01 * a10; - const b01 = a00 * a12 - a02 * a10; - const b02 = a00 * a13 - a03 * a10; - const b03 = a01 * a12 - a02 * a11; - const b04 = a01 * a13 - a03 * a11; - const b05 = a02 * a13 - a03 * a12; - const b06 = a20 * a31 - a21 * a30; - const b07 = a20 * a32 - a22 * a30; - const b08 = a20 * a33 - a23 * a30; - const b09 = a21 * a32 - a22 * a31; - const b10 = a21 * a33 - a23 * a31; - const b11 = a22 * a33 - a23 * a32; - - // Calculate the determinant - let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; - - if (!det) { - for (let i = 0; i < 16; i++) { - out[i] = NaN; - } - - return out as mat4; - } - - det = 1.0 / det; - out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; - out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; - out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; - out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; - out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; - out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; - out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; - out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; - out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; - out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; - out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; - out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; - out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; - out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; - out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; - out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; - - return out as mat4; -} - -export function mat4multiply (out: mat4 | number[], a: mat4, b: mat4): mat4 { - if (isIdentityMatrix(a)) { - mat4Clone(out, b); - - return out as mat4; - } else if (isIdentityMatrix(b)) { - mat4Clone(out, a); - - return out as mat4; - } - const a00 = a[0]; - const a01 = a[1]; - const a02 = a[2]; - const a03 = a[3]; - const a10 = a[4]; - const a11 = a[5]; - const a12 = a[6]; - const a13 = a[7]; - const a20 = a[8]; - const a21 = a[9]; - const a22 = a[10]; - const a23 = a[11]; - const a30 = a[12]; - const a31 = a[13]; - const a32 = a[14]; - const a33 = a[15]; - - // Cache only the current line of the second matrix - let b0 = b[0]; - let b1 = b[1]; - let b2 = b[2]; - let b3 = b[3]; - - out[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; - out[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; - out[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; - out[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; - b0 = b[4]; - b1 = b[5]; - b2 = b[6]; - b3 = b[7]; - out[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; - out[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; - out[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; - out[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; - b0 = b[8]; - b1 = b[9]; - b2 = b[10]; - b3 = b[11]; - out[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; - out[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; - out[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; - out[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; - b0 = b[12]; - b1 = b[13]; - b2 = b[14]; - b3 = b[15]; - out[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; - out[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; - out[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; - out[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; - - return out as mat4; -} - -/** - * 计算透视投影相机的投影矩阵 - * @param out - 结果矩阵 - * @param fovy - 视锥体的垂直视野角度 - * @param aspect - 视锥体的长宽比 - * @param near - 视锥体的近平面 - * @param far - 视锥体的远平面 - * @param reverse - 视锥体长宽反转 - */ -export function mat4perspective (out: number[] | mat4, fovy: number, aspect: number, near: number, far: number, reverse?: boolean) { - const f = 1.0 / Math.tan((fovy * d2r) / 2); - let nf; - - out[0] = reverse ? f : f / aspect; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 0; - out[5] = reverse ? f * aspect : f; - out[6] = 0; - out[7] = 0; - out[8] = 0; - out[9] = 0; - out[11] = -1; - out[12] = 0; - out[13] = 0; - out[15] = 0; - - if (far != null && far !== Infinity) { - nf = 1 / (near - far); - out[10] = (far + near) * nf; - out[14] = 2 * far * near * nf; - } else { - out[10] = -1; - out[14] = -2 * near; - } -} - -const matrixRotation = new Float32Array(9); -const quatOut = [0, 0, 0, 1]; -const temps = [] as unknown as vec3; - -export function mat4Determinate (a: mat4) { - const a00 = a[0], - a01 = a[1], - a02 = a[2], - a03 = a[3]; - const a10 = a[4], - a11 = a[5], - a12 = a[6], - a13 = a[7]; - const a20 = a[8], - a21 = a[9], - a22 = a[10], - a23 = a[11]; - const a30 = a[12], - a31 = a[13], - a32 = a[14], - a33 = a[15]; - const b00 = a00 * a11 - a01 * a10; - const b01 = a00 * a12 - a02 * a10; - const b02 = a00 * a13 - a03 * a10; - const b03 = a01 * a12 - a02 * a11; - const b04 = a01 * a13 - a03 * a11; - const b05 = a02 * a13 - a03 * a12; - const b06 = a20 * a31 - a21 * a30; - const b07 = a20 * a32 - a22 * a30; - const b08 = a20 * a33 - a23 * a30; - const b09 = a21 * a32 - a22 * a31; - const b10 = a21 * a33 - a23 * a31; - const b11 = a22 * a33 - a23 * a32; - - return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; -} - -export function getMat4TR (mat4: mat4 | number[], translate: vec3, quat: vec4, scaling: vec3) { - const m11 = mat4[0]; - const m12 = mat4[1]; - const m13 = mat4[2]; - const m21 = mat4[4]; - const m22 = mat4[5]; - const m23 = mat4[6]; - const m31 = mat4[8]; - const m32 = mat4[9]; - const m33 = mat4[10]; - - translate[0] = mat4[12]; - translate[1] = mat4[13]; - translate[2] = mat4[14]; - - if (quat) { - // copy from gl-matrix - const is1 = 1 / scaling[0]; - const is2 = 1 / scaling[1]; - const is3 = 1 / scaling[2]; - - matrixRotation[0] = m11 * is1; - matrixRotation[1] = m12 * is1; - matrixRotation[2] = m13 * is1; - - matrixRotation[3] = m21 * is2; - matrixRotation[4] = m22 * is2; - matrixRotation[5] = m23 * is2; - - matrixRotation[6] = m31 * is3; - matrixRotation[7] = m32 * is3; - matrixRotation[8] = m33 * is3; - - const fTrace = matrixRotation[0] + matrixRotation[4] + matrixRotation[8]; - let fRoot; - - if (fTrace > 0.0) { - // |w| > 1/2, may as well choose w > 1/2 - fRoot = Math.sqrt(fTrace + 1.0); // 2w - quatOut[3] = 0.5 * fRoot; - fRoot = 0.5 / fRoot; // 1/(4w) - quatOut[0] = (matrixRotation[5] - matrixRotation[7]) * fRoot; - quatOut[1] = (matrixRotation[6] - matrixRotation[2]) * fRoot; - quatOut[2] = (matrixRotation[1] - matrixRotation[3]) * fRoot; - } else { - // |w| <= 1/2 - let i = 0; - - if (matrixRotation[4] > matrixRotation[0]) { i = 1; } - if (matrixRotation[8] > matrixRotation[i * 3 + i]) { i = 2; } - const j = (i + 1) % 3; - const k = (i + 2) % 3; - - fRoot = Math.sqrt(matrixRotation[i * 3 + i] - matrixRotation[j * 3 + j] - matrixRotation[k * 3 + k] + 1.0); - quatOut[i] = 0.5 * fRoot; - fRoot = 0.5 / fRoot; - quatOut[3] = (matrixRotation[j * 3 + k] - matrixRotation[k * 3 + j]) * fRoot; - quatOut[j] = (matrixRotation[j * 3 + i] + matrixRotation[i * 3 + j]) * fRoot; - quatOut[k] = (matrixRotation[k * 3 + i] + matrixRotation[i * 3 + k]) * fRoot; - } - - quat[0] = quatOut[0]; - quat[1] = quatOut[1]; - quat[2] = quatOut[2]; - quat[3] = quatOut[3]; - - } - -} - -export function getMat4TRS (mat4: mat4, translate: vec3, quat: vec4, scaling: vec3) { - - scaling[0] = Math.hypot(mat4[0], mat4[1], mat4[2]); - scaling[1] = Math.hypot(mat4[4], mat4[5], mat4[6]); - scaling[2] = Math.hypot(mat4[8], mat4[9], mat4[10]); - const det = mat4Determinate(mat4); - - if (det < 0) { - scaling[0] = -scaling[0]; - } - - return getMat4TR(mat4, translate, quat, scaling); -} - -export function mat4Clone (out: mat4 | number[], from: mat4): mat4 { - for (let i = 0; i < 16; i++) { - out[i] = from[i]; - } - - return out as mat4; -} diff --git a/packages/effects-core/src/math/quat.ts b/packages/effects-core/src/math/quat.ts deleted file mode 100644 index c35ed3bd0..000000000 --- a/packages/effects-core/src/math/quat.ts +++ /dev/null @@ -1,79 +0,0 @@ -import type { vec3, vec4 } from '@galacean/effects-specification'; -import { vec3Cross, vecAdd, vecDot } from './vec'; - -const d2r = Math.PI / 180; -const cos = Math.cos; -const sin = Math.sin; - -export function quatMultiply (out: vec4 | number[], a: vec4, b: vec4): vec4 { - const ax = a[0]; - const ay = a[1]; - const az = a[2]; - const aw = a[3]; - - const bx = b[0]; - const by = b[1]; - const bz = b[2]; - const bw = b[3]; - - out[0] = ax * bw + aw * bx + ay * bz - az * by; - out[1] = ay * bw + aw * by + az * bx - ax * bz; - out[2] = az * bw + aw * bz + ax * by - ay * bx; - out[3] = aw * bw - ax * bx - ay * by - az * bz; - - return out as vec4; -} - -/** - * 按照 ZYX 的旋转顺序把旋转角转换成四元数 - * @param out - 结果向量 - * @param x - 沿x轴旋转角度 - * @param y - 沿y轴旋转角度 - * @param z - 沿z轴旋转角度 - */ -export function quatFromRotation (out: vec4 | number[], x: number, y: number, z: number) { - const c1 = cos((x * d2r) / 2); - const c2 = cos((y * d2r) / 2); - const c3 = cos((z * d2r) / 2); - - const s1 = sin((x * d2r) / 2); - const s2 = sin((y * d2r) / 2); - const s3 = sin((z * d2r) / 2); - - out[0] = s1 * c2 * c3 - c1 * s2 * s3; - out[1] = c1 * s2 * c3 + s1 * c2 * s3; - out[2] = c1 * c2 * s3 - s1 * s2 * c3; - out[3] = c1 * c2 * c3 + s1 * s2 * s3; -} - -/** - * 取四元数的共轭 - * @param out - 结果四元数 - * @param quat - 原始四元数 - */ -export function quatStar (out: vec4 | number[], quat: vec4) { - const x = quat[0], y = quat[1], z = quat[2], w = quat[3]; - - out[0] = -x; - out[1] = -y; - out[2] = -z; - out[3] = w; -} - -/** - * 根据指定四元数计算向量旋转后的结果 - * @param out - 结果向量 - * @param a - 原始向量 - * @param quat - 四元数 - */ -export function rotateByQuat (out: vec3, a: vec3, quat: vec4) { - const x = quat[0], y = quat[1], z = quat[2], w = quat[3]; - const qvec: vec3 = [x, y, z]; - const uv = vec3Cross([], qvec, a); - const uuv = vec3Cross([], qvec, uv); - - vecDot(uuv, uuv, 2); - vecDot(uv, uv, 2 * w); - vecAdd(uuv, uuv, uv); - vecAdd(out, a, uuv); -} diff --git a/packages/effects-core/src/math/raycast.ts b/packages/effects-core/src/math/raycast.ts deleted file mode 100644 index 9e494f45c..000000000 --- a/packages/effects-core/src/math/raycast.ts +++ /dev/null @@ -1,214 +0,0 @@ -import type { vec2, vec3 } from '@galacean/effects-specification'; -import type { Camera } from '../camera'; -import { vec3Cross, vec3MulMat4, vecAdd, vecDot, vecMinus, vecMulScalar, vecNormalize, vecSquareDistance } from './vec'; - -const tmp = [0, 0, 0]; - -export type Triangle = [p0: vec3, p1: vec3, p2: vec3]; - -export interface Ray { - center: vec3, - direction: vec3, -} - -export function setRayFromCamera (x: number, y: number, camera: Camera) { - const origin: vec3 = camera.position ?? [0, 0, 0]; - const direction: vec3 = [x, y, 0]; - const dir: vec3 = [0, 0, 0]; - - vec3MulMat4(dir, direction, camera.getInverseViewProjectionMatrix()); - vecMinus(dir, dir, origin); - - return { - center: origin, - direction: vecNormalize([], dir), - }; -} - -export function intersectRaySphere (out: vec3 | number[], origin: vec3, direction: vec3, center: vec3, radius: number): vec3 | null { - vecMinus(tmp, center, origin); - const len = vecDot(direction, tmp); - - if (len < 0) { // sphere is behind ray - return null; - } - vecMulScalar(tmp, direction, len); - vecAdd(tmp, origin, tmp); - - const dSq = vecSquareDistance(center, tmp); - const rSq = radius * radius; - - if (dSq > rSq) { - return null; - } - vecMulScalar(out, direction, len - Math.sqrt(rSq - dSq)); - - return vecAdd(out as vec3, out as vec3, origin); -} - -export function intersectRayBox (out: vec3 | number[], origin: vec3, direction: vec3, center: vec3, size: vec3): vec3 | null { - const [dx, dy, dz] = direction; - const [ox, oy, oz] = origin; - const bxmin = center[0] - size[0] / 2, - bxmax = center[0] + size[0] / 2, - bymin = center[1] - size[1] / 2, - bymax = center[1] + size[1] / 2, - bzmin = center[2] - size[2] / 2, - bzmax = center[2] + size[2] / 2; - let tmin, tmax, tymin, tymax, tzmin, tzmax; - const invdirx = 1 / dx, invdiry = 1 / dy, invdirz = 1 / dz; - - if (invdirx >= 0) { - tmin = (bxmin - ox) * invdirx; - tmax = (bxmax - ox) * invdirx; - } else { - tmin = (bxmax - ox) * invdirx; - tmax = (bxmin - ox) * invdirx; - } - if (invdiry >= 0) { - tymin = (bymin - oy) * invdiry; - tymax = (bymax - oy) * invdiry; - } else { - tymin = (bymax - oy) * invdiry; - tymax = (bymin - oy) * invdiry; - } - if ((tmin > tymax) || (tymin > tmax)) { - return null; - } - if (tymin > tmin || tmin !== tmin) { - tmin = tymin; - } - if (tymax < tmax || tmax !== tmax) { - tmax = tymax; - } - if (tymin > tmin || tmin !== tmin) { - tmin = tymin; - } - if (tymax < tmax || tmax !== tmax) { - tmax = tymax; - } - if (invdirz >= 0) { - tzmin = (bzmin - oz) * invdirz; - tzmax = (bzmax - oz) * invdirz; - } else { - tzmin = (bzmax - oz) * invdirz; - tzmax = (bzmin - oz) * invdirz; - } - if ((tmin > tzmax) || (tzmin > tmax)) { - return null; - } - if (tzmin > tmin || tmin !== tmin) { - tmin = tzmin; - } - if (tzmax < tmax || tmax !== tmax) { - tmax = tzmax; - } - if (tmax < 0) { - return null; - } - tmin >= 0 ? vecMulScalar(out, origin, tmin) : vecMulScalar(out, origin, tmax); - - return vecAdd(out as vec3, out as vec3, origin); - -} - -const edge1: vec3 = [0, 0, 0], edge2: vec3 = [0, 0, 0], normal: vec3 = [0, 0, 0], diff: vec3 = [0, 0, 0]; - -export function trianglesFromRect (position: vec3, halfWidth: number, halfHeight: number): Triangle[] { - const [x, y, z] = position; - const p0 = [x - halfWidth, y + halfHeight, z] as vec3; - const p1 = [x - halfWidth, y - halfHeight, z] as vec3; - const p2 = [x + halfWidth, y - halfHeight, z] as vec3; - const p3 = [x + halfWidth, y + halfHeight, z] as vec3; - - return [ - [p0, p1, p2], - [p0.slice() as vec3, p2.slice() as vec3, p3], - ]; -} - -export function intersectRayTriangle (out: vec3 | number[], origin: vec3, direction: vec3, triangle: Triangle, backfaceCulling?: boolean): vec3 | null { - let sign: number, DdN: number, DdQxE2: number, DdE1xQ: number, QdN: number; - let temp: vec3 = direction; - - vecMinus(edge1, triangle[1], triangle[0]); - vecMinus(edge2, triangle[2], triangle[0]); - vecMinus(diff, origin, triangle[0]); - vec3Cross(normal, edge1, edge2); - - DdN = vecDot(temp, normal); - if (DdN > 0) { - if (backfaceCulling) { - return null; - } - sign = 1; - } else if (DdN < 0) { - sign = -1; - DdN = -DdN; - } else { - return null; - } - vec3Cross(edge2, diff, edge2); - temp = direction; - DdQxE2 = vecDot(temp, edge2); - DdQxE2 = sign * DdQxE2; - if (DdQxE2 < 0) { - return null; - } - - vec3Cross(edge1, edge1, diff); - temp = direction; - DdE1xQ = vecDot(temp, edge1); - DdE1xQ = sign * DdE1xQ; - if (DdE1xQ < 0) { - return null; - } - - if (DdQxE2 + DdE1xQ > DdN) { - return null; - } - - QdN = vecDot(diff, normal); - QdN = -sign * QdN; - if (QdN < 0) { - return null; - } - - vecMulScalar(out, direction, QdN / DdN,); - - return vecAdd(out as vec3, out as vec3, origin); - -} - -const e0: vec2 = [0, 0], e1: vec2 = [0, 0], e2: vec2 = [0, 0]; - -export function dotInTriangle (out: boolean, dot: vec2, triangle: Triangle): boolean { - const [p0, p1, p2] = triangle; - const a = [p0[0], p0[1]], b = [p1[0], p1[1]], c = [p2[0], p2[1]]; - - vecMinus(e0, c, a); - vecMinus(e1, b, a); - vecMinus(e2, dot, a); - - const dot00 = vecDot(e0, e0); - const dot01 = vecDot(e0, e1); - const dot02 = vecDot(e0, e2); - const dot11 = vecDot(e1, e1); - const dot12 = vecDot(e1, e2); - - const denom = (dot00 * dot11 - dot01 * dot01); - - if (denom === 0) { - out = false; - - return out; - } - const invDenom = 1 / denom; - const u = (dot11 * dot02 - dot01 * dot12) * invDenom; - const v = (dot00 * dot12 - dot01 * dot02) * invDenom; - - out = (1 - u - v >= 0) && (v >= 0) && ((1 - u) <= 1); - - return out; -} - diff --git a/packages/effects-core/src/math/translate.ts b/packages/effects-core/src/math/translate.ts index 6f0036daa..4ae88f733 100644 --- a/packages/effects-core/src/math/translate.ts +++ b/packages/effects-core/src/math/translate.ts @@ -1,7 +1,5 @@ -import type { vec3 } from '@galacean/effects-specification'; -import type { mat3 } from './types'; import type { ValueGetter } from './value-getter'; -import { mat3FromRotation, vec3MulMat3, vecAdd } from './vec'; +import { Euler, Matrix4, Vector3 } from '@galacean/effects-math/es/core/index'; export function translatePoint (x: number, y: number): number[] { const origin = [-.5, .5, -.5, -.5, .5, .5, .5, -.5]; @@ -14,7 +12,8 @@ export function translatePoint (x: number, y: number): number[] { return origin; } -const tempRot: mat3 = [] as unknown as mat3; +const tempEuler = new Euler(); +const tempMat4 = new Matrix4(); export interface TranslateTarget { speedOverLifetime?: ValueGetter, @@ -24,17 +23,15 @@ export interface TranslateTarget { } export function calculateTranslation ( - out: vec3, + out: Vector3, target: TranslateTarget, - acc: vec3, + acc: Vector3, time: number, duration: number, - posData: number[] | Float32Array, - velData: Float32Array | number[], - posStartIndex?: number, - velStartIndex?: number -): vec3 { - let ret = out; + posData: Vector3, + velData: Vector3, +): Vector3 { + const ret = out; const lifetime = time / duration; let speedIntegrate = time; const speedOverLifetime = target.speedOverLifetime; @@ -45,21 +42,23 @@ export function calculateTranslation ( const d = target.gravityModifier ? target.gravityModifier.getIntegrateByTime(0, time) : 0; - posStartIndex = posStartIndex || 0; - velStartIndex = velStartIndex || 0; - for (let i = 0; i < 3; i++) { - ret[i] = posData[posStartIndex + i] + velData[velStartIndex + i] * speedIntegrate + acc[i] * d; - } + ret.copyFrom(posData); + ret.addScaledVector(velData, speedIntegrate); + ret.addScaledVector(acc, d); const linearVelocityOverLifetime = target.linearVelOverLifetime || {}; const orbVelOverLifetime = target.orbitalVelOverLifetime || {}; const map = ['x', 'y', 'z']; if (orbVelOverLifetime.enabled) { - const center = orbVelOverLifetime.center || [0, 0, 0]; - const pos: vec3 = [ret[0] - center[0], ret[1] - center[1], ret[2] - center[2]]; + const center = new Vector3(); + + if (orbVelOverLifetime.center) { + center.setFromArray(orbVelOverLifetime.center); + } + const pos = ret.clone().subtract(center); const asRotation = orbVelOverLifetime.asRotation; - const rot = vec3MulMat3(pos, pos, mat3FromRotation(tempRot, map.map(pro => { + const orbVel = map.map(pro => { const value = orbVelOverLifetime[pro]; if (value) { @@ -67,9 +66,13 @@ export function calculateTranslation ( } return 0; - }) as vec3)); + }); - ret = vecAdd(ret, center, rot); + tempEuler.setFromArray(orbVel).negate(); + tempMat4.setFromEuler(tempEuler); + const rot = tempMat4.transformPoint(pos); + + ret.addVectors(center, rot); } if (linearVelocityOverLifetime.enabled) { const asMovement = linearVelocityOverLifetime.asMovement; @@ -78,7 +81,9 @@ export function calculateTranslation ( const pro = linearVelocityOverLifetime[map[i]]; if (pro) { - ret[i] += asMovement ? pro.getValue(lifetime) : pro.getIntegrateValue(0, time, duration); + const val = asMovement ? pro.getValue(lifetime) : pro.getIntegrateValue(0, time, duration); + + ret.setElement(i, ret.getElement(i) + val); } } } diff --git a/packages/effects-core/src/math/types.ts b/packages/effects-core/src/math/types.ts deleted file mode 100644 index 7fbbeaefc..000000000 --- a/packages/effects-core/src/math/types.ts +++ /dev/null @@ -1,12 +0,0 @@ -export type mat2 = [m11: number, m12: number, m21: number, m22: number]; -export type mat3 = [ - m11: number, m12: number, m13: number, - m21: number, m22: number, m23: number, - m31: number, m32: number, m33: number, -]; -export type mat4 = [ - m11: number, m12: number, m13: number, m14: number, - m21: number, m22: number, m23: number, m24: number, - m31: number, m32: number, m33: number, m34: number, - m41: number, m42: number, m43: number, m44: number -]; diff --git a/packages/effects-core/src/math/utils.ts b/packages/effects-core/src/math/utils.ts index ebc546618..81d206465 100644 --- a/packages/effects-core/src/math/utils.ts +++ b/packages/effects-core/src/math/utils.ts @@ -1,6 +1,75 @@ import * as spec from '@galacean/effects-specification'; +import type { vec2, vec3, vec4 } from '@galacean/effects-specification'; +import type { TriangleLike } from '@galacean/effects-math/es/core/index'; +import { Vector3, Ray } from '@galacean/effects-math/es/core/index'; +import type { Camera } from '../camera'; -export const particleOriginTranslateMap: Record = { +export type vec = number[]; + +export function ensureVec3 (num?: any): vec3 { + return Array.isArray(num) ? [num[0], num[1], num[2]] : [0, 0, 0]; +} + +export function vecFill (out: T | number[], number: number): T { + for (let i = 0, len = out.length; i < len; i++) { + out[i] = number; + } + + return out as T; +} + +export function vecAssign (out: T | number[] | Float32Array, a: T, count: number, start = 0): T { + for (let i = 0; i < count; i++) { + out[i] = a[i + start]; + } + + return out as T; +} + +export function vecNormalize ( + out: T | number[], + a?: T | number[], +): T { + if (arguments.length === 1) { + a = out; + out = []; + } + const ap = a!; + const sum = Math.hypot(...ap); + + if (sum === 0) { + return vecAssign(out, ap, ap.length) as T; + } + for (let i = 0; i < ap.length; i++) { + out[i] = ap[i] / sum; + } + + return out as T; +} + +export function vecMulCombine (out: T | number[] | Float32Array, a?: T, b?: T): T { + if (a && b) { + for (let i = 0, len = a.length; i < len; i++) { + out[i] = a[i] * b[i]; + } + } else if (a) { + if (out !== a) { + for (let i = 0; i < a.length; i++) { + out[i] = a[i]; + } + } + } else if (b) { + if (out !== b) { + for (let i = 0; i < b.length; i++) { + out[i] = b[i]; + } + } + } + + return out as T; +} + +export const particleOriginTranslateMap: Record = { [spec.ParticleOrigin.PARTICLE_ORIGIN_CENTER]: [0, 0], [spec.ParticleOrigin.PARTICLE_ORIGIN_CENTER_BOTTOM]: [0, -0.5], [spec.ParticleOrigin.PARTICLE_ORIGIN_CENTER_TOP]: [0, 0.5], @@ -12,18 +81,10 @@ export const particleOriginTranslateMap: Record = { [spec.ParticleOrigin.PARTICLE_ORIGIN_RIGHT_TOP]: [0.5, 0.5], }; -export function clamp (v: number, min: number, max: number): number { - return v > max ? max : (v < min ? min : v); -} - -export function nearestPowerOfTwo (value: number): number { - return 2 ** Math.round(Math.log(value) / Math.LN2); -} - /** * 提取并转换 JSON 数据中的 anchor 值 */ -export function convertAnchor (anchor?: spec.vec2, particleOrigin?: spec.ParticleOrigin): spec.vec2 { +export function convertAnchor (anchor?: vec2, particleOrigin?: spec.ParticleOrigin): vec2 { if (anchor) { return [anchor[0] - 0.5, 0.5 - anchor[1]]; } else if (particleOrigin) { @@ -33,3 +94,32 @@ export function convertAnchor (anchor?: spec.vec2, particleOrigin?: spec.Particl } } +export function nearestPowerOfTwo (value: number): number { + return 2 ** Math.round(Math.log(value) / Math.LN2); +} + +export function setRayFromCamera (x: number, y: number, camera: Camera) { + const origin = camera.position; + const direction = new Vector3(x, y, 0); + const dir = new Vector3(); + + const mat = camera.getInverseViewProjectionMatrix(); + + mat.projectPoint(direction, dir); + dir.subtract(origin); + + return new Ray(origin, dir); +} + +export function trianglesFromRect (position: Vector3, halfWidth: number, halfHeight: number): TriangleLike[] { + const { x, y, z } = position; + const p0 = new Vector3(x - halfWidth, y + halfHeight, z); + const p1 = new Vector3(x - halfWidth, y - halfHeight, z); + const p2 = new Vector3(x + halfWidth, y - halfHeight, z); + const p3 = new Vector3(x + halfWidth, y + halfHeight, z); + + return [ + { p0, p1, p2 }, + { p0: p0.clone(), p1: p2.clone(), p2: p3 }, + ]; +} diff --git a/packages/effects-core/src/math/vec.ts b/packages/effects-core/src/math/vec.ts deleted file mode 100644 index 95114939e..000000000 --- a/packages/effects-core/src/math/vec.ts +++ /dev/null @@ -1,255 +0,0 @@ -import type { vec2, vec3, vec4 } from '@galacean/effects-specification'; -import type { mat3, mat4 } from './types'; -import { isArray } from '../utils'; -import { clamp } from './utils'; - -const d2r = Math.PI / 180; -const r2d = 180 / Math.PI; -const cos = Math.cos; -const sin = Math.sin; - -export type vec = number[]; - -export function vecAdd (out: T | number[], a: T, b: T): T { - for (let i = 0, len = a.length; i < len; i++) { - out[i] = a[i] + b[i]; - } - - return out as T; -} - -export function vecFill (out: T | number[], number: number): T { - for (let i = 0, len = out.length; i < len; i++) { - out[i] = number; - } - - return out as T; -} - -export const NumberEpsilon = Number.EPSILON || Math.pow(2, -32); - -export function isZeroVec (vec: vec): boolean { - for (let i = 0, len = vec.length; i < len; i++) { - if (Math.abs(vec[i]) > NumberEpsilon) { - return false; - } - } - - return true; -} - -export function ensureVec3 (num?: any): vec3 { - return isArray(num) ? [num[0], num[1], num[2]] : [0, 0, 0]; -} - -export function vec3MulMat3 (out: vec3, a: vec3, m: mat3): vec3 { - const x = a[0], y = a[1], z = a[2]; - - out[0] = x * m[0] + y * m[3] + z * m[6]; - out[1] = x * m[1] + y * m[4] + z * m[7]; - out[2] = x * m[2] + y * m[5] + z * m[8]; - - return out; -} - -export function vecAssign (out: T | number[] | Float32Array, a: T, count: number, start = 0): T { - for (let i = 0; i < count; i++) { - out[i] = a[i + start]; - } - - return out as T; -} - -export function vecMulCombine (out: T | number[] | Float32Array, a?: T, b?: T): T { - if (a && b) { - for (let i = 0, len = a.length; i < len; i++) { - out[i] = a[i] * b[i]; - } - } else if (a) { - if (out !== a) { - for (let i = 0; i < a.length; i++) { - out[i] = a[i]; - } - } - } else if (b) { - if (out !== b) { - for (let i = 0; i < b.length; i++) { - out[i] = b[i]; - } - } - } - - return out as T; -} - -export function vecNormalize ( - out: T | number[], - a?: T | number[], -): T { - if (arguments.length === 1) { - a = out; - out = []; - } - const ap = a!; - const sum = Math.hypot(...ap); - - if (sum === 0) { - return vecAssign(out, ap, ap.length) as T; - } - for (let i = 0; i < ap.length; i++) { - out[i] = ap[i] / sum; - } - - return out as T; -} - -export function vecMulScalar (out: T | number[], vec: T, a: number): T { - for (let i = 0, len = vec.length; i < len; i++) { - out[i] = vec[i] * a; - } - - return out as T; -} - -export function vecMinus (out: T | number[], v0: T, v1: T): T { - for (let i = 0, len = v0.length; i < len; i++) { - out[i] = v0[i] - v1[i]; - } - - return out as T; -} - -export function vecSquareDistance (v0: vec, v1: vec): number { - let sum = 0; - - for (let i = 0, len = v0.length; i < len; i++) { - const d = v0[i] - v1[i]; - - sum += d * d; - } - - return sum; -} - -type vecDotRes = T extends vec ? number : vec; -export function vecDot (out: vec, a: vec, b?: T): vecDotRes { - if (isNaN(b)) { - let sum = 0; - - for (let i = 0, len = a.length; i < len; i++) { - sum += out[i] * a[i]; - } - - return sum as vecDotRes; - } - for (let i = 0, len = a.length; i < len; i++) { - out[i] = a[i] * (b as number); - } - - return (out) as vecDotRes; -} - -export function vec3Cross (out: vec3 | number[], a: vec3, b: vec3): vec3 { - const ax = a[0], ay = a[1], az = a[2]; - const bx = b[0], by = b[1], bz = b[2]; - - out[0] = ay * bz - az * by; - out[1] = az * bx - ax * bz; - out[2] = ax * by - ay * bx; - - return out as vec3; -} - -export function vec3MulMat4 (out: vec3 | number[], vec3: vec3, mat4: mat4): vec3 { - const x = vec3[0], y = vec3[1], z = vec3[2]; - let w = mat4[3] * x + mat4[7] * y + mat4[11] * z + mat4[15]; - - w = w || 1.0; - out[0] = (mat4[0] * x + mat4[4] * y + mat4[8] * z + mat4[12]) / w; - out[1] = (mat4[1] * x + mat4[5] * y + mat4[9] * z + mat4[13]) / w; - out[2] = (mat4[2] * x + mat4[6] * y + mat4[10] * z + mat4[14]) / w; - - return out as vec3; -} - -export function vec3RotateByMat4 (out: vec3 | number[], a: vec3, m: mat4): vec3 { - const x = a[0], y = a[1], z = a[2]; - const w = (m[3] * x + m[7] * y + m[11] * z + m[15]) || 1; - - out[0] = (m[0] * x + m[4] * y + m[8] * z) / w; - out[1] = (m[1] * x + m[5] * y + m[9] * z) / w; - out[2] = (m[2] * x + m[6] * y + m[10] * z) / w; - - return out as vec3; -} - -const tempMat3FromRotationZ: mat3 = [0, 0, 0, 0, 0, 0, 0, 0, 0]; - -export function mat3FromRotationZ (out: mat3 | number[], rad: number): mat3 { - if (!out) { - out = tempMat3FromRotationZ; - } - const s = sin(rad); - const c = cos(rad); - - out[0] = c; - out[1] = s; - out[2] = 0; - out[3] = -s; - out[4] = c; - out[5] = 0; - out[6] = 0; - out[7] = 0; - out[8] = 1; - - return out as mat3; -} - -function mat3FromRotationZYX (ret: mat3 | number[], x: number, y: number, z: number): mat3 { - const cosX = cos(x * d2r); - const cosY = cos(y * d2r); - const cosZ = cos(z * d2r); - const sinX = sin(x * d2r); - const sinY = sin(y * d2r); - const sinZ = sin(z * d2r); - - ret[0] = cosY * cosZ; - ret[1] = cosY * sinZ; - ret[2] = -sinY; - ret[3] = -cosX * sinZ + sinX * sinY * cosZ; - ret[4] = cosX * cosZ + sinX * sinY * sinZ; - ret[5] = sinX * cosY; - ret[6] = sinZ * sinX + cosX * sinY * cosZ; - ret[7] = -sinX * cosZ + cosX * sinY * sinZ; - ret[8] = cosX * cosY; - - return ret as mat3; -} - -export function mat3FromRotation (ret: mat3 | number[], rotation: vec3): mat3 { - return mat3FromRotationZYX(ret, -rotation[0], -rotation[1], -rotation[2]); -} - -function rotationZYXFromMat3 (out: vec3, mat3: mat3): vec3 { - const te = mat3; - const m11 = te[0], m12 = te[3], m13 = te[6]; - const m21 = te[1], m22 = te[4], m23 = te[7]; - const m31 = te[2], m32 = te[5], m33 = te[8]; - - out[1] = Math.asin(clamp(-m31, -1, 1)) * r2d; - if (Math.abs(m31) < 0.9999999) { - out[0] = Math.atan2(m32, m33) * r2d; - out[2] = Math.atan2(m21, m11) * r2d; - } else { - out[0] = 0; - out[2] = Math.atan2(-m12, m22) * r2d; - } - - return out; -} - -export function rotationFromMat3 (out: vec3, mat3: mat3) { - rotationZYXFromMat3(out, mat3); - - return out; -} diff --git a/packages/effects-core/src/plugins/cal/calculate-item.ts b/packages/effects-core/src/plugins/cal/calculate-item.ts index 4bccbe2ce..b276cdee0 100644 --- a/packages/effects-core/src/plugins/cal/calculate-item.ts +++ b/packages/effects-core/src/plugins/cal/calculate-item.ts @@ -1,14 +1,7 @@ import * as spec from '@galacean/effects-specification'; +import { Euler, Vector3 } from '@galacean/effects-math/es/core/index'; import type { ValueGetter } from '../../math'; -import { - calculateTranslation, - createValueGetter, - ensureVec3, isZeroVec, - vecAdd, - vecAssign, - vecFill, vecMulScalar, - vecNormalize, -} from '../../math'; +import { calculateTranslation, createValueGetter, ensureVec3 } from '../../math'; import type { Transform } from '../../transform'; import type { VFXItem, VFXItemContent } from '../../vfx-item'; import type { SpriteRenderData } from '../sprite/sprite-mesh'; @@ -17,9 +10,9 @@ import type { SpriteRenderData } from '../sprite/sprite-mesh'; * 基础位移属性数据 */ export type ItemBasicTransform = { - position: [x: number, y: number, z: number], - rotation: [x: number, y: number, z: number], - scale: [x: number, y: number, z: number], + position: Vector3, + rotation: Euler, + scale: Vector3, path?: ValueGetter, }; @@ -34,21 +27,21 @@ export type ItemLinearVelOverLifetime = { export interface CalculateItemOptions { delay: number, startSpeed: number, - direction: spec.vec3, + direction: Vector3, startSize: number, sizeAspect: number, duration: number, looping: boolean, endBehavior: number, reusable?: boolean, - gravity: spec.vec3, + gravity: Vector3, gravityModifier: ValueGetter, startRotation?: number, start3DRotation?: number, } -const tempRot: spec.vec3 = [0, 0, 0]; -const tempSize: spec.vec3 = [1, 1, 1]; -const tempPos: spec.vec3 = [0, 0, 0]; +const tempRot = new Euler(); +const tempSize = new Vector3(1, 1, 1); +const tempPos = new Vector3(); export class CalculateItem { renderData: SpriteRenderData; @@ -91,18 +84,19 @@ export class CalculateItem { // readonly startColor: vec4 = [1, 1, 1, 1]; /*****************/ - private _velocity: spec.vec3; + private _velocity: Vector3; + constructor ( props: spec.NullContent, - vfxItem: VFXItem, + protected vfxItem: VFXItem, ) { this.transform = vfxItem.transform; const scale = this.transform.scale; this.basicTransform = { - position: [...this.transform.position], - rotation: [...this.transform.getRotation()], - scale: [scale[0], scale[1], scale[0]], + position: this.transform.position.clone(), + rotation: this.transform.getRotation(), + scale: new Vector3(scale.x, scale.y, scale.x), }; const { rotationOverLifetime, sizeOverLifetime, positionOverLifetime = {} } = props; @@ -178,14 +172,14 @@ export class CalculateItem { reusable: !!vfxItem.reusable, delay: vfxItem.delay || 0, startSpeed: positionOverLifetime.startSpeed || 0, - startSize: scale && scale[0] || 1, - sizeAspect: scale && (scale[0] / (scale[1] || 1)) || 1, + startSize: scale && scale.x || 1, + sizeAspect: scale && (scale.x / (scale.y || 1)) || 1, duration: vfxItem.duration || 0, looping: vfxItem.endBehavior && vfxItem.endBehavior === spec.ItemEndBehavior.loop, endBehavior: vfxItem.endBehavior || spec.END_BEHAVIOR_DESTROY, - gravity: ensureVec3(positionOverLifetime.gravity), + gravity: Vector3.fromArray(positionOverLifetime.gravity || []), gravityModifier: createValueGetter(positionOverLifetime.gravityOverLifetime ?? 0), - direction: positionOverLifetime.direction ? vecNormalize([], positionOverLifetime.direction) : ensureVec3(), + direction: positionOverLifetime.direction ? Vector3.fromArray(positionOverLifetime.direction).normalize() : new Vector3(), /* 要过包含父节点颜色/透明度变化的动画的帧对比 打开这段兼容代码 */ // startColor: props.options.startColor || [1, 1, 1, 1], /*********************/ @@ -204,14 +198,14 @@ export class CalculateItem { get startSize () { return this.basicTransform.scale; } - - set startSize (scale: [x: number, y: number, z: number]) { + set startSize (scale: Vector3) { this.basicTransform.scale = scale; } get velocity () { if (!this._velocity) { - this._velocity = vecMulScalar([] as unknown as spec.vec3, this.options.direction, this.options.startSpeed); + this._velocity = this.options.direction.clone(); + this._velocity.multiply(this.options.startSpeed); } return this._velocity; @@ -220,8 +214,8 @@ export class CalculateItem { getWillTranslate (): boolean { return !!((this.linearVelOverLifetime && this.linearVelOverLifetime.enabled) || (this.orbitalVelOverLifetime && this.orbitalVelOverLifetime.enabled) || - (this.options.gravityModifier && !isZeroVec(this.options.gravity)) || - (this.options.startSpeed && !isZeroVec(this.options.direction))); + (this.options.gravityModifier && !this.options.gravity.isZero()) || + (this.options.startSpeed && !this.options.direction.isZero())); } updateTime (globalTime: number) { @@ -244,8 +238,8 @@ export class CalculateItem { getRenderData (_time: number, init?: boolean): SpriteRenderData { const options = this.options; - const sizeInc = vecFill(tempSize, 1); - const rotInc = vecFill(tempRot, 0); + const sizeInc = tempSize.setFromNumber(1); + const rotInc = tempRot.set(0, 0, 0); let sizeChanged = false, rotChanged = false; const time = _time < 0 ? _time : Math.max(_time, 0.); const duration = options.duration; @@ -259,12 +253,12 @@ export class CalculateItem { life = life < 0 ? 0 : (life > 1 ? 1 : life); if (this.sizeXOverLifetime) { - sizeInc[0] = this.sizeXOverLifetime.getValue(life); + sizeInc.x = this.sizeXOverLifetime.getValue(life); if (this.sizeSeparateAxes) { - sizeInc[1] = this.sizeYOverLifetime.getValue(life); - sizeInc[2] = this.sizeZOverLifetime.getValue(life); + sizeInc.y = this.sizeYOverLifetime.getValue(life); + sizeInc.z = this.sizeZOverLifetime.getValue(life); } else { - sizeInc[2] = sizeInc[1] = sizeInc[0]; + sizeInc.z = sizeInc.y = sizeInc.x; } sizeChanged = true; } @@ -277,33 +271,33 @@ export class CalculateItem { const incZ = func(rotationOverLifetime.z!); const separateAxes = rotationOverLifetime.separateAxes; - rotInc[0] = separateAxes ? func(rotationOverLifetime.x!) : 0; - rotInc[1] = separateAxes ? func(rotationOverLifetime.y!) : 0; - rotInc[2] = incZ; + rotInc.x = separateAxes ? func(rotationOverLifetime.x!) : 0; + rotInc.y = separateAxes ? func(rotationOverLifetime.y!) : 0; + rotInc.z = incZ; rotChanged = true; } if (rotChanged || init) { - const rot = vecAdd(tempRot, this.basicTransform.rotation, rotInc); + const rot = tempRot.addEulers(this.basicTransform.rotation, rotInc); - ret.transform.setRotation(rot[0], rot[1], rot[2]); + ret.transform.setRotation(rot.x, rot.y, rot.z); } - let pos: spec.vec3 | undefined; + let pos: Vector3 | undefined; if (this.getWillTranslate() || init) { - pos = [0, 0, 0]; + pos = new Vector3(); calculateTranslation(pos, this, this.options.gravity, time, duration, this.basicTransform.position, this.velocity); } if (this.basicTransform.path) { if (!pos) { - pos = vecAssign(tempPos, this.basicTransform.position, 3); + pos = this.basicTransform.position.clone(); } - vecAdd(pos, pos, this.basicTransform.path.getValue(life)); + pos.add(this.basicTransform.path.getValue(life)); } if (pos) { - this.transform.setPosition(pos[0], pos[1], pos[2]); + this.transform.setPosition(pos.x, pos.y, pos.z); } /* 要过包含父节点颜色/透明度变化的动画的帧对比 打开这段兼容代码 */ @@ -331,8 +325,8 @@ export class CalculateItem { return ret; } - protected calculateScaling (sizeChanged: boolean, sizeInc: spec.vec3, init?: boolean) { - this.transform.setScale(sizeInc[0] * this.startSize[0], sizeInc[1] * this.startSize[1], sizeInc[2] * this.startSize[2]); + protected calculateScaling (sizeChanged: boolean, sizeInc: Vector3, init?: boolean) { + this.transform.setScale(sizeInc.x * this.startSize.x, sizeInc.y * this.startSize.y, sizeInc.z * this.startSize.z); } } diff --git a/packages/effects-core/src/plugins/cal/calculate-vfx-item.ts b/packages/effects-core/src/plugins/cal/calculate-vfx-item.ts index 0765c74f9..e1c3170f9 100644 --- a/packages/effects-core/src/plugins/cal/calculate-vfx-item.ts +++ b/packages/effects-core/src/plugins/cal/calculate-vfx-item.ts @@ -1,4 +1,5 @@ import * as spec from '@galacean/effects-specification'; +import { Vector3 } from '@galacean/effects-math/es/core/vector3'; import type { VFXItemProps } from '../../vfx-item'; import { VFXItem } from '../../vfx-item'; import type { Composition } from '../../composition'; @@ -53,13 +54,13 @@ export class CalculateVFXItem extends VFXItem { } override setScale (x: number, y: number, z: number) { - this.content.startSize = [x, y, z]; + this.content.startSize = new Vector3(x, y, z); } override scale (x: number, y: number, z: number) { - const startSize = this.content.startSize.slice(); + const startSize = this.content.startSize.clone(); - this.content.startSize = [x * startSize[0], y * startSize[1], z * startSize[2]]; + this.content.startSize = new Vector3(x * startSize.x, y * startSize.y, z * startSize.z); } override getHitTestParams (force?: boolean): void { diff --git a/packages/effects-core/src/plugins/camera/camera-controller-node.ts b/packages/effects-core/src/plugins/camera/camera-controller-node.ts index bfe52d328..e4394dc7d 100644 --- a/packages/effects-core/src/plugins/camera/camera-controller-node.ts +++ b/packages/effects-core/src/plugins/camera/camera-controller-node.ts @@ -1,7 +1,7 @@ -import type { vec3, vec4 } from '@galacean/effects-specification'; import type * as spec from '@galacean/effects-specification'; +import { clamp, Euler, Quaternion, Vector3 } from '@galacean/effects-math/es/core/index'; import type { ValueGetter } from '../../math'; -import { createValueGetter, vecAdd, vecAssign, clamp } from '../../math'; +import { createValueGetter } from '../../math'; import { Transform } from '../../transform'; export class CameraController { @@ -9,18 +9,18 @@ export class CameraController { far: number; fov: number; clipMode?: spec.CameraClipMode; - position: vec3; - rotation: vec3; + position: Vector3; + rotation: Euler; private options: { - position: vec3, - rotation: vec3, + position: Vector3, + rotation: Euler, near: ValueGetter, far: ValueGetter, fov: ValueGetter, }; private readonly translateOverLifetime?: { - path?: ValueGetter, + path?: ValueGetter, x: ValueGetter, y: ValueGetter, z: ValueGetter, @@ -37,16 +37,14 @@ export class CameraController { private readonly transform: Transform, model: spec.CameraContent, ) { - const { - position = [0, 0, 0], - } = transform; + const { position } = transform; const rotation = transform.getRotation(); const { near, far, fov, clipMode } = model.options; this.clipMode = clipMode; this.options = { - position: vecAssign([0, 0, 0], position, 3), - rotation: vecAssign([0, 0, 0], rotation, 3), + position: position.clone(), + rotation: rotation.clone(), near: createValueGetter(near), far: createValueGetter(far), fov: createValueGetter(fov), @@ -75,44 +73,50 @@ export class CameraController { } update (lifetime: number) { - const quat: vec4 = [0, 0, 0, 1], position: vec3 = [0, 0, 0], rotation: vec3 = [0, 0, 0]; + const quat = new Quaternion(); + const position = new Vector3(); + const rotation = new Euler(); - vecAssign(position, this.options.position, 3); - vecAssign(rotation, this.options.rotation, 3); + position.copyFrom(this.options.position); + rotation.copyFrom(this.options.rotation); const translateOverLifetime = this.translateOverLifetime; const rotationOverLifetime = this.rotationOverLifetime; lifetime = clamp(lifetime, 0, 1); if (translateOverLifetime) { - position[0] += translateOverLifetime.x.getValue(lifetime); - position[1] += translateOverLifetime.y.getValue(lifetime); - position[2] += translateOverLifetime.z.getValue(lifetime); + position.x += translateOverLifetime.x.getValue(lifetime); + position.y += translateOverLifetime.y.getValue(lifetime); + position.z += translateOverLifetime.z.getValue(lifetime); if (translateOverLifetime.path) { - vecAdd(position, position, translateOverLifetime.path.getValue(lifetime)); + const val = translateOverLifetime.path.getValue(lifetime); + + position.x += val[0]; + position.y += val[1]; + position.z += val[2]; } } if (rotationOverLifetime) { const z = rotationOverLifetime.z.getValue(lifetime); - rotation[2] += z; + rotation.z += z; if (rotationOverLifetime.separateAxes) { - rotation[0] += rotationOverLifetime.x.getValue(lifetime); - rotation[1] += rotationOverLifetime.y.getValue(lifetime); + rotation.x += rotationOverLifetime.x.getValue(lifetime); + rotation.y += rotationOverLifetime.y.getValue(lifetime); } else { - rotation[0] += z; - rotation[1] += z; + rotation.x += z; + rotation.y += z; } } this.far = this.options.far.getValue(lifetime); this.near = this.options.near.getValue(lifetime); this.fov = this.options.fov.getValue(lifetime); - this.transform.setPosition(position[0], position[1], position[2]); - this.transform.setRotation(rotation[0], rotation[1], rotation[2]); + this.transform.setPosition(position.x, position.y, position.z); + this.transform.setRotation(rotation.x, rotation.y, rotation.z); this.transform.assignWorldTRS(position, quat); this.position = position; - this.rotation = Transform.getRotation(rotation, quat); + this.rotation = Transform.getRotation(quat, rotation); } } diff --git a/packages/effects-core/src/plugins/interact/click-handler.ts b/packages/effects-core/src/plugins/interact/click-handler.ts index c0e4021d6..6043cb462 100644 --- a/packages/effects-core/src/plugins/interact/click-handler.ts +++ b/packages/effects-core/src/plugins/interact/click-handler.ts @@ -1,6 +1,6 @@ import type * as spec from '@galacean/effects-specification'; -import type { vec2, vec3 } from '@galacean/effects-specification'; -import type { mat4, Ray, Triangle } from '../../math'; +import type { Matrix4, Ray, TriangleLike, Vector2, Vector3 } from '@galacean/effects-math/es/core/index'; +import type { CompVFXItem } from '../../comp-vfx-item'; export enum HitTestType { triangle = 1, @@ -11,65 +11,66 @@ export enum HitTestType { export interface BoundingBoxData { type: HitTestType, - area: (Triangle | Record)[], + area: (TriangleLike | Record)[], } export interface BoundingBoxTriangle extends BoundingBoxData { type: HitTestType.triangle, - area: Triangle[], + area: TriangleLike[], } export interface BoundingBoxSphere extends BoundingBoxData { type: HitTestType.sphere, area: { - center: vec3, - size: vec3, + center: Vector3, + size: Vector3, }[], } export interface HitTestTriangleParams { type: HitTestType.triangle, - triangles: Triangle[], + triangles: TriangleLike[], backfaceCulling?: boolean, behavior?: spec.InteractBehavior, } export interface HitTestBoxParams { type: HitTestType.box, - center: vec3, - size: vec3, + center: Vector3, + size: Vector3, behavior?: spec.InteractBehavior, } export interface HitTestSphereParams { type: HitTestType.sphere, - center: vec3, + center: Vector3, radius: number, behavior?: spec.InteractBehavior, } export interface HitTestCustomParams { type: HitTestType.custom, - collect (ray: Ray, pointInCanvas: vec2): vec3[] | void, + collect (ray: Ray, pointInCanvas: Vector2): Vector3[] | void, behavior?: spec.InteractBehavior, } export type Region = { + compContent: CompVFXItem, name: string, id: string, - position: vec3, + position: Vector3, behavior?: spec.InteractBehavior, parentId?: string, - hitPositions?: vec3[], + hitPositions?: Vector3[], }; export type HitTestParams = { camera: { - position: vec3, - direction: vec3, - viewProjection: mat4, + position: Vector3, + direction: Vector3, + viewProjection: Matrix4, }, x: number, y: number, - inRect: (position: vec3, width: number, height: number) => boolean, + inRect: (position: Vector3, width: number, height: number) => boolean, }; diff --git a/packages/effects-core/src/plugins/interact/interact-mesh.ts b/packages/effects-core/src/plugins/interact/interact-mesh.ts index c261dffff..01cf2c67f 100644 --- a/packages/effects-core/src/plugins/interact/interact-mesh.ts +++ b/packages/effects-core/src/plugins/interact/interact-mesh.ts @@ -1,13 +1,14 @@ import type * as spec from '@galacean/effects-specification'; +import { Matrix4, Quaternion, Vector2, Vector3, Vector4 } from '@galacean/effects-math/es/core/index'; import { PLAYER_OPTIONS_ENV_EDITOR } from '../../constants'; import { glContext } from '../../gl'; import type { MaterialProps } from '../../material'; import { createShaderWithMarcos, Material, ShaderType } from '../../material'; -import { createValueGetter, mat4create } from '../../math'; import type { MeshRendererOptions } from '../../render'; import { Geometry, GLSLVersion, Mesh } from '../../render'; import type { Transform } from '../../transform'; import type { Engine } from '../../engine'; +import { createValueGetter } from '../../math'; const vertex = ` precision highp float; @@ -78,17 +79,24 @@ export class InteractMesh { updateMesh () { const { material } = this.mesh; - const uSize = material.getVector2('uSize')!.slice(); - const uPos = material.getVector4('uPos')!.slice(); - const uQuat = material.getVector4('uQuat')!.slice(); - const scale = [...this.transform.scale]; - - this.transform.assignWorldTRS(uPos, uQuat, scale); - uSize[0] = scale[0]; - uSize[1] = scale[1]; - material.setVector2('uSize', uSize as spec.vec2); - material.setVector4('uPos', uPos as spec.vec4); - material.setVector4('uQuat', uQuat as spec.vec4); + const uSize = material.getVector2('uSize')!.clone(); + const uPos = material.getVector4('uPos')!.clone(); + + const tempPos = new Vector3(); + const tempQuat = new Quaternion(); + const tempScale = this.transform.scale.clone(); + + this.transform.assignWorldTRS(tempPos, tempQuat, tempScale); + + uSize.x = tempScale.x; + uSize.y = tempScale.y; + uPos.x = tempPos.x; + uPos.y = tempPos.y; + uPos.z = tempPos.z; + + material.setVector2('uSize', uSize); + material.setVector4('uPos', uPos); + material.setQuaternion('uQuat', tempQuat); } private createMaterial (rendererOptions: MeshRendererOptions): Material { @@ -116,10 +124,10 @@ export class InteractMesh { const material = Material.create(this.engine, materialProps); material.depthTest = false; - material.setVector4('uPos', [0, 0, 0, 0]); - material.setVector2('uSize', [1, 1]); - material.setVector4('uColor', [color[0] / 255, color[1] / 255, color[2] / 255, color[3]]); - material.setVector4('uQuat', [0, 0, 0, 0]); + material.setVector4('uPos', new Vector4(0, 0, 0, 0)); + material.setVector2('uSize', new Vector2(1, 1)); + material.setVector4('uColor', new Vector4(color[0] / 255, color[1] / 255, color[2] / 255, color[3])); + material.setQuaternion('uQuat', new Quaternion(0, 0, 0, 0)); return material; } @@ -153,7 +161,7 @@ export class InteractMesh { { name: 'Interact_preview' + seed++, priority: 0, - worldMatrix: mat4create(), + worldMatrix: Matrix4.fromIdentity(), geometry, material, }); diff --git a/packages/effects-core/src/plugins/interact/interact-vfx-item.ts b/packages/effects-core/src/plugins/interact/interact-vfx-item.ts index d78881a23..d334c9c78 100644 --- a/packages/effects-core/src/plugins/interact/interact-vfx-item.ts +++ b/packages/effects-core/src/plugins/interact/interact-vfx-item.ts @@ -1,12 +1,13 @@ import type { vec3 } from '@galacean/effects-specification'; import * as spec from '@galacean/effects-specification'; +import { Vector3, clamp } from '@galacean/effects-math/es/core/index'; import { PLAYER_OPTIONS_ENV_EDITOR } from '../../constants'; import type { EventSystem, TouchEventType } from './event-system'; import type { VFXItemProps } from '../../vfx-item'; import { VFXItem } from '../../vfx-item'; import type { HitTestTriangleParams, BoundingBoxTriangle } from './click-handler'; import { HitTestType } from './click-handler'; -import { clamp, vec3MulMat4, trianglesFromRect } from '../../math'; +import { trianglesFromRect } from '../../math'; import type { Composition } from '../../composition'; import { InteractMesh } from './interact-mesh'; import type { InteractItem } from './interact-item'; @@ -85,12 +86,12 @@ export class InteractVFXItem extends VFXItem { override getBoundingBox (): BoundingBoxTriangle | void { const worldMatrix = this.transform.getWorldMatrix(); - const triangles = trianglesFromRect([0, 0, 0], 0.5, 0.5); + const triangles = trianglesFromRect(Vector3.ZERO, 0.5, 0.5); triangles.forEach(triangle => { - triangle.forEach(p => { - vec3MulMat4(p, p, worldMatrix); - }); + worldMatrix.transformPoint(triangle.p0 as Vector3); + worldMatrix.transformPoint(triangle.p1 as Vector3); + worldMatrix.transformPoint(triangle.p2 as Vector3); }); return { @@ -149,7 +150,7 @@ export class InteractVFXItem extends VFXItem { x: event.x, y: event.y, cameraParam: { - position: camera?.position || [0, 0, 8], + position: camera?.position.toArray() || [0, 0, 8], fov: camera?.fov || 60, }, }; @@ -220,7 +221,7 @@ export class InteractVFXItem extends VFXItem { event.origin?.preventDefault(); } } - this.composition.camera.position = [nx, ny, depth]; + this.composition.camera.position = new Vector3(nx, ny, depth); } } diff --git a/packages/effects-core/src/plugins/particle/particle-loader.ts b/packages/effects-core/src/plugins/particle/particle-loader.ts index 73d3ebf99..2a63241fb 100644 --- a/packages/effects-core/src/plugins/particle/particle-loader.ts +++ b/packages/effects-core/src/plugins/particle/particle-loader.ts @@ -4,7 +4,7 @@ import { createShaderWithMarcos, ShaderType } from '../../material'; import type { Mesh, Renderer, RenderFrame, SharedShaderWithSource } from '../../render'; import { GLSLVersion } from '../../render'; import { addItem, removeItem } from '../../utils'; -import type { VFXItem } from '../../vfx-item'; +import { Item, type VFXItem } from '../../vfx-item'; import { AbstractPlugin } from '../index'; import { getParticleMeshShader, modifyMaxKeyframeShader } from './particle-mesh'; import type { ParticleSystem } from './particle-system'; @@ -69,14 +69,13 @@ export class ParticleLoader extends AbstractPlugin { let maxVertexCount = 0; composition.items.forEach(item => { - // TODO: 待 spec 修改升级后移除 as - if (item.type as spec.ItemType === spec.ItemType.particle) { - items.push(item as spec.ParticleItem); - assignDefValue(item as spec.ParticleItem); + if (Item.isParticle(item)) { + items.push(item); + assignDefValue(item); if (level === 2) { Object.keys(compileOptions).forEach(name => { const content = compileOptions[name]; - const target = (item.content as spec.ParticleContent)[name as keyof spec.ParticleContent]; + const target = item.content[name as keyof spec.ParticleContent]; if (target) { Object.keys(content).forEach(pro => { @@ -193,7 +192,7 @@ export class ParticleLoader extends AbstractPlugin { } function assignDefValue (item: spec.ParticleItem) { - if (item.type === spec.ItemType.particle) { + if (Item.isParticle(item)) { const options = item.content; if (!options.rotationOverLifetime) { diff --git a/packages/effects-core/src/plugins/particle/particle-mesh.ts b/packages/effects-core/src/plugins/particle/particle-mesh.ts index cf92d4bf3..827c30bf9 100644 --- a/packages/effects-core/src/plugins/particle/particle-mesh.ts +++ b/packages/effects-core/src/plugins/particle/particle-mesh.ts @@ -1,4 +1,6 @@ import type * as spec from '@galacean/effects-specification'; +import type { Matrix4 } from '@galacean/effects-math/es/core/index'; +import { Euler, Quaternion, Vector2, Vector3, Vector4 } from '@galacean/effects-math/es/core/index'; import type { Composition } from '../../composition'; import { getConfig, RENDER_PREFER_LOOKUP_TEXTURE } from '../../config'; import { FILTER_NAME_NONE, PLAYER_OPTIONS_ENV_EDITOR } from '../../constants'; @@ -14,14 +16,13 @@ import { setSideMode, ShaderType, } from '../../material'; -import type { mat4, ValueGetter } from '../../math'; +import type { ValueGetter } from '../../math'; import { - calculateTranslation, createKeyFrameMeta, createValueGetter, CurveValue, getKeyFrameMetaByRawValue, - mat4create, + calculateTranslation, } from '../../math'; import type { Attribute, GeometryProps, ShaderMarcos, SharedShaderWithSource, GPUCapability } from '../../render'; import { Geometry, GLSLVersion, Mesh } from '../../render'; @@ -31,17 +32,17 @@ import { Transform } from '../../transform'; import { enlargeBuffer, imageDataFromGradient } from '../../utils'; export type Point = { - vel: spec.vec3, + vel: Vector3, lifetime: number, color: spec.vec4, uv: number[], - dirX: spec.vec3, - dirY: spec.vec3, + dirX: Vector3, + dirY: Vector3, delay: number, sprite?: [start: number, duration: number, cycles: number], transform: Transform, gravity: spec.vec3, - size: spec.vec2, + size: Vector2, }; export interface ParticleMeshData { @@ -93,7 +94,7 @@ export interface ParticleMeshProps extends ParticleMeshData { side: number, filter?: spec.FilterParams, transparentOcclusion?: boolean, - matrix?: mat4, + matrix?: Matrix4, sprite?: { animate?: boolean, blend?: boolean, @@ -115,7 +116,7 @@ export interface ParticleMeshProps extends ParticleMeshData { maxCount: number, shaderCachePrefix: string, name: string, - anchor: spec.vec2, + anchor: Vector2, } export class ParticleMesh implements ParticleMeshData { duration: number; @@ -135,7 +136,7 @@ export class ParticleMesh implements ParticleMeshData { readonly useSprite?: boolean; readonly textureOffsets: number[]; readonly maxCount: number; - readonly anchor: spec.vec2; + readonly anchor: Vector2; constructor ( props: ParticleMeshProps, @@ -149,7 +150,6 @@ export class ParticleMesh implements ParticleMeshData { filter, gravity, forceTarget, side, occlusion, anchor, blending, maskMode, mask, transparentOcclusion, listIndex, meshSlots, renderMode = 0, - matrix = mat4create(), diffuse = Texture.createWithData(engine), } = props; const { detail } = engine.gpuCapability; @@ -410,25 +410,25 @@ export class ParticleMesh implements ParticleMeshData { return; } - const res: spec.vec4[] = []; + const res: Vector4[] = []; switch (typeMap[name]) { case 'vec4': - material.setVector4(name, value); + material.setVector4(name, Vector4.fromArray(value)); break; case 'vec3': - material.setVector3(name, value); + material.setVector3(name, Vector3.fromArray(value)); break; case 'vec2': - material.setVector2(name, value); + material.setVector2(name, Vector2.fromArray(value)); break; case 'vec4Array': for (let i = 0; i < value.length; i = i + 4) { - const v: spec.vec4 = [value[i], value[i + 1], value[i + 2], value[i + 3]]; + const v = new Vector4(value[i], value[i + 1], value[i + 2], value[i + 3]); res.push(v); } @@ -440,7 +440,7 @@ export class ParticleMesh implements ParticleMeshData { console.warn(`uniform ${name}'s type not in typeMap`); } }); - material.setVector3('emissionColor', [0, 0, 0]); + material.setVector3('emissionColor', new Vector3(0, 0, 0)); material.setFloat('emissionIntensity', 0.0); const geometry = Geometry.create(engine, generateGeometryProps(maxCount * 4, this.useSprite, `particle#${name}`)); @@ -469,10 +469,10 @@ export class ParticleMesh implements ParticleMeshData { get time () { const value = this.mesh.material.getVector4('uParams')!; - return value[0]; + return value.x; } set time (v: number) { - this.mesh.material.setVector4('uParams', [+v, this.duration, 0, 0]); + this.mesh.material.setVector4('uParams', new Vector4(+v, this.duration, 0, 0)); } getPointColor (index: number) { @@ -486,7 +486,7 @@ export class ParticleMesh implements ParticleMeshData { * 待废弃 * @deprecated - 使用 `particle-system.getPointPosition` 替代 */ - getPointPosition (index: number): spec.vec3 { + getPointPosition (index: number): Vector3 { const geo = this.geometry; const posIndex = index * 48; const posData = geo.getAttributeData('aPos') as Float32Array; @@ -494,16 +494,19 @@ export class ParticleMesh implements ParticleMeshData { const time = this.time - offsetData[index * 16 + 2]; const pointDur = offsetData[index * 16 + 3]; const mtl = this.mesh.material; - const ret = calculateTranslation([0, 0, 0], this, mtl.getVector4('uAcceleration')!.slice(0, 3) as spec.vec3, time, pointDur, posData, posData, posIndex, posIndex + 3); + const acc = mtl.getVector4('uAcceleration')!.toVector3(); + const pos = Vector3.fromArray(posData, posIndex); + const vel = Vector3.fromArray(posData, posIndex + 3); + const ret = calculateTranslation(new Vector3(), this, acc, time, pointDur, pos, vel); if (this.forceTarget) { const target = mtl.getVector3('uFinalTarget')!; const life = this.forceTarget.curve.getValue(time / pointDur); const dl = 1 - life; - ret[0] = ret[0] * dl + target[0] * life; - ret[1] = ret[1] * dl + target[1] * life; - ret[2] = ret[2] * dl + target[2] * life; + ret.x = ret.x * dl + target.x * life; + ret.y = ret.y * dl + target.y * life; + ret.z = ret.z * dl + target.z * life; } return ret; @@ -577,15 +580,20 @@ export class ParticleMesh implements ParticleMeshData { pointData.aSprite = new Float32Array(12); } - const position: spec.vec3 = [0, 0, 0], rotation: spec.vec3 = [0, 0, 0], quat: spec.vec4 = [0, 0, 0, 1], scale: spec.vec3 = [1, 1, 1]; + const tempPos = new Vector3(); + const tempQuat = new Quaternion(); + const scale = new Vector3(1, 1, 1); + + point.transform.assignWorldTRS(tempPos, tempQuat, scale); + const tempEuler = Transform.getRotation(tempQuat, new Euler()); - point.transform.assignWorldTRS(position, quat, scale); - Transform.getRotation(rotation, quat); + const position = tempPos.toArray(); + const rotation = tempEuler.toArray(); const offsets = this.textureOffsets; const off = [0, 0, point.delay, point.lifetime]; const wholeUV = [0, 0, 1, 1]; - const vel: number[] = point.vel; + const vel = point.vel; const color: number[] = point.color; const sizeOffsets = [-.5, .5, -.5, -.5, .5, .5, .5, -.5]; const seed = Math.random(); @@ -603,7 +611,7 @@ export class ParticleMesh implements ParticleMeshData { const j8 = j * 8; pointData.aPos.set(position, j12); - pointData.aPos.set(vel, j12 + 3); + vel.fill(pointData.aPos, j12 + 3); pointData.aRot.set(rotation, j8); pointData.aRot[j8 + 3] = seed; pointData.aRot.set(color, j8 + 4); @@ -622,12 +630,12 @@ export class ParticleMesh implements ParticleMeshData { } pointData.aOffset.set(off, j4); const ji = (j + j); - const sx = (sizeOffsets[ji] - this.anchor[0]) * scale[0]; - const sy = (sizeOffsets[ji + 1] - this.anchor[1]) * scale[1]; + const sx = (sizeOffsets[ji] - this.anchor.x) * scale.x; + const sy = (sizeOffsets[ji + 1] - this.anchor.y) * scale.y; for (let k = 0; k < 3; k++) { - pointData.aPos[j12 + 6 + k] = point.dirX[k] * sx; - pointData.aPos[j12 + 9 + k] = point.dirY[k] * sy; + pointData.aPos[j12 + 6 + k] = point.dirX.getElement(k) * sx; + pointData.aPos[j12 + 9 + k] = point.dirY.getElement(k) * sy; } } const indexData = new Uint16Array([0, 1, 2, 2, 1, 3].map(x => x + index * 4)); diff --git a/packages/effects-core/src/plugins/particle/particle-system.ts b/packages/effects-core/src/plugins/particle/particle-system.ts index d17eeae94..a6ed090b9 100644 --- a/packages/effects-core/src/plugins/particle/particle-system.ts +++ b/packages/effects-core/src/plugins/particle/particle-system.ts @@ -1,26 +1,10 @@ import * as spec from '@galacean/effects-specification'; import type { vec2, vec3, vec4 } from '@galacean/effects-specification'; +import type { Ray } from '@galacean/effects-math/es/core/index'; +import { Euler, Vector3, Matrix4, Vector2, Vector4 } from '@galacean/effects-math/es/core/index'; import { Link } from './link'; -import type { Ray, mat3, mat4, ValueGetter } from '../../math'; -import { - mat4create, - intersectRaySphere, - createValueGetter, - ensureVec3, - mat3FromRotation, - vec3Cross, - vec3MulMat3, - vec3MulMat4, - vec3RotateByMat4, - vecAdd, - vecAssign, - vecDot, - vecMinus, - vecMulCombine, - vecNormalize, - convertAnchor, - calculateTranslation, -} from '../../math'; +import type { ValueGetter } from '../../math'; +import { ensureVec3, convertAnchor, calculateTranslation, createValueGetter } from '../../math'; import type { ShapeGenerator, ShapeGeneratorOptions } from '../../shape'; import { createShape } from '../../shape'; import { Texture } from '../../texture'; @@ -86,8 +70,8 @@ type ParticleEmissionOptions = { }; interface ParticleTransform { - position: vec3, - rotation?: vec3, + position: Vector3, + rotation?: Euler, path?: ValueGetter, } @@ -157,7 +141,7 @@ export interface ParticleTrailProps extends Omit export type ParticleContent = [number, number, number, Point]; // delay + lifetime, particleIndex, delay, pointData export class ParticleSystem { reusable: boolean; - renderMatrix: mat4; + renderMatrix: Matrix4; particleMesh: ParticleMesh; trailMesh?: TrailMesh; options: ParticleOptions; @@ -179,7 +163,7 @@ export class ParticleSystem { private ended: boolean; private lastEmitTime: number; private frozen: boolean; - private upDirectionWorld: vec3 | null; + private upDirectionWorld: Vector3 | null; private uvs: number[][]; private readonly basicTransform: ParticleTransform; private readonly transform: Transform; @@ -216,7 +200,7 @@ export class ParticleSystem { row: _textureSheetAnimation.row, total: _textureSheetAnimation.total || _textureSheetAnimation.col * _textureSheetAnimation.row, } : undefined; - const renderMatrix = mat4create(); + const renderMatrix = Matrix4.fromIdentity(); const startTurbulence = !!(shape && shape.turbulenceX || shape.turbulenceY || shape.turbulenceZ); let turbulence: ParticleOptions['turbulence']; @@ -284,7 +268,7 @@ export class ParticleSystem { separateAxes: false, x: createValueGetter(('size' in sizeOverLifetime ? sizeOverLifetime.size : sizeOverLifetime.x) || 1), }; - const anchor = convertAnchor(renderer.anchor, renderer.particleOrigin); + const anchor = Vector2.fromArray(convertAnchor(renderer.anchor, renderer.particleOrigin)); this.options = { particleFollowParent: !!options.particleFollowParent, @@ -453,14 +437,14 @@ export class ParticleSystem { this.trailMesh = new TrailMesh(trailMeshProps, engine); } this.transform = vfxItem.transform; - const position = [...this.transform.position] as vec3; - const rotation = [...this.transform.rotation] as vec3; + const position = this.transform.position.clone(); + const rotation = this.transform.rotation.clone(); const transformPath = props.emitterTransform && props.emitterTransform.path; let path; if (transformPath) { if (transformPath[0] === spec.ValueType.CONSTANT_VEC3) { - vecAdd(position, position, transformPath[1]); + position.add(transformPath[1]); } else { path = createValueGetter(transformPath); } @@ -476,7 +460,7 @@ export class ParticleSystem { } this.meshes = meshes; this.reusable = vfxItem.reusable; - this.setVisible(vfxItem.getVisible()); + this.setVisible(vfxItem.contentVisible); const interaction = props.interaction; if (interaction) { @@ -504,21 +488,21 @@ export class ParticleSystem { const parentTransform = this.parentTransform; const { path, position } = this.basicTransform; - const selfPos = [...position]; + const selfPos = position.clone(); if (path) { const duration = this.options.duration; - vecAdd(selfPos, selfPos, path.getValue(time / duration)); + selfPos.add(path.getValue(time / duration)); } - this.transform.setPosition(selfPos[0], selfPos[1], selfPos[2]); + this.transform.setPosition(selfPos.x, selfPos.y, selfPos.z); if (this.options.particleFollowParent && parentTransform) { const tempMatrix = parentTransform.getWorldMatrix(); - this.particleMesh.mesh.worldMatrix = tempMatrix; + this.particleMesh.mesh.worldMatrix = tempMatrix.clone(); if (this.trailMesh) { - this.trailMesh.mesh.worldMatrix = tempMatrix; + this.trailMesh.mesh.worldMatrix = tempMatrix.clone(); } } } @@ -540,7 +524,7 @@ export class ParticleSystem { this.particleMesh.setPoint(point, pointIndex); this.clearPointTrail(pointIndex); if (this.parentTransform && this.trailMesh) { - this.trailMesh.setPointStartPos(pointIndex, this.parentTransform.position); + this.trailMesh.setPointStartPos(pointIndex, this.parentTransform.position.clone()); } } @@ -554,9 +538,9 @@ export class ParticleSystem { setOpacity (opacity: number) { const material = this.particleMesh.mesh.material; const geometry = this.particleMesh.mesh.geometry; - const originalColor = material.getVector4('uOpacityOverLifetimeValue') || [1, 1, 1, 1]; + const originalColor = material.getVector4('uOpacityOverLifetimeValue')?.toArray() || [1, 1, 1, 1]; - material.setVector4('uOpacityOverLifetimeValue', [originalColor[0], originalColor[1], originalColor[2], opacity]); + material.setVector4('uOpacityOverLifetimeValue', new Vector4(originalColor[0], originalColor[1], originalColor[2], opacity)); const data = geometry.getAttributeData('aColor') || []; for (let i = 0; i < data.length; i += 32) { @@ -570,9 +554,9 @@ export class ParticleSystem { setColor (r: number, g: number, b: number, a: number) { const material = this.particleMesh.mesh.material; const geometry = this.particleMesh.mesh.geometry; - const originalColor = material.getVector4('uOpacityOverLifetimeValue') || [1, 1, 1, 1]; + const originalColor = material.getVector4('uOpacityOverLifetimeValue')?.toArray() || [1, 1, 1, 1]; - material.setVector4('uOpacityOverLifetimeValue', [originalColor[0], originalColor[1], originalColor[2], a]); + material.setVector4('uOpacityOverLifetimeValue', new Vector4(originalColor[0], originalColor[1], originalColor[2], a)); const data = geometry.getAttributeData('aColor') || []; for (let i = 0; i < data.length; i += 32) { @@ -770,10 +754,10 @@ export class ParticleSystem { onDestroy () { } - getParticleBoxes (): { center: vec3, size: vec3 }[] { + getParticleBoxes (): { center: Vector3, size: Vector3 }[] { const link = this.particleLink; const mesh = this.particleMesh; - const res: { center: vec3, size: vec3 }[] = []; + const res: { center: Vector3, size: Vector3 }[] = []; const maxCount = this.particleCount; let counter = 0; @@ -810,7 +794,7 @@ export class ParticleSystem { } - raycast (options: ParticleSystemRayCastOptions): vec3[] | undefined { + raycast (options: ParticleSystemRayCastOptions): Vector3[] | undefined { const link = this.particleLink; const mesh = this.particleMesh; @@ -819,7 +803,7 @@ export class ParticleSystem { } let node = link.last; const hitPositions = []; - const temp: vec3 = [0, 0, 0]; + const temp = new Vector3(); let finish = false; if (node && node.content) { @@ -832,7 +816,10 @@ export class ParticleSystem { let pass = false; if (ray) { - pass = !!intersectRaySphere(temp, ray.center, ray.direction, pos, options.radius); + pass = !!ray.intersectSphere({ + center: pos, + radius: options.radius, + }, temp); } if (pass) { if (options.removeParticle) { @@ -871,7 +858,7 @@ export class ParticleSystem { const particleMesh = this.particleMesh; const position = this.getPointPosition(point); const color = trails.inheritParticleColor ? particleMesh.getPointColor(pointIndex) : [1, 1, 1, 1]; - const size: vec3 = point.transform.getWorldScale(); + const size: vec3 = point.transform.getWorldScale().toArray(); let width = 1; let lifetime = trails.lifetime.getValue(emitterLifetime); @@ -883,11 +870,11 @@ export class ParticleSystem { lifetime *= size[0]; } if (trails.parentAffectsPosition && this.parentTransform) { - vecAdd(position, position, this.parentTransform.position); + position.add(this.parentTransform.position); const pos = this.trailMesh.getPointStartPos(pointIndex); if (pos) { - vecMinus(position, position, pos); + position.subtract(pos); } } this.trailMesh.addPoint(pointIndex, position, { @@ -898,30 +885,32 @@ export class ParticleSystem { }); } - getPointPosition (point: Point): spec.vec3 { + getPointPosition (point: Point): Vector3 { const { transform, vel, lifetime, delay, - gravity = [0, 0, 0], + gravity = [], } = point; const forceTarget = this.options.forceTarget; - const pos: vec3 = [0, 0, 0]; const time = this.lastUpdate - delay; - transform.assignWorldTRS(pos); - const ret = calculateTranslation([0, 0, 0], this.options, gravity, time, lifetime, pos, vel); + const tempPos = new Vector3(); + const acc = Vector3.fromArray(gravity); + + transform.assignWorldTRS(tempPos); + const ret = calculateTranslation(new Vector3(), this.options, acc, time, lifetime, tempPos, vel); if (forceTarget) { const target = forceTarget.target || [0, 0, 0]; const life = forceTarget.curve.getValue(time / lifetime); const dl = 1 - life; - ret[0] = ret[0] * dl + target[0] * life; - ret[1] = ret[1] * dl + target[1] * life; - ret[2] = ret[2] * dl + target[2] * life; + ret.x = ret.x * dl + target[0] * life; + ret.y = ret.y * dl + target[1] * life; + ret.z = ret.z * dl + target[2] * life; } return ret; @@ -940,7 +929,7 @@ export class ParticleSystem { const speed = options.startSpeed.getValue(lifetime); const matrix4 = options.particleFollowParent ? this.transform.getMatrix() : this.transform.getWorldMatrix(); // 粒子的位置受发射器的位置影响,自身的旋转和缩放不受影响 - const position = vec3MulMat4([], data.position, matrix4); + const position = matrix4.transformPoint(data.position, new Vector3()); const transform = new Transform({ position, valid: true, @@ -948,29 +937,37 @@ export class ParticleSystem { let direction = data.direction; - direction = vecNormalize(tempDir, vec3RotateByMat4(tempDir, direction, matrix4)); + direction = matrix4.transformNormal(direction, tempDir).normalize(); if (options.startTurbulence && options.turbulence) { for (let i = 0; i < 3; i++) { - tempVec3[i] = options.turbulence[i]!.getValue(lifetime); + tempVec3.setElement(i, options.turbulence[i]!.getValue(lifetime)); } - const mat3 = mat3FromRotation(tempMat3, tempVec3); + tempEuler.setFromVector3(tempVec3.negate()); + const mat4 = tempMat4.setFromEuler(tempEuler); - direction = vecNormalize(tempDir, vec3MulMat3(direction, direction, mat3)); + mat4.transformNormal(direction).normalize(); } - let dirX = tmpDirX; + const dirX = tmpDirX; const dirY = tmpDirY; if (shape.alignSpeedDirection) { - vecAssign(dirY, direction, 3); + dirY.copyFrom(direction); if (!this.upDirectionWorld) { - this.upDirectionWorld = vec3RotateByMat4([], shape.upDirection ?? [0, 0, 1], matrix4); + if (shape.upDirection) { + this.upDirectionWorld = shape.upDirection.clone(); + } else { + this.upDirectionWorld = Vector3.Z.clone(); + } + matrix4.transformNormal(this.upDirectionWorld); + } + dirX.crossVectors(dirY, this.upDirectionWorld).normalize(); + // FIXME: 原先因为有精度问题,这里dirX不是0向量 + if (dirX.isZero()) { + dirX.set(1, 0, 0); } - dirX = vecNormalize(dirX, vec3Cross(dirX, dirY, this.upDirectionWorld)); } else { - dirX[0] = 1; - dirX[1] = dirX[2] = 0; - dirY[1] = 1; - dirY[0] = dirY[2] = 0; + dirX.set(1, 0, 0); + dirY.set(0, 1, 0); } let sprite; const tsa = this.textureSheetAnimation; @@ -981,15 +978,17 @@ export class ParticleSystem { sprite[1] = tsa.animationDuration.getValue(lifetime); sprite[2] = tsa.cycles.getValue(lifetime); } - let rot = tempRot; + const rot = tempRot; if (options.start3DRotation) { // @ts-expect-error - rot = [options.startRotationX.getValue(lifetime), options.startRotationY.getValue(lifetime), options.startRotationZ.getValue(lifetime)]; + rot.set(options.startRotationX.getValue(lifetime), options.startRotationY.getValue(lifetime), options.startRotationZ.getValue(lifetime)); } else if (options.startRotation) { - rot = [0, 0, options.startRotation.getValue(lifetime)]; + rot.set(0, 0, options.startRotation.getValue(lifetime)); + } else { + rot.set(0, 0, 0); } - transform.setRotation(...rot); + transform.setRotation(rot.x, rot.y, rot.z); const color = options.startColor.getValue(lifetime) as number[]; if (color.length === 3) { @@ -998,28 +997,31 @@ export class ParticleSystem { const size = tempSize; if (options.start3DSize) { - size[0] = options.startSizeX!.getValue(lifetime); - size[1] = options.startSizeY!.getValue(lifetime); + size.x = options.startSizeX!.getValue(lifetime); + size.y = options.startSizeY!.getValue(lifetime); } else { const n = options.startSize!.getValue(lifetime); const aspect = options.sizeAspect!.getValue(lifetime); - size[0] = n; + size.x = n; // 兼容aspect为0的情况 - size[1] = aspect === 0 ? 0 : n / aspect; + size.y = aspect === 0 ? 0 : n / aspect; // size[1] = n / aspect; } - const vel: vec3 = [0, 0, 0]; + const vel = direction.clone(); - vecDot(vel, direction, speed); + vel.multiply(speed); // 粒子的大小受发射器父节点的影响 if (!options.particleFollowParent) { - this.transform.assignWorldTRS([], [], tempVec3); - vecMulCombine(size, size, tempVec3 as unknown as vec2); + const tempScale = new Vector3(); + + this.transform.assignWorldTRS(undefined, undefined, tempScale); + size.x *= tempScale.x; + size.y *= tempScale.y; } - transform.setScale(size[0], size[1], 1); + transform.setScale(size.x, size.y, 1); return { size, @@ -1077,14 +1079,15 @@ export class ParticleSystem { } // array performance better for small memory than Float32Array -const tempDir: vec3 = [0, 0, 0]; -const tempSize: vec2 = [0, 0]; -const tempRot: vec3 = [0, 0, 0]; -const tmpDirX: vec3 = [0, 0, 0]; -const tmpDirY: vec3 = [0, 0, 0]; -const tempVec3: vec3 = [0, 0, 0]; +const tempDir = new Vector3(); +const tempSize = new Vector2(); +const tempRot = new Euler(); +const tmpDirX = new Vector3(); +const tmpDirY = new Vector3(); +const tempVec3 = new Vector3(); +const tempEuler = new Euler(); const tempSprite: vec3 = [0, 0, 0]; -const tempMat3 = [] as unknown as mat3; +const tempMat4 = new Matrix4(); function getBurstOffsets (burstOffsets: Record[]): Record { const ret: Record = {}; diff --git a/packages/effects-core/src/plugins/particle/particle-vfx-item.ts b/packages/effects-core/src/plugins/particle/particle-vfx-item.ts index 13f2dd31d..1700c6fb2 100644 --- a/packages/effects-core/src/plugins/particle/particle-vfx-item.ts +++ b/packages/effects-core/src/plugins/particle/particle-vfx-item.ts @@ -1,7 +1,6 @@ import * as spec from '@galacean/effects-specification'; -import type { vec3 } from '@galacean/effects-specification'; +import type { Ray, Vector3 } from '@galacean/effects-math/es/core/index'; import type { Composition } from '../../composition'; -import type { Ray } from '../../math'; import { assertExist, DestroyOptions } from '../../utils'; import { VFXItem } from '../../vfx-item'; import type { BoundingBoxSphere, HitTestCustomParams } from '../interact/click-handler'; @@ -55,9 +54,9 @@ export class ParticleVFXItem extends VFXItem { } } if (hide) { - this.content.setVisible(true); - } else { this.content.setVisible(false); + } else { + this.content.setVisible(true); this.content.onUpdate(dt); } @@ -125,7 +124,7 @@ export class ParticleVFXItem extends VFXItem { if (force || interactParams) { return { type: HitTestType.custom, - collect: (ray: Ray): vec3[] | void => + collect: (ray: Ray): Vector3[] | void => this.content?.raycast({ radius: interactParams?.radius || 0.4, multiple: !!interactParams?.multiple, diff --git a/packages/effects-core/src/plugins/particle/trail-mesh.ts b/packages/effects-core/src/plugins/particle/trail-mesh.ts index f683a14d1..65179ab65 100644 --- a/packages/effects-core/src/plugins/particle/trail-mesh.ts +++ b/packages/effects-core/src/plugins/particle/trail-mesh.ts @@ -1,5 +1,7 @@ import type * as spec from '@galacean/effects-specification'; import type { vec3, vec4, GradientStop } from '@galacean/effects-specification'; +import { Vector2, Vector3, Vector4 } from '@galacean/effects-math/es/core/index'; +import type { Matrix4 } from '@galacean/effects-math/es/core/index'; import { getConfig, RENDER_PREFER_LOOKUP_TEXTURE } from '../../config'; import { PLAYER_OPTIONS_ENV_EDITOR } from '../../constants'; import { glContext } from '../../gl'; @@ -12,16 +14,11 @@ import { setMaskMode, ShaderType, } from '../../material'; -import type { mat4, ValueGetter } from '../../math'; import { createKeyFrameMeta, createValueGetter, CurveValue, getKeyFrameMetaByRawValue, - vecAdd, - vecMinus, - vecNormalize, - vecSquareDistance, } from '../../math'; import type { GeometryProps, ShaderMarcos, ShaderWithSource, GPUCapability } from '../../render'; import { Geometry, GLSLVersion, Mesh } from '../../render'; @@ -29,6 +26,7 @@ import { particleFrag, trailVert } from '../../shader'; import { generateHalfFloatTexture, Texture } from '../../texture'; import { imageDataFromGradient } from '../../utils'; import type { Engine } from '../../engine'; +import type { ValueGetter } from '../../math'; export type TrailMeshConstructor = { maxTrailCount: number, @@ -40,7 +38,7 @@ export type TrailMeshConstructor = { widthOverTrail: ValueGetter, colorOverTrail?: Array, order: number, - matrix?: mat4, + matrix?: Matrix4, opacityOverLifetime: ValueGetter, occlusion: boolean, transparentOcclusion: boolean, @@ -59,8 +57,8 @@ type TrailPointOptions = { time: number, }; -const tmp0: vec3 = [0, 0, 0]; -const tmp1: vec3 = [0, 0, 0]; +const tmp0 = new Vector3(); +const tmp1 = new Vector3(); export class TrailMesh { mesh: Mesh; @@ -72,7 +70,7 @@ export class TrailMesh { useAttributeTrailStart: boolean; checkVertexDistance: boolean; - private pointStart: vec3[] = []; + private pointStart: Vector3[] = []; private trailCursors: Uint16Array; // TODO: engine 挪到第一个参数 @@ -231,24 +229,24 @@ export class TrailMesh { } else if (name === 'uTrailStart') { material.setFloats('uTrailStart', value); } else if (name === 'uVCurveValues') { - const array: vec4[] = []; + const array: Vector4[] = []; - array.push([value[0], value[1], value[2], value[3]]); - array.push([value[4], value[5], value[6], value[7]]); + array.push(new Vector4(value[0], value[1], value[2], value[3])); + array.push(new Vector4(value[4], value[5], value[6], value[7])); material.setVector4Array(name, array); } else { - material.setVector4(name, value); + material.setVector4(name, Vector4.fromArray(value)); } }); material.setFloat('uTime', 0); // TODO: 修改下长度 - material.setVector4('uWidthOverTrail', uWidthOverTrail as unknown as vec4); - material.setVector2('uTexOffset', [0, 0]); - material.setVector4('uTextureMap', textureMap); - material.setVector4('uParams', [0, pointCountPerTrail - 1, 0, 0]); + material.setVector4('uWidthOverTrail', Vector4.fromArray(uWidthOverTrail)); + material.setVector2('uTexOffset', new Vector2(0, 0)); + material.setVector4('uTextureMap', Vector4.fromArray(textureMap)); + material.setVector4('uParams', new Vector4(0, pointCountPerTrail - 1, 0, 0)); material.setTexture('uMaskTex', uMaskTex); - material.setVector4('uColorParams', [texture ? 1 : 0, +preMulAlpha, 0, +(occlusion && !transparentOcclusion)]); + material.setVector4('uColorParams', new Vector4(texture ? 1 : 0, +preMulAlpha, 0, +(occlusion && !transparentOcclusion))); this.maxTrailCount = maxTrailCount; this.pointCountPerTrail = pointCountPerTrail; @@ -270,7 +268,7 @@ export class TrailMesh { this.mesh.material.setFloat('uTime', t ?? 0); } - addPoint (trailIndex: number, position: vec3, opt: TrailPointOptions) { + addPoint (trailIndex: number, position: Vector3, opt: TrailPointOptions) { opt = opt || ({} as TrailPointOptions); let cursor = this.trailCursors[trailIndex]; const pointCountPerTrail = this.pointCountPerTrail; @@ -282,7 +280,7 @@ export class TrailMesh { const previousPoint = this.getTrailPosition(trailIndex, previousIndex, tmp0); // point too close - if (previousPoint && this.checkVertexDistance && vecSquareDistance(previousPoint, position) < this.minimumVertexDistance) { + if (previousPoint && this.checkVertexDistance && previousPoint?.distanceSquared(position) < this.minimumVertexDistance) { return; } @@ -303,24 +301,25 @@ export class TrailMesh { const color = opt.color || [1, 1, 1, 1]; const colorData = new Float32Array(24); + const positionData = position.toArray(); colorData.set(color, 0); colorData.set(info, 4); colorData[7] = 0; - colorData.set(position, 8); + colorData.set(positionData, 8); colorData[11] = 0.5 * size; colorData.set(color, 12); colorData.set(info, 16); colorData[19] = 1; - colorData.set(position, 20); + colorData.set(positionData, 20); colorData[23] = -0.5 * size; geometry.setAttributeSubData('aColor', pointStartIndex * 24, colorData); if (previousIndex >= 0) { - const bPreviousPoint = this.getTrailPosition(trailIndex, bpreviousIndex, tmp1) as vec3; - const previousDir = new Float32Array(calculateDirection(bPreviousPoint, previousPoint as vec3, position)); + const bPreviousPoint = this.getTrailPosition(trailIndex, bpreviousIndex, tmp1) as Vector3; + const previousDir = new Float32Array(calculateDirection(bPreviousPoint, previousPoint as Vector3, position)); const previousDirStartIndex = (trailIndex * pointCountPerTrail + previousIndex) * 6; geometry.setAttributeSubData('aDir', previousDirStartIndex, previousDir); @@ -362,21 +361,21 @@ export class TrailMesh { } if (params) { - params[1] = Math.max(params[1], cursor - 1) - Math.max(0, cursor - pointCountPerTrail); + params.y = Math.max(params.y, cursor - 1) - Math.max(0, cursor - pointCountPerTrail); mtl.setVector4('uParams', params); } } - getTrailPosition (trail: number, index: number, out: vec3): vec3 | undefined { + getTrailPosition (trail: number, index: number, out: Vector3): Vector3 | undefined { const pointCountPerTrail = this.pointCountPerTrail; if (index >= 0 && index < pointCountPerTrail) { const startIndex = (trail * pointCountPerTrail + index) * 24 + 8; const data = this.geometry.getAttributeData('aColor')!; - out[0] = data[startIndex]; - out[1] = data[1 + startIndex]; - out[2] = data[2 + startIndex]; + out.x = data[startIndex]; + out.y = data[1 + startIndex]; + out.z = data[2 + startIndex]; return out; } @@ -418,7 +417,7 @@ export class TrailMesh { return this.pointStart[index]; } - setPointStartPos (index: number, pos: vec3) { + setPointStartPos (index: number, pos: Vector3) { this.pointStart[index] = pos; } @@ -427,26 +426,28 @@ export class TrailMesh { } -const tempDir: vec3 = [0, 0, 0]; -const tempDa: vec3 = [0, 0, 0]; -const tempDb: vec3 = [0, 0, 0]; +const tempDir = new Vector3(); +const tempDa = new Vector3(); +const tempDb = new Vector3(); -function calculateDirection (prePoint: vec3 | undefined, point: vec3, nextPoint?: vec3): vec3 { - const dir: vec3 = tempDir; +function calculateDirection (prePoint: Vector3 | undefined, point: Vector3, nextPoint?: Vector3): vec3 { + const dir = tempDir; if (!prePoint && !nextPoint) { return [0, 0, 0]; } else if (!prePoint) { - vecMinus(dir, nextPoint!, point); + dir.subtractVectors(nextPoint!, point); } else if (!nextPoint) { - vecMinus(dir, point, prePoint); + dir.subtractVectors(point, prePoint); } else { - vecNormalize(tempDa, vecMinus(tempDa, point, prePoint)); - vecNormalize(tempDb, vecMinus(tempDa, nextPoint, point)); - vecAdd(dir, tempDa, tempDb); + tempDa.subtractVectors(point, prePoint).normalize(); + // FIXME: 这里有bug。。。 + tempDa.subtractVectors(nextPoint, point); + tempDb.copyFrom(tempDa).normalize(); + dir.addVectors(tempDa, tempDb); } - return vecNormalize(dir, dir); + return dir.normalize().toArray(); } export function getTrailMeshShader (trails: spec.ParticleTrail, particleMaxCount: number, name: string, env = '', gpuCapability: GPUCapability): ShaderWithSource { diff --git a/packages/effects-core/src/plugins/sprite/filter-sprite-vfx-item.ts b/packages/effects-core/src/plugins/sprite/filter-sprite-vfx-item.ts index 308639272..6ec552e49 100644 --- a/packages/effects-core/src/plugins/sprite/filter-sprite-vfx-item.ts +++ b/packages/effects-core/src/plugins/sprite/filter-sprite-vfx-item.ts @@ -1,5 +1,5 @@ -import type { mat4, vec4 } from '@galacean/effects-specification'; import * as spec from '@galacean/effects-specification'; +import { Matrix4, Vector4 } from '@galacean/effects-math/es/core/index'; import type { FilterDefine } from '../../filter'; import { createFilter } from '../../filter'; import type { Composition } from '../../composition'; @@ -40,9 +40,9 @@ export class FilterSpriteVFXItem extends SpriteVFXItem { const value = variables[key](lifetime); if ((value as Float32Array).length > 4) { - material.setMatrix(key, value as mat4); + material.setMatrix(key, Matrix4.fromArray(value as spec.mat4)); } else { - material.setVector4(key, value as vec4); + material.setVector4(key, Vector4.fromArray(value as spec.vec4)); } } } diff --git a/packages/effects-core/src/plugins/sprite/sprite-group.ts b/packages/effects-core/src/plugins/sprite/sprite-group.ts index ecf097e81..2d33a0dde 100644 --- a/packages/effects-core/src/plugins/sprite/sprite-group.ts +++ b/packages/effects-core/src/plugins/sprite/sprite-group.ts @@ -1,7 +1,7 @@ -import * as spec from '@galacean/effects-specification'; import type { Disposable } from '../../utils'; import { addItem, addItemWithOrder, removeItem, DestroyOptions } from '../../utils'; -import type { VFXItem } from '../../vfx-item'; +import type { VFXItemContent } from '../../vfx-item'; +import { VFXItem } from '../../vfx-item'; import type { Composition } from '../../composition'; import type { Mesh } from '../../render'; import type { Texture } from '../../texture'; @@ -685,7 +685,7 @@ export class SpriteGroup implements Disposable { if ( current.cacheId === cacheId && - item.type === spec.ItemType.sprite && + VFXItem.isSprite(item) && current.items.length < maxSpriteMeshItemCount && (texInc + current.textures.length) <= maxSpriteTextureCount ) { @@ -719,8 +719,6 @@ export class SpriteGroup implements Disposable { } } -function isSprite (item: VFXItem) { - const type = item.type; - - return type === spec.ItemType.sprite || type === spec.ItemType.filter; +function isSprite (item: VFXItem) { + return VFXItem.isSprite(item) || VFXItem.isFilterSprite(item); } diff --git a/packages/effects-core/src/plugins/sprite/sprite-item.ts b/packages/effects-core/src/plugins/sprite/sprite-item.ts index f6df9815c..589f05dcb 100644 --- a/packages/effects-core/src/plugins/sprite/sprite-item.ts +++ b/packages/effects-core/src/plugins/sprite/sprite-item.ts @@ -1,14 +1,9 @@ +import type { Vector3 } from '@galacean/effects-math/es/core/index'; import * as spec from '@galacean/effects-specification'; import type { vec2, vec4, TypedArray, TextureSheetAnimation } from '@galacean/effects-specification'; import type { FilterDefine } from '../../filter'; import type { ValueGetter } from '../../math'; -import { - convertAnchor, - createValueGetter, - vecAdd, - vecFill, - vecMulCombine, -} from '../../math'; +import { vecFill, vecMulCombine, convertAnchor, createValueGetter } from '../../math'; import type { GeometryFromShape } from '../../shape'; import type { Texture } from '../../texture'; import { colorStopsFromGradient, getColorFromGradientStops } from '../../utils'; @@ -138,9 +133,9 @@ export class SpriteItem extends CalculateItem { // 兼容旧JSON(anchor和particleOrigin可能同时存在) if (!renderer.anchor && renderer.particleOrigin !== undefined) { - vecAdd(this.basicTransform.position, this.basicTransform.position, [-realAnchor[0] * scale[0], -realAnchor[1] * scale[1], 0]); + this.basicTransform.position.add([-realAnchor[0] * scale.x, -realAnchor[1] * scale.y, 0]); } - this.transform.setAnchor(realAnchor[0] * scale[0], realAnchor[1] * scale[1], 0); + this.transform.setAnchor(realAnchor[0] * scale.x, realAnchor[1] * scale.y, 0); const colorOverLifetime = props.colorOverLifetime; @@ -294,16 +289,16 @@ export class SpriteItem extends CalculateItem { } else if (init) { ret.texOffset = [0, 0, 1, 1]; } - ret.visible = this.visible; + ret.visible = this.vfxItem.contentVisible; // 图层元素作为父节点时,除了k的大小变换,自身的尺寸也需要传递给子元素,子元素可以通过startSize读取 ret.startSize = this.startSize; return ret; } - protected override calculateScaling (sizeChanged: boolean, sizeInc: spec.vec3, init?: boolean) { + protected override calculateScaling (sizeChanged: boolean, sizeInc: Vector3, init?: boolean) { if (sizeChanged || init) { - this.transform.setScale(sizeInc[0], sizeInc[1], sizeInc[2]); + this.transform.setScale(sizeInc.x, sizeInc.y, sizeInc.z); } } diff --git a/packages/effects-core/src/plugins/sprite/sprite-loader.ts b/packages/effects-core/src/plugins/sprite/sprite-loader.ts index 7ffab3cd6..61dd828d7 100644 --- a/packages/effects-core/src/plugins/sprite/sprite-loader.ts +++ b/packages/effects-core/src/plugins/sprite/sprite-loader.ts @@ -2,7 +2,7 @@ import * as spec from '@galacean/effects-specification'; import type { Composition } from '../../composition'; import type { SpriteItem } from '../index'; import { AbstractPlugin } from '../index'; -import type { VFXItem } from '../../vfx-item'; +import { Item, type VFXItem } from '../../vfx-item'; import type { LayerInfo } from './sprite-group'; import { SpriteGroup } from './sprite-group'; import type { RenderFrame, Renderer, RenderPassSplitOptions } from '../../render'; @@ -36,10 +36,9 @@ export class SpriteLoader extends AbstractPlugin { let hasFilter = false; compositions[0]?.items.forEach(item => { - // TODO: 待 spec 修改升级后移除 as - if (item.type as spec.ItemType === spec.ItemType.filter && (item as spec.FilterItem).content.filter) { + if (Item.isFilter(item) && item.content.filter) { hasFilter = true; - const shaderDefs = createFilterShaders((item as spec.FilterItem).content.filter); + const shaderDefs = createFilterShaders(item.content.filter); shaderDefs.forEach(function (def) { if (!def.isParticle) { @@ -68,13 +67,16 @@ export class SpriteLoader extends AbstractPlugin { const spriteGroup: SpriteGroup = composition.loaderData.spriteGroup; spriteGroup.dispose(); + delete composition.loaderData.spriteGroup; } override onCompositionReset (composition: Composition, pipeline: RenderFrame) { - const spriteGroup = new SpriteGroup(composition); + if (!composition.loaderData.spriteGroup) { + const spriteGroup = new SpriteGroup(composition); - composition.loaderData.spriteGroup = spriteGroup; - spriteGroup.resetMeshSplits(); + composition.loaderData.spriteGroup = spriteGroup; + spriteGroup.resetMeshSplits(); + } } override onCompositionItemLifeBegin (composition: Composition, item: VFXItem) { diff --git a/packages/effects-core/src/plugins/sprite/sprite-mesh.ts b/packages/effects-core/src/plugins/sprite/sprite-mesh.ts index 96d5afbe5..5caedd896 100644 --- a/packages/effects-core/src/plugins/sprite/sprite-mesh.ts +++ b/packages/effects-core/src/plugins/sprite/sprite-mesh.ts @@ -1,4 +1,5 @@ import type * as spec from '@galacean/effects-specification'; +import { Matrix4, Quaternion, Vector2, Vector3, Vector4 } from '@galacean/effects-math/es/core/index'; import type { Composition } from '../../composition'; import { PLAYER_OPTIONS_ENV_EDITOR, SPRITE_VERTEX_STRIDE } from '../../constants'; import type { FilterShaderDefine } from '../../filter'; @@ -14,7 +15,6 @@ import { ShaderType, } from '../../material'; import type { ValueGetter } from '../../math'; -import { mat4create } from '../../math'; import type { GeometryDrawMode, GPUCapabilityDetail, SharedShaderWithSource } from '../../render'; import { Geometry, GLSLVersion, Mesh } from '../../render'; import { itemFrag, itemFrameFrag, itemVert } from '../../shader'; @@ -28,7 +28,7 @@ export type SpriteRenderData = { life: number, transform: Transform, visible?: boolean, - startSize?: spec.vec3, + startSize?: Vector3, color?: spec.vec4, texOffset?: spec.vec4, active?: boolean, @@ -76,7 +76,7 @@ export class SpriteMesh { { name: 'MSprite' + seed++, priority: 0, - worldMatrix: mat4create(), + worldMatrix: Matrix4.fromIdentity(), geometry, material, }); @@ -91,7 +91,7 @@ export class SpriteMesh { let pointCount = 0; if (!items.length) { - this.mesh.setVisible(true); + this.mesh.setVisible(false); return true; } @@ -157,7 +157,7 @@ export class SpriteMesh { geometry.setIndexData(indexData); geometry.setAttributeData('aPoint', bundle.aPoint); geometry.setDrawCount(indexLen); - this.mesh.setVisible(!geometry.getDrawCount()); + this.mesh.setVisible(!!geometry.getDrawCount()); this.mesh.priority = items[0].listIndex; for (let i = 0; i < textures.length; i++) { const texture = textures[i]; @@ -195,21 +195,33 @@ export class SpriteMesh { const uQuatStart = start + 8; const uColorStart = start + 12; - if (!selfData.visible) { + if (!selfData.visible && !init) { mainDataArray[uSizeStart + 2] = -1; return; } - const uPos = [0, 0, 0, 0]; - const uSize = [1, 1, 1, 0]; - const uQuat = [0, 0, 1, 1]; const uColor = selfData.color || [mainDataArray[uColorStart], mainDataArray[uColorStart + 1], mainDataArray[uColorStart + 2], mainDataArray[uColorStart + 3]]; + // if (selfData.startSize) { + // selfData.transform.scaleBy(1 / selfData.startSize[0], 1 / selfData.startSize[1], 1); + // } + + const tempPos = new Vector3(); + const tempQuat = new Quaternion(); + const tempScale = new Vector3(); + + selfData.transform.assignWorldTRS(tempPos, tempQuat, tempScale); + + const uPos = [...tempPos.toArray(), 0]; + const uSize = [...tempScale.toArray(), 0]; + const uQuat = tempQuat.toArray(); + if (!isNaN(item.getCustomOpacity())) { uColor[3] = item.getCustomOpacity(); } - selfData.transform.assignWorldTRS(uPos, uQuat, uSize); + + // selfData.transform.assignWorldTRS(uPos, uQuat, uSize); /* 要过包含父节点颜色/透明度变化的动画的帧对比 打开这段兼容代码 */ // vecMulCombine(uColor, selfData.color, parentData.color); @@ -375,23 +387,23 @@ export class SpriteMesh { } else if (typeof value === 'number') { material.setFloat(key, value); } else if ((value as number[]).length === 2) { - material.setVector2(key, value as spec.vec2); + material.setVector2(key, Vector2.fromArray(value as spec.vec2)); } else if ((value as number[]).length === 4) { - material.setVector4(key, value as spec.vec4); + material.setVector4(key, Vector4.fromArray(value as spec.vec4)); } else { - material.setMatrix(key, value as spec.mat4); + material.setMatrix(key, Matrix4.fromArray(value as spec.mat4)); } } } - const uMainData: spec.mat4[] = []; - const uTexParams: spec.vec4[] = []; - const uTexOffset: spec.vec4[] = []; + const uMainData: Matrix4[] = []; + const uTexParams: Vector4[] = []; + const uTexOffset: Vector4[] = []; for (let i = 0; i < count; i++) { - uMainData.push(mat4create()); - uTexParams.push([0, 0, 0, 0]); - uTexOffset.push([0, 0, 0, 0]); + uMainData.push(Matrix4.fromIdentity()); + uTexParams.push(new Vector4()); + uTexOffset.push(new Vector4()); } if (!material.hasUniform('uMainData')) { material.setMatrixArray('uMainData', uMainData); @@ -408,7 +420,7 @@ export class SpriteMesh { getItemGeometryData (item: SpriteItem, aIndex: number) { const { splits, renderer, textureSheetAnimation, startSize } = item; - const [sx, sy] = startSize; + const { x: sx, y: sy } = startSize; if (renderer.shape) { const { index, aPoint } = renderer.shape; diff --git a/packages/effects-core/src/plugins/sprite/sprite-vfx-item.ts b/packages/effects-core/src/plugins/sprite/sprite-vfx-item.ts index bf5534ba3..349d1d5df 100644 --- a/packages/effects-core/src/plugins/sprite/sprite-vfx-item.ts +++ b/packages/effects-core/src/plugins/sprite/sprite-vfx-item.ts @@ -1,6 +1,5 @@ import * as spec from '@galacean/effects-specification'; -import type { vec3 } from '@galacean/effects-specification'; -import { trianglesFromRect, vec3MulMat4 } from '../../math'; +import { Vector3 } from '@galacean/effects-math/es/core/index'; import { VFXItem } from '../../vfx-item'; import type { Composition } from '../../composition'; import type { HitTestTriangleParams, BoundingBoxTriangle } from '../interact/click-handler'; @@ -10,6 +9,7 @@ import type { SpriteItemProps } from './sprite-item'; import { SpriteItem } from './sprite-item'; import type { SpriteRenderData } from './sprite-mesh'; import { SpriteMesh } from './sprite-mesh'; +import { trianglesFromRect } from '../../math'; export class SpriteVFXItem extends VFXItem { override composition: Composition; @@ -25,30 +25,22 @@ export class SpriteVFXItem extends VFXItem { } override onLifetimeBegin (composition: Composition, content: SpriteItem) { - this._contentVisible = true; content.active = true; } override onItemRemoved (composition: Composition, content?: SpriteItem) { - this._contentVisible = false; if (content) { delete content.mesh; composition.destroyTextures(content.getTextures()); } } - override handleVisibleChanged (visible: boolean) { - if (this.content) { - this.content.visible = visible; - } - } - override onItemUpdate (dt: number, lifetime: number) { this.content?.updateTime(this.time); } override getCurrentPosition () { - const pos: vec3 = [0, 0, 0]; + const pos = new Vector3(); this.transform.assignWorldTRS(pos); @@ -78,12 +70,12 @@ export class SpriteVFXItem extends VFXItem { } const worldMatrix = this.transform.getWorldMatrix(); const size = item.startSize; - const triangles = trianglesFromRect([0, 0, 0], size[0] / 2, size[1] / 2); + const triangles = trianglesFromRect(Vector3.ZERO, size.x / 2, size.y / 2); triangles.forEach(triangle => { - triangle.forEach(p => { - vec3MulMat4(p, p, worldMatrix); - }); + worldMatrix.transformPoint(triangle.p0 as Vector3); + worldMatrix.transformPoint(triangle.p1 as Vector3); + worldMatrix.transformPoint(triangle.p2 as Vector3); }); return { @@ -125,7 +117,7 @@ export class SpriteVFXItem extends VFXItem { const spMesh = new SpriteMesh(this.composition.getEngine(), { wireframe: true, ...item.renderInfo }, this.composition); spMesh.setItems([item]); - spMesh.mesh.material.setVector3('uFrameColor', [color[0], color[1], color[2]]); + spMesh.mesh.material.setVector3('uFrameColor', Vector3.fromArray(color)); spMesh.mesh.priority = 999; return spMesh; diff --git a/packages/effects-core/src/plugins/text/text-item.ts b/packages/effects-core/src/plugins/text/text-item.ts index 592514fbc..c1c245fed 100644 --- a/packages/effects-core/src/plugins/text/text-item.ts +++ b/packages/effects-core/src/plugins/text/text-item.ts @@ -1,5 +1,4 @@ import * as spec from '@galacean/effects-specification'; -import type { TextureDataType } from '../../texture'; import { Texture } from '../../texture'; import { TextMesh } from './text-mesh'; import type { TextVFXItem } from './text-vfx-item'; @@ -49,7 +48,6 @@ export class TextItem extends SpriteItem { ) { super(props as unknown as SpriteItemProps, opts, vfxItem as unknown as SpriteVFXItem); const { options } = props; - const basicScale = this.basicTransform.scale; this.canvas = canvasPool.getCanvas(); canvasPool.saveCanvas(this.canvas); @@ -58,19 +56,17 @@ export class TextItem extends SpriteItem { this.engine = vfxItem.composition.getEngine(); this.textStyle = new TextStyle(options); - this.textLayout = new TextLayout(options, basicScale); + this.textLayout = new TextLayout(options); this.text = options.text; // Text this.mesh = new TextMesh(this.engine, this.renderInfo, vfxItem.composition) as unknown as SpriteMesh; - this.mesh.setItems([this]); - this.updateTexture(); } /** * 设置字号大小 - * @param value 字号 + * @param value - 字号 * @returns */ setFontSize (value: number): void { @@ -86,7 +82,7 @@ export class TextItem extends SpriteItem { /** * 设置字重 - * @param value 字重类型 + * @param value - 字重类型 * @returns */ setFontWeight (value: spec.TextWeight): void { @@ -112,7 +108,7 @@ export class TextItem extends SpriteItem { /** * 设置文本 - * @param value 文本内容 + * @param value - 文本内容 * @returns */ setText (value: string): void { @@ -125,7 +121,7 @@ export class TextItem extends SpriteItem { /** * 设置文本水平布局 - * @param value 布局选项 + * @param value - 布局选项 * @returns */ setTextAlign (value: spec.TextAlignment): void { @@ -138,7 +134,7 @@ export class TextItem extends SpriteItem { /** * 设置文本垂直布局 - * @param value 布局选项 + * @param value - 布局选项 * @returns */ setTextBaseline (value: spec.TextBaseline): void { @@ -151,7 +147,7 @@ export class TextItem extends SpriteItem { /** * 设置文本颜色 - * @param value 颜色内容 + * @param value - 颜色内容 * @returns */ setTextColor (value: spec.RGBAColorValue): void { @@ -164,7 +160,7 @@ export class TextItem extends SpriteItem { /** * 设置文本字体 - * @param value 文本字体 + * @param value - 文本字体 * @returns */ setFontFamily (value: string): void { @@ -177,7 +173,7 @@ export class TextItem extends SpriteItem { /** * 设置外描边文本颜色 - * @param value 颜色内容 + * @param value - 颜色内容 * @returns */ setOutlineColor (value: spec.RGBAColorValue): void { @@ -190,7 +186,7 @@ export class TextItem extends SpriteItem { /** * 设置外描边文本宽度 - * @param value 外描边宽度 + * @param value - 外描边宽度 * @returns */ setOutlineWidth (value: number): void { @@ -203,7 +199,7 @@ export class TextItem extends SpriteItem { /** * 设置阴影模糊 - * @param value 阴影模糊强度 + * @param value - 阴影模糊强度 * @returns */ setShadowBlur (value: number): void { @@ -216,7 +212,7 @@ export class TextItem extends SpriteItem { /** * 设置阴影颜色 - * @param value 阴影颜色 + * @param value - 阴影颜色 * @returns */ setShadowColor (value: spec.RGBAColorValue): void { @@ -229,7 +225,7 @@ export class TextItem extends SpriteItem { /** * 设置阴影水平偏移距离 - * @param value 水平偏移距离 + * @param value - 水平偏移距离 * @returns */ setShadowOffsetX (value: number): void { @@ -242,7 +238,7 @@ export class TextItem extends SpriteItem { /** * 设置阴影水平偏移距离 - * @param value 水平偏移距离 + * @param value - 水平偏移距离 * @returns */ setShadowOffsetY (value: number): void { @@ -255,7 +251,7 @@ export class TextItem extends SpriteItem { /** * 设置字体清晰度 - * @param value 字体清晰度 + * @param value - 字体清晰度 * @returns */ setFontScale (value: number): void { @@ -286,7 +282,7 @@ export class TextItem extends SpriteItem { const fontSize = style.fontSize * fontScale; const lineHeight = layout.lineHeight * fontScale; - this.char = this.text.split(''); + this.char = (this.text || '').split(''); this.canvas.width = width ; this.canvas.height = height; @@ -436,5 +432,4 @@ export class TextItem extends SpriteItem { context!.shadowOffsetX = style.shadowOffsetX ; context!.shadowOffsetY = -style.shadowOffsetY ; } - } diff --git a/packages/effects-core/src/plugins/text/text-layout.ts b/packages/effects-core/src/plugins/text/text-layout.ts index dda90351a..c7170377f 100644 --- a/packages/effects-core/src/plugins/text/text-layout.ts +++ b/packages/effects-core/src/plugins/text/text-layout.ts @@ -24,7 +24,7 @@ export class TextLayout { */ lineHeight: number; - constructor (options: spec.TextContentOptions, basicScale: spec.vec3) { + constructor (options: spec.TextContentOptions) { const { textHeight = 100, textWidth = 100, textOverflow = spec.TextOverflow.display, textBaseline = spec.TextBaseline.top, textAlign = spec.TextAlignment.left, text, letterSpace = 0, autoWidth = false, fontSize, lineHeight = fontSize } = options; const tempWidth = fontSize + letterSpace; diff --git a/packages/effects-core/src/plugins/text/text-loader.ts b/packages/effects-core/src/plugins/text/text-loader.ts index 5949c8f46..469d30263 100644 --- a/packages/effects-core/src/plugins/text/text-loader.ts +++ b/packages/effects-core/src/plugins/text/text-loader.ts @@ -10,7 +10,7 @@ export class TextLoader extends AbstractPlugin { override name = 'text'; addItems: TextVFXItem[] = []; removeItems: TextVFXItem[] = []; - public readonly meshes: Mesh[] = []; // meshSplits对应的mesh数组 每次diff后更新 + public readonly meshes: Mesh[] = []; override onCompositionDestroyed (composition: Composition) { if (composition.reusable) { @@ -26,11 +26,18 @@ export class TextLoader extends AbstractPlugin { override onCompositionUpdate (composition: Composition, dt: number): void { this.addItems.forEach(item => { + if (!item.contentVisible) { + item.content.mesh?.mesh.setVisible(false); + + return; + } else { + item.content.mesh?.mesh.setVisible(true); + } item.content.updateTexture(); if (!item.content.ended && item.content.mesh) { item.content.mesh.updateItem(item.content); + item.content.mesh?.applyChange(); } - item.content.mesh?.applyChange(); }); } @@ -50,6 +57,7 @@ export class TextLoader extends AbstractPlugin { } override onCompositionItemRemoved (composition: Composition, item: VFXItem) { + // FIXME: 此处判断有问题,item 应该先判断 if (item instanceof TextVFXItem && item) { addItem(this.removeItems, item); if (this.addItems.includes(item)) { @@ -69,7 +77,6 @@ export class TextLoader extends AbstractPlugin { removeItem(this.addItems, item); item.content.mesh.mesh.dispose({ material: { textures: DestroyOptions.keep } }); item.dispose(); - } }); diff --git a/packages/effects-core/src/plugins/text/text-mesh.ts b/packages/effects-core/src/plugins/text/text-mesh.ts index 00579bb3a..837c14acb 100644 --- a/packages/effects-core/src/plugins/text/text-mesh.ts +++ b/packages/effects-core/src/plugins/text/text-mesh.ts @@ -12,7 +12,7 @@ export class TextMesh extends SpriteMesh { */ override getItemGeometryData (item: SpriteItem, aIndex: number) { const { splits, renderer, textureSheetAnimation, startSize, textLayout } = item as TextItem; - const [sx, sy] = startSize; + const { x: sx, y: sy } = startSize; if (renderer.shape) { const { index, aPoint } = renderer.shape; diff --git a/packages/effects-core/src/plugins/text/text-vfx-item.ts b/packages/effects-core/src/plugins/text/text-vfx-item.ts index edba8ad23..308903c59 100644 --- a/packages/effects-core/src/plugins/text/text-vfx-item.ts +++ b/packages/effects-core/src/plugins/text/text-vfx-item.ts @@ -1,6 +1,6 @@ import * as spec from '@galacean/effects-specification'; -import type { vec3 } from '@galacean/effects-specification'; -import { trianglesFromRect, vec3MulMat4 } from '../../math'; +import { Vector3 } from '@galacean/effects-math/es/core/index'; +import { trianglesFromRect } from '../../math'; import { VFXItem } from '../../vfx-item'; import type { Composition } from '../../composition'; import type { HitTestTriangleParams, BoundingBoxTriangle } from '../interact/click-handler'; @@ -23,32 +23,27 @@ export class TextVFXItem extends VFXItem { } override onLifetimeBegin (composition: Composition, content: TextItem) { - this._contentVisible = true; content.active = true; - + this.content?.mesh?.setItems([this.content]); + this.content.updateTexture(); } override onItemRemoved (composition: Composition, content?: TextItem) { - this._contentVisible = false; - if (content) { delete content.mesh; composition.destroyTextures(content.getTextures()); } } - override handleVisibleChanged (visible: boolean) { - if (this.content) { - this.content.visible = visible; - } - } - override onItemUpdate (dt: number, lifetime: number) { + if (!this.content) { + return ; + } this.content?.updateTime(this.time); } override getCurrentPosition () { - const pos: vec3 = [0, 0, 0]; + const pos = new Vector3(); this.transform.assignWorldTRS(pos); @@ -67,12 +62,12 @@ export class TextVFXItem extends VFXItem { } const worldMatrix = this.transform.getWorldMatrix(); const size = item.startSize; - const triangles = trianglesFromRect([0, 0, 0], size[0] / 2, size[1] / 2); + const triangles = trianglesFromRect(Vector3.ZERO, size.x / 2, size.y / 2); triangles.forEach(triangle => { - triangle.forEach(p => { - vec3MulMat4(p, p, worldMatrix); - }); + worldMatrix.transformPoint(triangle.p0 as Vector3); + worldMatrix.transformPoint(triangle.p1 as Vector3); + worldMatrix.transformPoint(triangle.p2 as Vector3); }); return { @@ -112,8 +107,9 @@ export class TextVFXItem extends VFXItem { createWireframeMesh (item: TextItem, color: spec.vec4): TextMesh { const spMesh = new TextMesh(this.composition.getEngine(), { wireframe: true, ...item.renderInfo }, this.composition); + spMesh.mesh.setVisible(true); spMesh.setItems([item]); - spMesh.mesh.material.setVector3('uFrameColor', [color[0], color[1], color[2]]); + spMesh.mesh.material.setVector3('uFrameColor', Vector3.fromArray(color)); spMesh.mesh.priority = 999; return spMesh; diff --git a/packages/effects-core/src/render/mesh.ts b/packages/effects-core/src/render/mesh.ts index 8c4c2d639..0b6d149b1 100644 --- a/packages/effects-core/src/render/mesh.ts +++ b/packages/effects-core/src/render/mesh.ts @@ -1,8 +1,8 @@ import type * as spec from '@galacean/effects-specification'; +import { Matrix4, Vector3 } from '@galacean/effects-math/es/core/index'; import { getConfig, POST_PROCESS_SETTINGS } from '../config'; import type { Engine } from '../engine'; import type { Material, MaterialDestroyOptions } from '../material'; -import type { mat4 } from '../math'; import type { Geometry, Renderer } from '../render'; import type { Disposable } from '../utils'; import { DestroyOptions } from '../utils'; @@ -10,7 +10,7 @@ import { DestroyOptions } from '../utils'; export interface MeshOptionsBase { material: Material, name?: string, - worldMatrix?: mat4 | Float32Array, + worldMatrix?: Matrix4, priority?: number, } @@ -40,7 +40,7 @@ export class Mesh implements Disposable { /** * Mesh 的世界矩阵 */ - worldMatrix: mat4 | Float32Array; + worldMatrix: Matrix4; /** * Mesh 的材质 */ @@ -54,7 +54,7 @@ export class Mesh implements Disposable { // 各引擎独立实现 priority,重写 getter/setter private _priority: number; - private visible = false; + private visible = true; /** * 创建一个新的 Mesh 对象。 @@ -70,7 +70,7 @@ export class Mesh implements Disposable { geometry, name = '', priority = 0, - worldMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], + worldMatrix = Matrix4.fromIdentity(), } = props; this.id = 'Mesh' + seed++; @@ -112,7 +112,7 @@ export class Mesh implements Disposable { renderer.setGlobalMatrix('effects_MatrixVP', renderingData.currentCamera.getViewProjectionMatrix()); renderer.setGlobalMatrix('_MatrixP', renderingData.currentCamera.getProjectionMatrix()); } - renderer.setGlobalMatrix('effects_ObjectToWorld', this.worldMatrix as mat4); + renderer.setGlobalMatrix('effects_ObjectToWorld', this.worldMatrix); } if (renderingData.currentFrame.editorTransform) { material.setVector4('uEditorTransform', renderingData.currentFrame.editorTransform); @@ -125,7 +125,7 @@ export class Mesh implements Disposable { emissionColor[0] /= 255; emissionColor[1] /= 255; emissionColor[2] /= 255; - material.setVector3('emissionColor', emissionColor); + material.setVector3('emissionColor', Vector3.fromArray(emissionColor)); material.setFloat('emissionIntensity', getConfig>(POST_PROCESS_SETTINGS)['intensity']); } } diff --git a/packages/effects-core/src/render/render-frame.ts b/packages/effects-core/src/render/render-frame.ts index 4824bdf6e..ad07ba15e 100644 --- a/packages/effects-core/src/render/render-frame.ts +++ b/packages/effects-core/src/render/render-frame.ts @@ -1,4 +1,6 @@ -import type { vec2, vec4, mat4 } from '@galacean/effects-specification'; +import type { vec4 } from '@galacean/effects-specification'; +import type { Matrix4 } from '@galacean/effects-math/es/core/index'; +import { Vector2, Vector4 } from '@galacean/effects-math/es/core/index'; import type { Camera } from '../camera'; import { glContext } from '../gl'; import type { UniformValue } from '../material'; @@ -179,6 +181,7 @@ export class RenderFrame implements Disposable { renderer: Renderer; resource: RenderFrameResource; keepColorBuffer?: boolean; + editorTransform: Vector4; /** * 名称 @@ -199,7 +202,6 @@ export class RenderFrame implements Disposable { readonly transparentTexture: Texture; protected destroyed = false; - protected _editorTransform: vec4; protected renderPassInfoMap: WeakMap = new WeakMap(); constructor (options: RenderFrameOptions) { @@ -389,7 +391,7 @@ export class RenderFrame implements Disposable { this.camera = camera; this.keepColorBuffer = keepColorBuffer; this.renderPassInfoMap.set(firstRP, { listStart: 0, listEnd: 0, renderPass: firstRP, intermedia: false }); - this.editorTransform = editorTransform; + this.editorTransform = Vector4.fromArray(editorTransform); if (!options.clearAction) { this.resetClearActions(); @@ -405,14 +407,6 @@ export class RenderFrame implements Disposable { this.renderer.getShaderLibrary()?.addShader(shader); } - get editorTransform (): vec4 { - return this._editorTransform; - } - - set editorTransform (v: vec4) { - this._editorTransform = v; - } - get renderPasses () { return this._renderPasses.slice(); } @@ -953,7 +947,7 @@ export class RenderFrame implements Disposable { engine, { uniformValues: { - // @ts-expect-error + // @ts-expect-error uDepth: semantics?.depthTexture, }, name, @@ -987,8 +981,8 @@ export class RenderFrame implements Disposable { } } -export function getTextureSize (tex?: Texture): vec2 { - return tex ? [tex.getWidth(), tex.getHeight()] : [0, 0]; +export function getTextureSize (tex?: Texture): Vector2 { + return tex ? new Vector2(tex.getWidth(), tex.getHeight()) : new Vector2(); } export function findPreviousRenderPass (renderPasses: RenderPass[], renderPass: RenderPass): RenderPass | undefined { @@ -1019,7 +1013,7 @@ export class GlobalUniforms { floats: Record = {}; ints: Record = {}; // vector3s: Record = {}; - matrices: Record = {}; + matrices: Record = {}; //... samplers: string[] = []; // 存放的sampler名称。 diff --git a/packages/effects-core/src/render/renderer.ts b/packages/effects-core/src/render/renderer.ts index 46b5e958b..7d5d28d28 100644 --- a/packages/effects-core/src/render/renderer.ts +++ b/packages/effects-core/src/render/renderer.ts @@ -1,4 +1,4 @@ -import type * as spec from '@galacean/effects-specification'; +import type { Matrix4 } from '@galacean/effects-math/es/core/index'; import type { LostHandler, RestoreHandler } from '../utils'; import type { FilterMode, FrameBuffer, RenderTextureFormat } from './frame-buffer'; import type { Mesh } from './mesh'; @@ -36,7 +36,7 @@ export class Renderer implements LostHandler, RestoreHandler { // OVERRIDE } - setGlobalMatrix (name: string, value: spec.mat4) { + setGlobalMatrix (name: string, value: Matrix4) { // OVERRIDE } diff --git a/packages/effects-core/src/shape/2d-shape.ts b/packages/effects-core/src/shape/2d-shape.ts index c0835fbc0..03fdd075f 100644 --- a/packages/effects-core/src/shape/2d-shape.ts +++ b/packages/effects-core/src/shape/2d-shape.ts @@ -1,5 +1,5 @@ import * as spec from '@galacean/effects-specification'; -import { DEG2RAD } from '../constants'; +import { DEG2RAD, Vector3 } from '@galacean/effects-math/es/core/index'; import { random } from '../utils'; import { getArcAngle } from './cone'; import type { Shape, ShapeGeneratorOptions, ShapeParticle } from './shape'; @@ -17,12 +17,12 @@ export class Circle implements Shape { generate (opt: ShapeGeneratorOptions): ShapeParticle { const arc = getArcAngle(this.arc, this.arcMode, opt) * DEG2RAD; - const direction = [Math.cos(arc), Math.sin(arc), 0]; + const direction = new Vector3(Math.cos(arc), Math.sin(arc), 0); const radius = this.radius; return { direction, - position: direction.map(n => n * radius), + position: direction.clone().multiply(radius), }; } } @@ -41,8 +41,8 @@ export class Rectangle implements Shape { const y = random(-this._h, this._h); return { - direction: [0, 0, 1], - position: [x, y, 0], + direction: new Vector3(0, 0, 1), + position: new Vector3(x, y, 0), }; } @@ -65,23 +65,23 @@ export class RectangleEdge implements Shape { generate (opt: ShapeGeneratorOptions): ShapeParticle { const arc = getArcAngle(this.arc, this.arcMode, opt) * DEG2RAD; - const direction = [Math.cos(arc), Math.sin(arc), 0]; + const direction = new Vector3(Math.cos(arc), Math.sin(arc), 0); const w = this._d; const h = this._h; const r0 = Math.atan2(h, w); const tan = Math.tan(arc); - let position; + const position = new Vector3(); if (arc < r0) { - position = [w, w * tan, 0]; + position.set(w, w * tan, 0); } else if (arc >= r0 && arc < Math.PI - r0) { - position = [h / tan, h, 0]; + position.set(h / tan, h, 0); } else if (arc < Math.PI + r0) { - position = [-w, -w * tan, 0]; + position.set(-w, -w * tan, 0); } else if (arc < Math.PI * 2 - r0) { - position = [-h / tan, -h, 0]; + position.set(-h / tan, -h, 0); } else { - position = [w, w * tan, 0]; + position.set(w, w * tan, 0); } return { @@ -106,8 +106,8 @@ export class Edge implements Shape { const x = this.arcMode === spec.ShapeArcMode.UNIFORM_BURST ? ((options.burstIndex % options.burstCount) / (options.burstCount - 1)) : random(0, 1); return { - direction: [0, 1, 0], - position: [this._d * (x - 0.5), 0, 0], + direction: new Vector3(0, 1, 0), + position: new Vector3(this._d * (x - 0.5), 0, 0), }; } } diff --git a/packages/effects-core/src/shape/cone.ts b/packages/effects-core/src/shape/cone.ts index 1b9faee7c..199f5abe4 100644 --- a/packages/effects-core/src/shape/cone.ts +++ b/packages/effects-core/src/shape/cone.ts @@ -1,7 +1,5 @@ import * as spec from '@galacean/effects-specification'; -import type { vec3 } from '@galacean/effects-specification'; -import { DEG2RAD } from '../constants'; -import { vecDot, vecNormalize } from '../math'; +import { DEG2RAD, Vector3 } from '@galacean/effects-math/es/core/index'; import { random } from '../utils'; import type { Shape, ShapeGeneratorOptions, ShapeParticle } from './index'; @@ -23,16 +21,16 @@ export class Cone implements Shape { const x = Math.cos(a) * this.radius; const y = Math.sin(a) * this.radius; - const position = [x, y, 0]; + const position = new Vector3(x, y, 0); const l = Math.tan(this.angle * DEG2RAD); - const dir = vecDot([], position, l) as vec3; + const dir = position.clone().multiply(l); // dir + [0,0,1] - dir[2] += 1; + dir.z += 1; return { - position: vecDot(position, position, random(0, 1)) as vec3, - direction: vecNormalize(dir, dir), + position: position.multiply(random(0, 1)), + direction: dir.normalize(), }; } } diff --git a/packages/effects-core/src/shape/donut.ts b/packages/effects-core/src/shape/donut.ts index 59ae9955a..8503eec8f 100644 --- a/packages/effects-core/src/shape/donut.ts +++ b/packages/effects-core/src/shape/donut.ts @@ -1,12 +1,9 @@ -import type { vec3 } from '@galacean/effects-specification'; -import { DEG2RAD } from '../constants'; -import type { mat3 } from '../math'; -import { mat3FromRotationZ, vec3MulMat3 } from '../math'; +import { DEG2RAD, Matrix4, Vector3 } from '@galacean/effects-math/es/core/index'; import { random } from '../utils'; import { getArcAngle } from './cone'; import type { Shape, ShapeGeneratorOptions, ShapeParticle } from './shape'; -const tempMat3 = [] as unknown as mat3; +const tempMat4 = new Matrix4(); export class Donut implements Shape { radius: number; @@ -25,13 +22,13 @@ export class Donut implements Shape { const center = this.radius - dradius; const angle = random(0, Math.PI * 2); const arc = getArcAngle(this.arc, this.arcMode, opt) * DEG2RAD; - const rot = mat3FromRotationZ(tempMat3, arc); - const direction = [Math.cos(angle), Math.sin(angle), 0] as vec3; - const position = [center + Math.cos(angle) * dradius, 0, Math.sin(angle) * dradius] as vec3; + const rot = tempMat4.setFromRotationZ(arc); + const direction = new Vector3(Math.cos(angle), Math.sin(angle), 0); + const position = new Vector3(center + Math.cos(angle) * dradius, 0, Math.sin(angle) * dradius); return { - direction: vec3MulMat3(direction, direction, rot), - position: vec3MulMat3(position, position, rot), + direction: rot.transformNormal(direction), + position: rot.transformPoint(position), }; } diff --git a/packages/effects-core/src/shape/shape.ts b/packages/effects-core/src/shape/shape.ts index 6086719d1..00d178e86 100644 --- a/packages/effects-core/src/shape/shape.ts +++ b/packages/effects-core/src/shape/shape.ts @@ -1,6 +1,5 @@ import * as spec from '@galacean/effects-specification'; -import type { vec3 } from '@galacean/effects-specification'; -import { vecNormalize } from '../math'; +import { Vector3 } from '@galacean/effects-math/es/core/index'; import { Circle, Edge, Rectangle, RectangleEdge } from './2d-shape'; import { Cone } from './cone'; import { Donut } from './donut'; @@ -15,8 +14,8 @@ export type ShapeGeneratorOptions = { }; export type ShapeParticle = { - direction: number[], - position: number[], + direction: Vector3, + position: Vector3, }; export interface Shape { @@ -25,13 +24,13 @@ export interface Shape { export type ShapeGenerator = Shape - & { reverseDirection?: boolean, alignSpeedDirection?: boolean, upDirection?: vec3 }; + & { reverseDirection?: boolean, alignSpeedDirection?: boolean, upDirection?: Vector3 }; class ShapeNone implements Shape { generate () { return { - position: [0, 0, 0], - direction: [0, 0, 0], + position: new Vector3(), + direction: new Vector3(), }; } } @@ -72,7 +71,7 @@ export function createShape (shapeOptions: spec.ParticleShape): Shape { const { alignSpeedDirection, upDirection = [0, 0, 1] } = shapeOptions as spec.ParticleShapeBase; ctrl.alignSpeedDirection = alignSpeedDirection; - ctrl.upDirection = vecNormalize(upDirection); + ctrl.upDirection = Vector3.fromArray(upDirection).normalize(); } return ctrl; diff --git a/packages/effects-core/src/shape/sphere.ts b/packages/effects-core/src/shape/sphere.ts index a0972d236..9c15cc619 100644 --- a/packages/effects-core/src/shape/sphere.ts +++ b/packages/effects-core/src/shape/sphere.ts @@ -1,12 +1,9 @@ -import type { vec3 } from '@galacean/effects-specification'; -import { DEG2RAD } from '../constants'; -import type { mat3 } from '../math'; -import { mat3FromRotationZ, vec3MulMat3 } from '../math'; +import { DEG2RAD, Matrix4, Vector3 } from '@galacean/effects-math/es/core/index'; import { random } from '../utils'; import { getArcAngle } from './cone'; import type { Shape, ShapeGeneratorOptions, ShapeParticle } from '.'; -const tempMat3 = [] as unknown as mat3; +const tempMat4 = new Matrix4(); export class Sphere implements Shape { arc: number; @@ -27,12 +24,12 @@ export class Sphere implements Shape { const rz = getArcAngle(this.arc, this.arcMode, opt) * DEG2RAD; const rh = this.getHorizontalAngle() * DEG2RAD; const radius = this.radius; - const point = [Math.cos(rh), 0, Math.sin(rh)] as vec3; - const mat3 = mat3FromRotationZ(tempMat3, rz); - const p = vec3MulMat3(point, point, mat3); + const point = new Vector3(Math.cos(rh), 0, Math.sin(rh)); + const mat4 = tempMat4.setFromRotationZ(rz); + const p = mat4.transformNormal(point); return { - position: p.map(a => a * radius), + position: p.clone().multiply(radius), direction: p, }; } diff --git a/packages/effects-core/src/shape/texture-shape.ts b/packages/effects-core/src/shape/texture-shape.ts index df5634144..ed240c9bf 100644 --- a/packages/effects-core/src/shape/texture-shape.ts +++ b/packages/effects-core/src/shape/texture-shape.ts @@ -1,5 +1,4 @@ -import type { vec3 } from '@galacean/effects-specification'; -import { clamp, vecNormalize } from '../math'; +import { Vector3, clamp } from '@galacean/effects-math/es/core/index'; import { getArcAngle } from './cone'; import type { Shape, ShapeGeneratorOptions, ShapeParticle } from '.'; @@ -29,11 +28,11 @@ export class TextureShape implements Shape { const pointX = (anchors[index * 2] + this.block[0] * this.random * Math.random()) % 1 - 0.5; const pointY = (anchors[index * 2 + 1] + this.block[1] * this.random * Math.random()) % 1 - 0.5; - const dir: vec3 = [pointX, pointY, 0]; + const dir = new Vector3(pointX, pointY, 0); return { - position: [pointX * this.width, pointY * this.height, 0], - direction: vecNormalize(dir, dir), + position: new Vector3(pointX * this.width, pointY * this.height, 0), + direction: dir.normalize(), }; } } diff --git a/packages/effects-core/src/ticker.ts b/packages/effects-core/src/ticker.ts index fcfe9fa9a..9f747c9fb 100644 --- a/packages/effects-core/src/ticker.ts +++ b/packages/effects-core/src/ticker.ts @@ -1,4 +1,4 @@ -import { clamp } from './math'; +import { clamp } from '@galacean/effects-math/es/core/index'; /** * 定时器类 diff --git a/packages/effects-core/src/transform.ts b/packages/effects-core/src/transform.ts index 9fcc04273..7725079d1 100644 --- a/packages/effects-core/src/transform.ts +++ b/packages/effects-core/src/transform.ts @@ -1,68 +1,57 @@ -import type { vec2, vec3, vec4 } from '@galacean/effects-specification'; +import type * as spec from '@galacean/effects-specification'; +import { Vector3, Matrix4, Quaternion, Euler } from '@galacean/effects-math/es/core/index'; import type { Disposable } from './utils'; import { addItem, removeItem } from './utils'; -import type { mat4, mat3 } from './math'; -import { - quatFromRotation, - quatStar, - getMat4TR, - getMat4TRS, - mat3FromQuat, - mat4create, - mat4multiply, - rotationFromMat3, - vecAssign, - mat4fromRotationTranslationScale, isIdentityMatrix, mat4ToIdentityMatrix, quatMultiply, mat4Clone, -} from './math'; export interface TransformProps { - position?: vec3, - rotation?: vec3, - quat?: vec4, - scale?: vec3, + position?: spec.vec3 | Vector3, + rotation?: spec.vec3 | Euler, + quat?: spec.vec4 | Quaternion, + scale?: spec.vec3 | Vector3, name?: string, - anchor?: vec2 | vec3, + anchor?: spec.vec2 | spec.vec3 | Vector3, valid?: boolean, } -const tempMat3 = new Array(9).fill(0) as mat3; -const tempQuat = new Array(4).fill(0) as vec4; +const tempQuat = new Quaternion(); let seed = 1; export class Transform implements Disposable { /** * 转换右手坐标系左手螺旋对应的四元数到对应的旋转角 - * @param out - 旋转向量 * @param quat - 四元数 + * @param out - 欧拉角 * @returns */ - static getRotation (out: vec3, quat: vec4): vec3 { - quatStar(tempQuat, quat); - const m3 = mat3FromQuat(tempMat3, tempQuat); + static getRotation (quat: Quaternion, out: Euler): Euler { + const newQuat = tempQuat.copyFrom(quat); - return rotationFromMat3(out, m3); + newQuat.conjugate(); + + return out.setFromQuaternion(newQuat); } + public name: string; /** * 自身位移 */ - public readonly position: vec3 = [0, 0, 0]; + public readonly position = new Vector3(0, 0, 0); /** * 自身旋转对应的四元数,右手坐标系,旋转正方向左手螺旋(轴向的顺时针),旋转欧拉角的顺序为 ZYX */ - public readonly quat: vec4 = [0, 0, 0, 1]; + public readonly quat = new Quaternion(0, 0, 0, 1); /** * 自身旋转角度 */ - public readonly rotation: vec3 = [0, 0, 0]; + public readonly rotation = new Euler(0, 0, 0); /** * 自身缩放 */ - public readonly scale: vec3 = [1, 1, 1]; + public readonly scale = new Vector3(1, 1, 1); /** * 自身锚点 */ - public readonly anchor: vec3 = [0, 0, 0]; + public readonly anchor = new Vector3(0, 0, 0); /** * 子变换,可以有多个 */ @@ -74,15 +63,15 @@ export class Transform implements Disposable { /** * 所有父变换对应的联合矩阵 */ - private parentMatrix: mat4; + private parentMatrix: Matrix4; /** * 包含父变换的最终模型矩阵 */ - private worldMatrix: mat4 = mat4create(); + private worldMatrix = Matrix4.fromIdentity(); /** * 仅包含自身变换的模型矩阵 */ - private localMatrix: mat4 = mat4create(); + private localMatrix = Matrix4.fromIdentity(); /** * 变换是否需要生效,不生效返回的模型矩阵为单位矩阵,需要随元素生命周期改变 */ @@ -103,7 +92,7 @@ export class Transform implements Disposable { /** * 最终模型矩阵对应变换的缓存,当自身矩阵或父矩阵有修改时需要更新 */ - private readonly worldTRSCache: { position: vec3, quat: vec4, scale: vec3 } = { position: [0, 0, 0], quat: [0, 0, 0, 1], scale: [1, 1, 1] }; + private readonly worldTRSCache = { position: new Vector3(0, 0, 0), quat: new Quaternion(0, 0, 0, 1), scale: new Vector3(1, 1, 1) }; constructor (props: TransformProps = {}, parent?: Transform) { this.name = `transform_${seed++}`; @@ -164,10 +153,10 @@ export class Transform implements Disposable { * @param z */ setPosition (x: number, y: number, z: number) { - if (this.position[0] !== x || this.position[1] !== y || this.position[2] !== z) { - this.position[0] = x; - this.position[1] = y; - this.position[2] = z; + if (this.position.x !== x || this.position.y !== y || this.position.z !== z) { + this.position.x = x; + this.position.y = y; + this.position.z = z; this.dirtyFlags.localData = true; this.dispatchValueChange(); } @@ -181,9 +170,9 @@ export class Transform implements Disposable { */ translate (x: number, y: number, z: number) { if (x !== 0 || y !== 0 || z !== 0) { - this.position[0] += x; - this.position[1] += y; - this.position[2] += z; + this.position.x += x; + this.position.y += y; + this.position.z += z; this.dirtyFlags.localData = true; this.dispatchValueChange(); } @@ -195,12 +184,12 @@ export class Transform implements Disposable { * @param z */ setRotation (x: number, y: number, z: number) { - if (this.rotation[0] !== x || this.rotation[1] !== y || this.rotation[2] !== z) { - this.rotation[0] = x; - this.rotation[1] = y; - this.rotation[2] = z; - quatFromRotation(this.quat, x, y, z); - quatStar(this.quat, this.quat); + if (this.rotation.x !== x || this.rotation.y !== y || this.rotation.z !== z) { + this.rotation.x = x; + this.rotation.y = y; + this.rotation.z = z; + this.quat.setFromEuler(this.rotation); + this.quat.conjugate(); this.dirtyFlags.localData = true; this.dispatchValueChange(); } @@ -215,12 +204,12 @@ export class Transform implements Disposable { * @private */ setQuaternion (x: number, y: number, z: number, w: number) { - if (this.quat[0] !== x || this.quat[1] !== y || this.quat[2] !== z || this.quat[3] !== w) { - this.quat[0] = x; - this.quat[1] = y; - this.quat[2] = z; - this.quat[3] = w; - rotationFromMat3(this.rotation, mat3FromQuat(tempMat3, [x, y, z, w])); + if (this.quat.x !== x || this.quat.y !== y || this.quat.z !== z || this.quat.w !== w) { + this.quat.x = x; + this.quat.y = y; + this.quat.z = z; + this.quat.w = w; + this.rotation.setFromQuaternion(this.quat); this.dirtyFlags.localData = true; this.dispatchValueChange(); } @@ -233,10 +222,10 @@ export class Transform implements Disposable { * @param z */ setScale (x: number, y: number, z: number) { - if (this.scale[0] !== x || this.scale[1] !== y || this.scale[2] !== z) { - this.scale[0] = x; - this.scale[1] = y; - this.scale[2] = z; + if (this.scale.x !== x || this.scale.y !== y || this.scale.z !== z) { + this.scale.x = x; + this.scale.y = y; + this.scale.z = z; this.dirtyFlags.localData = true; this.dispatchValueChange(); } @@ -246,9 +235,9 @@ export class Transform implements Disposable { * 在当前旋转的基础上使用四元素添加旋转 * @param quat */ - rotateByQuat (quat: vec4) { - quatMultiply(this.quat, this.quat, quat); - rotationFromMat3(this.rotation, mat3FromQuat(tempMat3, quat)); + rotateByQuat (quat: Quaternion) { + this.quat.multiply(quat); + this.rotation.setFromQuaternion(this.quat); this.dirtyFlags.localData = true; this.dispatchValueChange(); } @@ -260,9 +249,9 @@ export class Transform implements Disposable { * @param z */ scaleBy (x: number, y: number, z: number) { - this.scale[0] *= x; - this.scale[1] *= y; - this.scale[2] *= z; + this.scale.x *= x; + this.scale.y *= y; + this.scale.z *= z; this.dirtyFlags.localData = true; this.dispatchValueChange(); } @@ -274,10 +263,10 @@ export class Transform implements Disposable { * @param z */ setAnchor (x: number, y: number, z: number) { - if (this.anchor[0] !== x || this.anchor[1] !== y || this.anchor[2] !== z) { - this.anchor[0] = x; - this.anchor[1] = y; - this.anchor[2] = z; + if (this.anchor.x !== x || this.anchor.y !== y || this.anchor.z !== z) { + this.anchor.x = x; + this.anchor.y = y; + this.anchor.z = z; this.dirtyFlags.localData = true; this.dispatchValueChange(); } @@ -295,20 +284,40 @@ export class Transform implements Disposable { this.name = name; } if (position) { - this.setPosition(position[0], position[1], position[2]); + if (position instanceof Vector3) { + this.setPosition(position.x, position.y, position.z); + } else { + this.setPosition(position[0], position[1], position[2]); + } } if (quat) { - this.setQuaternion(quat[0], quat[1], quat[2], quat[3]); + if (quat instanceof Quaternion) { + this.setQuaternion(quat.x, quat.y, quat.z, quat.w); + } else { + this.setQuaternion(quat[0], quat[1], quat[2], quat[3]); + } } else if (rotation) { const mul = reverseEuler ? -1 : 1; - this.setRotation(rotation[0] * mul, rotation[1] * mul, rotation[2] * mul); + if (rotation instanceof Euler) { + this.setRotation(rotation.x * mul, rotation.y * mul, rotation.z * mul); + } else { + this.setRotation(rotation[0] * mul, rotation[1] * mul, rotation[2] * mul); + } } if (scale) { - this.setScale(scale[0], scale[1], scale[2]); + if (scale instanceof Vector3) { + this.setScale(scale.x, scale.y, scale.z); + } else { + this.setScale(scale[0], scale[1], scale[2]); + } } if (anchor) { - this.setAnchor(anchor[0], anchor[1], anchor[2] ?? 0); + if (anchor instanceof Vector3) { + this.setAnchor(anchor.x, anchor.y, anchor.z); + } else { + this.setAnchor(anchor[0], anchor[1], anchor[2] ?? 0); + } } } @@ -332,15 +341,15 @@ export class Transform implements Disposable { * 获取当前的旋转量 * @returns */ - getRotation (): vec3 { - return Transform.getRotation([0, 0, 0], this.quat); + getRotation (): Euler { + return Transform.getRotation(this.quat, new Euler()); } /** * 获取当前的四元数 * @returns */ - getQuaternion (): vec4 { + getQuaternion (): Quaternion { return this.quat; } @@ -350,13 +359,13 @@ export class Transform implements Disposable { updateLocalMatrix () { if (this.valid) { if (this.dirtyFlags.localData) { - mat4fromRotationTranslationScale(this.localMatrix, this.quat, this.position, this.scale, this.anchor); + this.localMatrix.compose(this.position, this.quat, this.scale, this.anchor); this.dirtyFlags.localMatrix = true; } this.dirtyFlags.localData = false; } else { - if (!isIdentityMatrix(this.localMatrix)) { - mat4ToIdentityMatrix(this.localMatrix); + if (!this.localMatrix.isIdentity()) { + this.localMatrix.identity(); this.dirtyFlags.localMatrix = true; } } @@ -368,7 +377,7 @@ export class Transform implements Disposable { * 当变换不需要生效时返回单位矩阵 * @returns */ - getMatrix (): mat4 { + getMatrix (): Matrix4 { this.updateLocalMatrix(); return this.localMatrix; @@ -377,7 +386,7 @@ export class Transform implements Disposable { * 获取父矩阵,如果有多级父节点,返回整体变换 * @returns */ - getParentMatrix (): mat4 | undefined { + getParentMatrix (): Matrix4 | undefined { if (this.parent) { this.parentMatrix = this.parent.getWorldMatrix(); this.dirtyFlags.parentMatrix = this.dirtyFlags.parentMatrix || this.parent.dirtyFlags.localMatrix || this.parent.dirtyFlags.worldMatrix; @@ -390,15 +399,15 @@ export class Transform implements Disposable { * 获取包含自身变换和父变换的模型变换矩阵 * @returns */ - getWorldMatrix (): mat4 { + getWorldMatrix (): Matrix4 { const localMatrix = this.getMatrix(); const parentMatrix = this.getParentMatrix(); if (this.dirtyFlags.localMatrix || this.dirtyFlags.parentMatrix) { if (parentMatrix) { - mat4multiply(this.worldMatrix, parentMatrix, localMatrix); + this.worldMatrix.multiplyMatrices(parentMatrix, localMatrix); } else { - mat4Clone(this.worldMatrix, localMatrix); + this.worldMatrix.copyFrom(localMatrix); } this.dirtyFlags.worldMatrix = true; this.dirtyFlags.localMatrix = false; @@ -412,39 +421,37 @@ export class Transform implements Disposable { * 获取联合变换后的最终缩放因子 * @returns */ - getWorldScale (): vec3 { + getWorldScale (): Vector3 { const cache = this.worldTRSCache; if (this.dirtyFlags.worldMatrix) { - getMat4TRS(this.getWorldMatrix(), cache.position, cache.quat, cache.scale); + const mat = this.getWorldMatrix(); + + mat.decompose(cache.position, cache.quat, cache.scale); this.dirtyFlags.worldMatrix = false; } - return [...this.worldTRSCache.scale]; + return this.worldTRSCache.scale.clone(); } /** * 获取联合变换后的最终位置 * @returns */ - getWorldPosition (): vec3 { + getWorldPosition (): Vector3 { this.updateTRSCache(); - return [...this.worldTRSCache.position]; + return this.worldTRSCache.position.clone(); } /** * 获取联合变换后的最终旋转量 * @returns */ - getWorldRotation (): vec3 { + getWorldRotation (): Euler { this.updateTRSCache(); - const out: vec3 = [0, 0, 0]; - - Transform.getRotation(out, this.worldTRSCache.quat); - - return out; + return Transform.getRotation(this.worldTRSCache.quat, new Euler()); } /** @@ -453,16 +460,16 @@ export class Transform implements Disposable { * @param quat * @param scale */ - assignWorldTRS (position?: vec3 | number[] | Float32Array, quat?: vec4 | number[] | Float32Array, scale?: vec3 | number[] | Float32Array) { + assignWorldTRS (position?: Vector3, quat?: Quaternion, scale?: Vector3) { this.updateTRSCache(); if (position) { - vecAssign(position, this.worldTRSCache.position, 3); + position.copyFrom(this.worldTRSCache.position); } if (quat) { - vecAssign(quat, this.worldTRSCache.quat, 4); + quat.copyFrom(this.worldTRSCache.quat); } if (scale) { - vecAssign(scale, this.worldTRSCache.scale, 3); + scale.copyFrom(this.worldTRSCache.scale); } } @@ -472,25 +479,23 @@ export class Transform implements Disposable { * @param scale * @returns */ - cloneFromMatrix (m4: mat4, scale?: vec3) { + cloneFromMatrix (m4: Matrix4, scale?: Vector3) { + m4.decompose(this.position, this.quat, this.scale); if (scale) { - getMat4TR(m4, this.position, this.quat, scale); - vecAssign(this.scale, scale, 3); - } else { - getMat4TRS(m4, this.position, this.quat, this.scale); + scale.copyFrom(this.scale); } this.dirtyFlags.localData = true; this.dispatchValueChange(); } /** - * 设置 Transform 生效 / 失效, 默认元素生命周期开始后生效,结束后失效 - */ + * 设置 Transform 生效 / 失效, 默认元素生命周期开始后生效,结束后失效 + */ setValid (val: boolean) { if (this.valid !== val) { this.valid = val; if (!val) { - mat4ToIdentityMatrix(this.localMatrix); + this.localMatrix.identity(); this.dirtyFlags.localMatrix = true; } else { this.dirtyFlags.localData = true; @@ -506,7 +511,7 @@ export class Transform implements Disposable { return this.valid; } - dispose (): void {} + dispose (): void { } private updateTRSCache () { const worldMatrix = this.getWorldMatrix(); @@ -514,7 +519,7 @@ export class Transform implements Disposable { if (this.dirtyFlags.worldMatrix) { const cache = this.worldTRSCache; - getMat4TRS(worldMatrix, cache.position, cache.quat, cache.scale); + worldMatrix.decompose(cache.position, cache.quat, cache.scale); this.dirtyFlags.worldMatrix = false; } } diff --git a/packages/effects-core/src/utils/timeline-component.ts b/packages/effects-core/src/utils/timeline-component.ts index 3185243cd..85304861a 100644 --- a/packages/effects-core/src/utils/timeline-component.ts +++ b/packages/effects-core/src/utils/timeline-component.ts @@ -1,17 +1,7 @@ import * as spec from '@galacean/effects-specification'; -import type { vec3 } from '@galacean/effects-specification'; +import { Euler, Vector3 } from '@galacean/effects-math/es/core/index'; import type { ValueGetter } from '../math'; -import { - calculateTranslation, - createValueGetter, - ensureVec3, - isZeroVec, - vecAdd, - vecAssign, - vecFill, - vecMulScalar, - vecNormalize, -} from '../math'; +import { ensureVec3, calculateTranslation, createValueGetter } from '../math'; import type { Transform } from '../transform'; import type { VFXItem } from '../vfx-item'; import type { SpriteItemOptions, ItemBasicTransform, ItemLinearVelOverLifetime, SpriteRenderData } from '../plugins'; @@ -22,9 +12,9 @@ export interface TimelineComponentOptions { positionOverLifetime?: spec.PositionOverLifetime, } -const tempRot: vec3 = [0, 0, 0]; -const tempSize: vec3 = [1, 1, 1]; -const tempPos: vec3 = [0, 0, 0]; +const tempRot = new Euler(); +const tempSize = new Vector3(1, 1, 1); +const tempPos = new Vector3(0, 0, 0); export class TimelineComponent { options: Omit; @@ -44,7 +34,7 @@ export class TimelineComponent { enabled?: boolean, }; - private velocity: vec3; + private velocity: Vector3; private readonly rotationOverLifetime: { asRotation?: boolean, separateAxes?: boolean, @@ -65,9 +55,9 @@ export class TimelineComponent { this.transform = transform; this.basicTransform = { - position: [...transform.position], - rotation: [...transform.getRotation()], - scale: [scale[0], scale[1], scale[2]], + position: transform.position.clone(), + rotation: transform.getRotation(), + scale, }; if (positionOverLifetime.path) { this.basicTransform.path = createValueGetter(positionOverLifetime.path); @@ -75,14 +65,14 @@ export class TimelineComponent { this.positionOverLifetime = positionOverLifetime; this.options = { startSpeed: positionOverLifetime.startSpeed || 0, - startSize: scale && scale[0] || 1, - sizeAspect: scale && (scale[0] / (scale[1] || 1)) || 1, + startSize: scale && scale.x || 1, + sizeAspect: scale && (scale.x / (scale.y || 1)) || 1, startColor: [1, 1, 1, 1], duration: duration || 0, looping: endBehavior && endBehavior === spec.ItemEndBehavior.loop, - gravity: ensureVec3(positionOverLifetime.gravity), + gravity: Vector3.fromArray(positionOverLifetime.gravity || []), gravityModifier: createValueGetter(positionOverLifetime.gravityOverLifetime ?? 0), - direction: positionOverLifetime.direction ? vecNormalize([], positionOverLifetime.direction) : ensureVec3(), + direction: positionOverLifetime.direction ? Vector3.fromArray(positionOverLifetime.direction).normalize() : new Vector3(), endBehavior: endBehavior || spec.END_BEHAVIOR_DESTROY, }; @@ -147,7 +137,7 @@ export class TimelineComponent { private getVelocity () { if (!this.velocity) { - this.velocity = vecMulScalar([] as unknown as vec3, this.options.direction, this.options.startSpeed); + this.velocity = this.options.direction.clone().multiply(this.options.startSpeed); } return this.velocity; @@ -156,18 +146,22 @@ export class TimelineComponent { private willTranslate () { return !!((this.linearVelOverLifetime && this.linearVelOverLifetime.enabled) || (this.orbitalVelOverLifetime && this.orbitalVelOverLifetime.enabled) || - (this.options.gravityModifier && !isZeroVec(this.options.gravity)) || - (this.options.startSpeed && !isZeroVec(this.options.direction))); + (this.options.gravityModifier && !this.options.gravity.isZero()) || + (this.options.startSpeed && !this.options.direction.isZero())); } updatePosition (x: number, y: number, z: number) { - this.basicTransform.position = [x, y, z]; + this.basicTransform.position.set(x, y, z); + } + + updateRotation (x: number, y: number, z: number) { + this.basicTransform.rotation.set(x, y, z); } getRenderData (_time: number, init?: boolean): SpriteRenderData { const options = this.options; - const sizeInc = vecFill(tempSize, 1); - const rotInc = vecFill(tempRot, 0); + const sizeInc = tempSize.setFromNumber(1); + const rotInc = tempRot.set(0, 0, 0); let sizeChanged, rotChanged; const time = _time < 0 ? _time : Math.max(_time, 0.); const duration = options.duration; @@ -176,23 +170,23 @@ export class TimelineComponent { const ret: SpriteRenderData = { life, transform: this.transform, - startSize: this.basicTransform.scale, + startSize: this.basicTransform.scale.clone(), }; const transform = this.basicTransform; life = life < 0 ? 0 : (life > 1 ? 1 : life); if (this.sizeXOverLifetime) { - sizeInc[0] = this.sizeXOverLifetime.getValue(life); + sizeInc.x = this.sizeXOverLifetime.getValue(life); if (this.sizeSeparateAxes) { - sizeInc[1] = this.sizeYOverLifetime.getValue(life); - sizeInc[2] = this.sizeZOverLifetime.getValue(life); + sizeInc.y = this.sizeYOverLifetime.getValue(life); + sizeInc.z = this.sizeZOverLifetime.getValue(life); } else { - sizeInc[2] = sizeInc[1] = sizeInc[0]; + sizeInc.z = sizeInc.y = sizeInc.x; } sizeChanged = true; } if (sizeChanged || init) { - ret.transform.setScale(sizeInc[0], sizeInc[1], sizeInc[2]); + ret.transform.setScale(sizeInc.x, sizeInc.y, sizeInc.z); } const rotationOverLifetime = this.rotationOverLifetime; @@ -201,39 +195,39 @@ export class TimelineComponent { const incZ = func(rotationOverLifetime.z!); const separateAxes = rotationOverLifetime.separateAxes; - rotInc[0] = separateAxes ? func(rotationOverLifetime.x!) : 0; - rotInc[1] = separateAxes ? func(rotationOverLifetime.y!) : 0; - rotInc[2] = incZ; + rotInc.x = separateAxes ? func(rotationOverLifetime.x!) : 0; + rotInc.y = separateAxes ? func(rotationOverLifetime.y!) : 0; + rotInc.z = incZ; rotChanged = true; } if (rotChanged || init) { - const rot = vecAdd(tempRot, transform.rotation, rotInc); + const rot = tempRot.addEulers(transform.rotation, rotInc); - ret.transform.setRotation(rot[0], rot[1], rot[2]); + ret.transform.setRotation(rot.x, rot.y, rot.z); } - let pos: vec3 | undefined; + let pos: Vector3 | undefined; if (this.willTranslate() || init) { - const out = vecFill(tempSize, 0); + const out = tempSize.setFromNumber(0); pos = calculateTranslation(out, this, options.gravity, time, duration, transform.position, this.getVelocity()); } if (transform.path) { if (!pos) { - pos = vecAssign(tempPos, transform.position, 3); + pos = tempPos.copyFrom(transform.position); } - vecAdd(pos, pos, transform.path.getValue(life)); + pos.add(transform.path.getValue(life)); } if (pos) { - ret.transform.setPosition(pos[0], pos[1], pos[2]); + ret.transform.setPosition(pos.x, pos.y, pos.z); } if (ret.startSize) { const scaling = ret.transform.scale; - ret.transform.setScale(scaling[0] * ret.startSize[0], scaling[1] * ret.startSize[1], scaling[2] * ret.startSize[2]); + ret.transform.setScale(scaling.x * ret.startSize.x, scaling.y * ret.startSize.y, scaling.z * ret.startSize.z); } return ret; diff --git a/packages/effects-core/src/vfx-item.ts b/packages/effects-core/src/vfx-item.ts index 53836dc10..3c0691bc7 100644 --- a/packages/effects-core/src/vfx-item.ts +++ b/packages/effects-core/src/vfx-item.ts @@ -1,6 +1,6 @@ import * as spec from '@galacean/effects-specification'; -import { HELP_LINK, VFX_ITEM_TYPE_TREE } from './constants'; -import { quatFromRotation, quatStar } from './math'; +import { Euler, Quaternion, Vector3 } from '@galacean/effects-math/es/core/index'; +import { HELP_LINK } from './constants'; import type { Disposable } from './utils'; import { Transform } from './transform'; import type { @@ -15,8 +15,13 @@ import type { InteractItem, BoundingBoxData, SpriteRenderData, + ParticleVFXItem, + FilterSpriteVFXItem, + SpriteVFXItem, + CameraVFXItem, } from './plugins'; import type { Composition } from './composition'; +import type { CompVFXItem } from './comp-vfx-item'; export type VFXItemContent = ParticleSystem | SpriteItem | CalculateItem | CameraController | InteractItem | void; export type VFXItemConstructor = new (props: VFXItemProps, composition: Composition) => VFXItem; @@ -27,6 +32,7 @@ export type VFXItemProps = startTime: number, relative?: boolean, listIndex?: number, + refId?: string, } ; @@ -139,12 +145,39 @@ export abstract class VFXItem implements Disposable { * 元素动画结束回调是否被调用 */ private callEnd: boolean; - /** * 元素动画的速度 */ private speed: number; + static isComposition (item: VFXItem): item is CompVFXItem { + return item.type === spec.ItemType.composition; + } + + static isSprite (item: VFXItem): item is SpriteVFXItem { + return item.type === spec.ItemType.sprite; + } + + static isParticle (item: VFXItem): item is ParticleVFXItem { + return item.type === spec.ItemType.particle; + } + + static isFilterSprite (item: VFXItem): item is FilterSpriteVFXItem { + return item.type === spec.ItemType.filter; + } + + static isNull (item: VFXItem): item is VFXItem { + return item.type === spec.ItemType.null; + } + + static isTree (item: VFXItem): item is VFXItem { + return item.type === spec.ItemType.tree; + } + + static isExtraCamera (item: VFXItem): item is CameraVFXItem { + return item.id === 'extra-camera' && item.name === 'extra-camera'; + } + constructor ( props: VFXItemProps, composition: Composition, @@ -313,7 +346,7 @@ export abstract class VFXItem implements Disposable { */ public onUpdate (deltaTime: number) { if (this.started && !this.frozen && this.composition) { - const dt = deltaTime * this.speed; + let dt = deltaTime * this.speed; const time = (this.timeInms += dt); this.time += dt / 1000; @@ -324,6 +357,7 @@ export abstract class VFXItem implements Disposable { this.transform.setValid(true); this.createContent(); + this._contentVisible = true; this.onLifetimeBegin(this.composition, this.content); this.composition.itemLifetimeEvent(this, true); } @@ -354,6 +388,7 @@ export abstract class VFXItem implements Disposable { this.transform.setValid(true); shouldUpdate = true; lifetime = 1; + dt = 0; } if (!this.reusable) { if ( @@ -366,7 +401,7 @@ export abstract class VFXItem implements Disposable { this.endBehavior = spec.END_BEHAVIOR_FORWARD; } } else if (this.endBehavior === spec.END_BEHAVIOR_DESTROY) { - this.setVisible(false); + this._contentVisible = false; } lifetime = Math.min(lifetime, 1); } else { @@ -382,6 +417,7 @@ export abstract class VFXItem implements Disposable { this.callEnd = false; } this.lifetime = lifetime; + shouldUpdate && this.onItemUpdate(dt, lifetime); } } @@ -515,10 +551,10 @@ export abstract class VFXItem implements Disposable { * 设置元素在 3D 坐标轴上相对旋转(角度) */ rotate (x: number, y: number, z: number) { - const q: spec.vec4 = [0, 0, 0, 1]; + const euler = new Euler(x, y, z); + const q = Quaternion.fromEuler(euler); - quatFromRotation(q, x, y, z); - quatStar(q, q); + q.conjugate(); this.transform.rotateByQuat(q); } /** @@ -533,8 +569,8 @@ export abstract class VFXItem implements Disposable { */ setPositionByPixel (x: number, y: number) { if (this.composition) { - const z = this.transform.getWorldPosition()[2]; - const [rx, ry] = this.composition.camera.getInverseVPRatio(z); + const { z } = this.transform.getWorldPosition(); + const { x: rx, y: ry } = this.composition.camera.getInverseVPRatio(z); const width = this.composition.renderer.getWidth() / 2; const height = this.composition.renderer.getHeight() / 2; @@ -593,8 +629,8 @@ export abstract class VFXItem implements Disposable { /** * 获取元素当前世界坐标 */ - getCurrentPosition (): spec.vec3 | undefined { - const pos: spec.vec3 = [0, 0, 0]; + getCurrentPosition (): Vector3 { + const pos = new Vector3(); this.transform.assignWorldTRS(pos); @@ -618,14 +654,15 @@ export abstract class VFXItem implements Disposable { if (this.composition) { this.onItemRemoved(this.composition, this._content); this._content = undefined; + this._contentVisible = false; } this.started = false; } translateByPixel (x: number, y: number) { if (this.composition) { - const z = this.transform.getWorldPosition()[2]; - const [rx, ry] = this.composition.camera.getInverseVPRatio(z); + const { z } = this.transform.getWorldPosition(); + const { x: rx, y: ry } = this.composition.camera.getInverseVPRatio(z); const width = this.composition.renderer.getWidth() / 2; const height = this.composition.renderer.getHeight() / 2; @@ -647,6 +684,28 @@ export abstract class VFXItem implements Disposable { } } +export namespace Item { + export function is (item: spec.Item, type: spec.ItemType): item is T { + return item.type === type; + } + + export function isFilter (item: spec.Item): item is spec.FilterItem { + return item.type === spec.ItemType.filter; + } + + export function isComposition (item: spec.Item): item is spec.CompositionItem { + return item.type === spec.ItemType.composition; + } + + export function isParticle (item: spec.Item): item is spec.ParticleItem { + return item.type === spec.ItemType.particle; + } + + export function isNull (item: spec.Item): item is spec.NullItem { + return item.type === spec.ItemType.null; + } +} + /** * 根据元素的类型创建对应的 `VFXItem` 实例 * @param props @@ -687,8 +746,8 @@ export function createVFXItem (props: VFXItemProps, composition: Composition): V pluginName = 'text'; break; - case VFX_ITEM_TYPE_TREE: - pluginName = VFX_ITEM_TYPE_TREE; + case spec.ItemType.tree: + pluginName = 'tree'; break; default: diff --git a/packages/effects-helper/package.json b/packages/effects-helper/package.json index aa6fab963..b5dd79ec8 100644 --- a/packages/effects-helper/package.json +++ b/packages/effects-helper/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/effects-helper", - "version": "1.0.1", + "version": "1.1.0", "description": "Galacean Effects runtime helper for the web", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", diff --git a/packages/effects-threejs/package.json b/packages/effects-threejs/package.json index 450a5d050..23f34e7a7 100644 --- a/packages/effects-threejs/package.json +++ b/packages/effects-threejs/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/effects-threejs", - "version": "1.0.1", + "version": "1.1.0", "description": "Galacean Effects runtime threejs plugin for the web", "module": "./dist/index.mjs", "main": "./dist/index.js", diff --git a/packages/effects-threejs/src/material/three-material.ts b/packages/effects-threejs/src/material/three-material.ts index 9dad26b2e..13d50bd91 100644 --- a/packages/effects-threejs/src/material/three-material.ts +++ b/packages/effects-threejs/src/material/three-material.ts @@ -1,9 +1,16 @@ -import type { MaterialProps, Texture, UniformValue, MaterialDestroyOptions, UndefinedAble, Engine } from '@galacean/effects-core'; -import { GPUCapability, Material, maxSpriteMeshItemCount, spec } from '@galacean/effects-core'; +import type { MaterialProps, Texture, UniformValue, MaterialDestroyOptions, UndefinedAble, Engine, math } from '@galacean/effects-core'; +import { Material, maxSpriteMeshItemCount, spec } from '@galacean/effects-core'; import * as THREE from 'three'; import type { ThreeTexture } from '../three-texture'; import { CONSTANT_MAP_BLEND, CONSTANT_MAP_DEPTH, CONSTANT_MAP_STENCIL_FUNC, CONSTANT_MAP_STENCIL_OP, TEXTURE_UNIFORM_MAP } from './three-material-util'; +type Matrix4 = math.Matrix4; +type Vector2 = math.Vector2; +type Vector3 = math.Vector3; +type Vector4 = math.Vector4; +type Matrix3 = math.Matrix3; +type Quaternion = math.Quaternion; + /** * THREE 抽象材质类 */ @@ -330,11 +337,11 @@ export class ThreeMaterial extends Material { getVector4Array (name: string): number[] { return this.uniforms[name].value as number[]; } - setVector4Array (name: string, array: spec.vec4[]): void { + setVector4Array (name: string, array: Vector4[]): void { let value: number[] = []; for (const v of array) { - value = value.concat(v); + value = value.concat(v.toArray()); } this.setUniform(name, value); @@ -343,11 +350,11 @@ export class ThreeMaterial extends Material { getMatrixArray (name: string): number[] | null { return this.uniforms[name].value; } - setMatrixArray (name: string, array: spec.mat4[]): void { + setMatrixArray (name: string, array: Matrix4[]): void { let value: number[] = []; for (const v of array) { - value = value.concat(v); + value = value.concat(v.elements); } this.setUniform(name, value); } @@ -355,34 +362,41 @@ export class ThreeMaterial extends Material { this.setUniform(name, array); } - getMatrix (name: string): spec.mat4 | null { + getMatrix (name: string): Matrix4 | null { return this.uniforms[name].value; } - setMatrix (name: string, value: spec.mat4): void { + setMatrix (name: string, value: Matrix4): void { this.setUniform(name, value); } - setMatrix3 (name: string, value: spec.mat3): void { + setMatrix3 (name: string, value: Matrix3): void { this.setUniform(name, value); } - getVector2 (name: string): spec.vec2 | null { + getVector2 (name: string): Vector2 | null { return this.uniforms[name].value; } - setVector2 (name: string, value: spec.vec2): void { + setVector2 (name: string, value: Vector2): void { + this.setUniform(name, value); + } + + getVector3 (name: string): Vector3 { + return this.uniforms[name].value as Vector3; + } + setVector3 (name: string, value: Vector3): void { this.setUniform(name, value); } - getVector3 (name: string): spec.vec3 { - return this.uniforms[name].value as spec.vec3; + getVector4 (name: string): Vector4 | null { + return this.uniforms[name].value; } - setVector3 (name: string, value: spec.vec3): void { + setVector4 (name: string, value: Vector4): void { this.setUniform(name, value); } - getVector4 (name: string): spec.vec4 | null { + getQuaternion (name: string): Quaternion | null { return this.uniforms[name].value; } - setVector4 (name: string, value: spec.vec4): void { + setQuaternion (name: string, value: Quaternion): void { this.setUniform(name, value); } @@ -411,7 +425,7 @@ export class ThreeMaterial extends Material { return !!this.uniforms[name]; } - private setUniform (name: string, value: THREE.Texture | number | number[] | spec.mat4 | spec.vec2 | spec.vec3 | spec.vec4 | spec.vec4[]) { + private setUniform (name: string, value: THREE.Texture | number | number[] | Matrix4 | Matrix3 | Quaternion | Vector2 | Vector3 | Vector4 | Vector4[]) { const uniform = new THREE.Uniform(value); this.uniforms[name] = this.material.uniforms[name] = uniform; diff --git a/packages/effects-threejs/src/three-mesh.ts b/packages/effects-threejs/src/three-mesh.ts index a394176dd..355fb1342 100644 --- a/packages/effects-threejs/src/three-mesh.ts +++ b/packages/effects-threejs/src/three-mesh.ts @@ -70,7 +70,7 @@ export class ThreeMesh extends Mesh implements Sortable { * @param val - 可见性开关 */ override setVisible (val: boolean): void { - this.mesh.visible = !val; + this.mesh.visible = val; } /** @@ -79,7 +79,7 @@ export class ThreeMesh extends Mesh implements Sortable { * @returns */ override getVisible (): boolean { - return !this.mesh.visible; + return this.mesh.visible; } /** diff --git a/packages/effects-threejs/src/three-render-frame.ts b/packages/effects-threejs/src/three-render-frame.ts index 7c1ce1063..0c7590baf 100644 --- a/packages/effects-threejs/src/three-render-frame.ts +++ b/packages/effects-threejs/src/three-render-frame.ts @@ -87,10 +87,10 @@ export class ThreeRenderFrame extends RenderFrame { const camera = this.camera; uniformSemanticsMap = { - // 'effects_ObjectToWorld': mesh.worldMatrix, - 'effects_MatrixV': camera.getViewMatrix(), - 'effects_MatrixVP': camera.getViewProjectionMatrix(), - 'effects_MatrixInvV': camera.getInverseViewMatrix(), + 'effects_ObjectToWorld': mesh.worldMatrix.toArray(), + 'effects_MatrixV': camera.getViewMatrix().toArray(), + 'effects_MatrixVP': camera.getViewProjectionMatrix().toArray(), + 'effects_MatrixInvV': camera.getInverseViewMatrix().toArray(), }; } @@ -138,9 +138,9 @@ export class ThreeRenderFrame extends RenderFrame { group.children.forEach(mesh => { const material = (mesh as THREE.Mesh).material as THREE.ShaderMaterial; - setUniformValue(material.uniforms, 'effects_MatrixInvV', camera.getInverseViewMatrix()); - setUniformValue(material.uniforms, 'effects_MatrixVP', camera.getViewProjectionMatrix()); - setUniformValue(material.uniforms, 'effects_MatrixV', camera.getViewMatrix()); + setUniformValue(material.uniforms, 'effects_MatrixInvV', camera.getInverseViewMatrix().toArray()); + setUniformValue(material.uniforms, 'effects_MatrixVP', camera.getViewProjectionMatrix().toArray()); + setUniformValue(material.uniforms, 'effects_MatrixV', camera.getViewMatrix().toArray()); material.uniformsNeedUpdate = true; }); } diff --git a/packages/effects-webgl/package.json b/packages/effects-webgl/package.json index fad97a22d..764839a8b 100644 --- a/packages/effects-webgl/package.json +++ b/packages/effects-webgl/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/effects-webgl", - "version": "1.0.1", + "version": "1.1.0", "description": "Galacean Effects runtime webgl for the web", "types": "./dist/index.d.ts", "files": [ diff --git a/packages/effects-webgl/src/gl-material.ts b/packages/effects-webgl/src/gl-material.ts index 24fcc9fdb..915d707a9 100644 --- a/packages/effects-webgl/src/gl-material.ts +++ b/packages/effects-webgl/src/gl-material.ts @@ -4,11 +4,10 @@ import type { MaterialStates, UndefinedAble, Texture, - spec, Engine, GlobalUniforms, } from '@galacean/effects-core'; -import { DestroyOptions, Material, assertExist, mat4create, throwDestroyedError } from '@galacean/effects-core'; +import { DestroyOptions, Material, assertExist, throwDestroyedError, math } from '@galacean/effects-core'; import { GLMaterialState } from './gl-material-state'; import type { GLPipelineContext } from './gl-pipeline-context'; import type { GLShader } from './gl-shader'; @@ -16,17 +15,27 @@ import type { GLTexture } from './gl-texture'; import type { GLEngine } from './gl-engine'; import type { GLRenderer } from './gl-renderer'; +type Vector2 = math.Vector2; +type Vector3 = math.Vector3; +type Vector4 = math.Vector4; +type Matrix3 = math.Matrix3; +type Matrix4 = math.Matrix4; +type Quaternion = math.Quaternion; + +const { Vector4, Matrix4 } = math; + export class GLMaterial extends Material { shader: GLShader; // material存放的uniform数据。 floats: Record = {}; ints: Record = {}; - vector2s: Record = {}; - vector3s: Record = {}; - vector4s: Record = {}; - matrices: Record = {}; - matrice3s: Record = {}; + vector2s: Record = {}; + vector3s: Record = {}; + vector4s: Record = {}; + quaternions: Record = {}; + matrices: Record = {}; + matrice3s: Record = {}; textures: Record = {}; floatArrays: Record = {}; vector4Arrays: Record = {}; @@ -281,7 +290,7 @@ export class GLMaterial extends Material { } if (globalUniforms) { - // 设置全局 uniform + // 设置全局 uniform for (name in globalUniforms.floats) { this.shader.setFloat(name, globalUniforms.floats[name]); } @@ -320,6 +329,9 @@ export class GLMaterial extends Material { for (name in this.vector4s) { this.shader.setVector4(name, this.vector4s[name]); } + for (name in this.quaternions) { + this.shader.setQuaternion(name, this.quaternions[name]); + } for (name in this.matrices) { this.shader.setMatrix(name, this.matrices[name]); } @@ -358,38 +370,46 @@ export class GLMaterial extends Material { this.floatArrays[name] = value; } - public getVector2 (name: string): spec.vec2 | null { + public getVector2 (name: string): Vector2 | null { return this.vector2s[name]; } - public setVector2 (name: string, value: spec.vec2): void { + public setVector2 (name: string, value: Vector2): void { this.checkUniform(name); this.vector2s[name] = value; } - public getVector3 (name: string): spec.vec3 | null { + public getVector3 (name: string): Vector3 | null { return this.vector3s[name]; } - public setVector3 (name: string, value: spec.vec3): void { + public setVector3 (name: string, value: Vector3): void { this.checkUniform(name); this.vector3s[name] = value; } - public getVector4 (name: string): spec.vec4 | null { + public getVector4 (name: string): Vector4 | null { return this.vector4s[name]; } - public setVector4 (name: string, value: spec.vec4): void { + public setVector4 (name: string, value: Vector4): void { this.checkUniform(name); this.vector4s[name] = value; } - public getMatrix (name: string): spec.mat4 | null { + public getQuaternion (name: string): Quaternion | null { + return this.quaternions[name]; + } + public setQuaternion (name: string, value: Quaternion): void { + this.checkUniform(name); + this.quaternions[name] = value; + } + + public getMatrix (name: string): Matrix4 | null { return this.matrices[name]; } - public setMatrix (name: string, value: spec.mat4): void { + public setMatrix (name: string, value: Matrix4): void { this.checkUniform(name); this.matrices[name] = value; } - public setMatrix3 (name: string, value: spec.mat3): void { + public setMatrix3 (name: string, value: Matrix3): void { this.checkUniform(name); this.matrice3s[name] = value; } @@ -397,23 +417,23 @@ export class GLMaterial extends Material { public getVector4Array (name: string): number[] { return this.vector4Arrays[name]; } - public setVector4Array (name: string, array: spec.vec4[]): void { + public setVector4Array (name: string, array: Vector4[]): void { this.checkUniform(name); this.vector4Arrays[name] = []; for (const v of array) { - this.vector4Arrays[name].push(v[0], v[1], v[2], v[3]); + this.vector4Arrays[name].push(v.x, v.y, v.z, v.w); } } public getMatrixArray (name: string): number[] | null { return this.matrixArrays[name]; } - public setMatrixArray (name: string, array: spec.mat4[]): void { + public setMatrixArray (name: string, array: Matrix4[]): void { this.checkUniform(name); this.matrixArrays[name] = []; for (const m of array) { for (let i = 0; i < 16; i++) { - this.matrixArrays[name].push(m[i]); + this.matrixArrays[name].push(m.elements[i]); } } } @@ -451,6 +471,7 @@ export class GLMaterial extends Material { clonedMaterial.vector2s = this.vector2s; clonedMaterial.vector3s = this.vector3s; clonedMaterial.vector4s = this.vector4s; + clonedMaterial.quaternions = this.quaternions; clonedMaterial.matrices = this.matrices; clonedMaterial.textures = this.textures; clonedMaterial.floatArrays = this.floatArrays; @@ -488,29 +509,33 @@ export class GLMaterial extends Material { for (name in material.vector4s) { this.setVector4(name, material.vector4s[name]); } + for (name in material.quaternions) { + this.setQuaternion(name, material.quaternions[name]); + } for (name in material.matrices) { this.setMatrix(name, material.matrices[name]); } for (name in material.vector4Arrays) { - const vec4Array: spec.vec4[] = []; + const vec4Array: Vector4[] = []; - for (let i = 0;i < material.vector4Arrays[name].length;i += 4) { - vec4Array.push([material.vector4Arrays[name][i], + for (let i = 0; i < material.vector4Arrays[name].length; i += 4) { + vec4Array.push(new Vector4( + material.vector4Arrays[name][i], material.vector4Arrays[name][i + 1], material.vector4Arrays[name][i + 2], material.vector4Arrays[name][i + 3], - ]); + )); } this.setVector4Array(name, vec4Array); } for (name in material.matrixArrays) { - const mat4Array: spec.mat4[] = []; + const mat4Array: Matrix4[] = []; - for (let i = 0;i < material.matrixArrays[name].length;i += 16) { - const matrix: spec.mat4 = mat4create(); + for (let i = 0; i < material.matrixArrays[name].length; i += 16) { + const matrix = Matrix4.fromIdentity(); - for (let j = 0;j < 16;j++) { - matrix[j] = (material.matrixArrays[name][i + j]); + for (let j = 0; j < 16; j++) { + matrix.elements[j] = (material.matrixArrays[name][i + j]); } mat4Array.push(matrix); } @@ -545,6 +570,7 @@ export class GLMaterial extends Material { this.vector2s = {}; this.vector3s = {}; this.vector4s = {}; + this.quaternions = {}; this.matrices = {}; this.matrice3s = {}; this.textures = {}; diff --git a/packages/effects-webgl/src/gl-pipeline-context.ts b/packages/effects-webgl/src/gl-pipeline-context.ts index 414e184f4..19409c9eb 100644 --- a/packages/effects-webgl/src/gl-pipeline-context.ts +++ b/packages/effects-webgl/src/gl-pipeline-context.ts @@ -1,9 +1,16 @@ -import type { Disposable, Texture } from '@galacean/effects-core'; +import type { Disposable, Texture, math } from '@galacean/effects-core'; import { glContext } from '@galacean/effects-core'; import { GLShaderLibrary } from './gl-shader-library'; import type { GLTexture } from './gl-texture'; import type { GLEngine } from './gl-engine'; +type Vector2 = math.Vector2; +type Vector3 = math.Vector3; +type Vector4 = math.Vector4; +type Matrix3 = math.Matrix3; +type Matrix4 = math.Matrix4; +type Quaternion = math.Quaternion; + export type Nullable = T | null; export class GLPipelineContext implements Disposable { @@ -525,16 +532,20 @@ export class GLPipelineContext implements Disposable { this.gl.uniform1fv(uniform, value); } - public setVector2 (uniform: Nullable, value: number[]) { - this.setFloat2(uniform, value[0], value[1]); + public setVector2 (uniform: Nullable, value: Vector2) { + this.setFloat2(uniform, value.x, value.y); + } + + public setVector3 (uniform: Nullable, value: Vector3) { + this.setFloat3(uniform, value.x, value.y, value.z); } - public setVector3 (uniform: Nullable, value: number[]) { - this.setFloat3(uniform, value[0], value[1], value[2]); + public setVector4 (uniform: Nullable, value: Vector4) { + this.setFloat4(uniform, value.x, value.y, value.z, value.w); } - public setVector4 (uniform: Nullable, value: number[]) { - this.setFloat4(uniform, value[0], value[1], value[2], value[3]); + public setQuaternion (uniform: Nullable, value: Quaternion) { + this.setFloat4(uniform, value.x, value.y, value.z, value.w); } public setVector4Array (uniform: Nullable, array: number[]) { @@ -542,14 +553,14 @@ export class GLPipelineContext implements Disposable { this.gl.uniform4fv(uniform, array); } - public setMatrix (uniform: Nullable, value: number[]) { - if (!uniform || value.length != 16) { return; } - this.gl.uniformMatrix4fv(uniform, false, value); + public setMatrix (uniform: Nullable, value: Matrix4) { + if (!uniform) { return; } + this.gl.uniformMatrix4fv(uniform, false, value.elements); } - public setMatrix3 (uniform: Nullable, value: number[]) { - if (!uniform || value.length != 9) { return; } - this.gl.uniformMatrix3fv(uniform, false, value); + public setMatrix3 (uniform: Nullable, value: Matrix3) { + if (!uniform) { return; } + this.gl.uniformMatrix3fv(uniform, false, value.elements); } public setMatrixArray (uniform: Nullable, array: number[]) { diff --git a/packages/effects-webgl/src/gl-renderer.ts b/packages/effects-webgl/src/gl-renderer.ts index b37c31951..b787606ef 100644 --- a/packages/effects-webgl/src/gl-renderer.ts +++ b/packages/effects-webgl/src/gl-renderer.ts @@ -1,15 +1,5 @@ -import type { Disposable, FrameBuffer, Geometry, LostHandler, Material, Mesh, RenderFrame, RenderPass, RenderPassClearAction, RenderPassStoreAction, RestoreHandler, ShaderLibrary, mat4 } from '@galacean/effects-core'; -import { - assertExist, - glContext, - Renderer, - RenderPassAttachmentStorageType, - TextureLoadAction, - TextureSourceType, - FilterMode, - RenderTextureFormat, - sortByOrder, -} from '@galacean/effects-core'; +import type { Disposable, FrameBuffer, Geometry, LostHandler, Material, Mesh, RenderFrame, RenderPass, RenderPassClearAction, RenderPassStoreAction, RestoreHandler, ShaderLibrary, math } from '@galacean/effects-core'; +import { assertExist, glContext, Renderer, RenderPassAttachmentStorageType, TextureLoadAction, TextureSourceType, FilterMode, RenderTextureFormat, sortByOrder } from '@galacean/effects-core'; import { ExtWrap } from './ext-wrap'; import { GLContextManager } from './gl-context-manager'; import { GLFrameBuffer } from './gl-frame-buffer'; @@ -18,6 +8,8 @@ import { GLRendererInternal } from './gl-renderer-internal'; import { GLTexture } from './gl-texture'; import { GLEngine } from './gl-engine'; +type Matrix4 = math.Matrix4; + export class GLRenderer extends Renderer implements Disposable { glRenderer: GLRendererInternal; extension: ExtWrap; @@ -144,7 +136,7 @@ export class GLRenderer extends Renderer implements Disposable { // console.error(`mesh ${mesh.name} destroyed`, mesh); continue; } - if (mesh.getVisible()) { + if (!mesh.getVisible()) { continue; } if (!mesh.material) { @@ -170,7 +162,7 @@ export class GLRenderer extends Renderer implements Disposable { this.renderingData.currentFrame.globalUniforms.ints[name] = value; } - override setGlobalMatrix (name: string, value: mat4) { + override setGlobalMatrix (name: string, value: Matrix4) { this.checkGlobalUniform(name); this.renderingData.currentFrame.globalUniforms.matrices[name] = value; } diff --git a/packages/effects-webgl/src/gl-shader.ts b/packages/effects-webgl/src/gl-shader.ts index bf213d2ac..a1e7fb226 100644 --- a/packages/effects-webgl/src/gl-shader.ts +++ b/packages/effects-webgl/src/gl-shader.ts @@ -1,9 +1,16 @@ -import type { ShaderCompileResult, ShaderWithSource, Texture, Engine } from '@galacean/effects-core'; +import type { ShaderCompileResult, ShaderWithSource, Texture, Engine, math } from '@galacean/effects-core'; import { Shader } from '@galacean/effects-core'; import type { GLProgram } from './gl-program'; import type { GLPipelineContext } from './gl-pipeline-context'; import type { GLEngine } from './gl-engine'; +type Vector2 = math.Vector2; +type Vector3 = math.Vector3; +type Vector4 = math.Vector4; +type Matrix3 = math.Matrix3; +type Matrix4 = math.Matrix4; +type Quaternion = math.Quaternion; + export class GLShader extends Shader { pipelineContext: GLPipelineContext; program: GLProgram; @@ -42,19 +49,22 @@ export class GLShader extends Shader { public setTexture (name: string, texture: Texture) { this.pipelineContext.setTexture(this.uniformLocations[name], this.samplerChannels[name], texture); } - public setVector2 (name: string, value: number[]) { + public setVector2 (name: string, value: Vector2) { this.pipelineContext.setVector2(this.uniformLocations[name], value); } - public setVector3 (name: string, value: number[]) { + public setVector3 (name: string, value: Vector3) { this.pipelineContext.setVector3(this.uniformLocations[name], value); } - public setVector4 (name: string, value: number[]) { + public setVector4 (name: string, value: Vector4) { this.pipelineContext.setVector4(this.uniformLocations[name], value); } - public setMatrix (name: string, value: number[]) { + public setQuaternion (name: string, value: Quaternion) { + this.pipelineContext.setQuaternion(this.uniformLocations[name], value); + } + public setMatrix (name: string, value: Matrix4) { this.pipelineContext.setMatrix(this.uniformLocations[name], value); } - public setMatrix3 (name: string, value: number[]) { + public setMatrix3 (name: string, value: Matrix3) { this.pipelineContext.setMatrix3(this.uniformLocations[name], value); } public setVector4Array (name: string, array: number[]) { diff --git a/packages/effects/package.json b/packages/effects/package.json index 8e79b6a9d..842dbcc6e 100644 --- a/packages/effects/package.json +++ b/packages/effects/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/effects", - "version": "1.0.1", + "version": "1.1.0", "description": "Galacean Effects runtime player for the web", "module": "./dist/index.mjs", "main": "./dist/index.js", diff --git a/packages/effects/src/player.ts b/packages/effects/src/player.ts index fe1ff21b3..cfeaa57fa 100644 --- a/packages/effects/src/player.ts +++ b/packages/effects/src/player.ts @@ -1,6 +1,6 @@ import type { - Disposable, GLType, JSONValue, LostHandler, MessageItem, RestoreHandler, SceneLoadOptions, - Texture2DSourceOptionsVideo, TouchEventType, VFXItem, VFXItemContent, Scene, GPUCapability, + Disposable, GLType, JSONValue, LostHandler, MessageItem, RestoreHandler, + SceneLoadOptions, Texture2DSourceOptionsVideo, TouchEventType, VFXItem, VFXItemContent, Scene, math, GPUCapability, } from '@galacean/effects-core'; import { Ticker, @@ -34,7 +34,7 @@ export interface ItemClickedData { name: string, player: Player, id: string, - hitPositions: spec.vec3[], + hitPositions: math.Vector3[], compositionId: number, } @@ -897,15 +897,10 @@ export class Player implements Disposable, LostHandler, RestoreHandler { const behavior = regions[i].behavior || spec.InteractBehavior.NOTIFY; if (behavior === spec.InteractBehavior.NOTIFY) { - const { name, id, hitPositions } = regions[i]; - this.handleItemClicked?.({ - name, - player: this, + ...regions[i], composition: composition.name, - id, - compositionId: composition.id, - hitPositions, + player: this, }); } else if (behavior === spec.InteractBehavior.RESUME_PLAYER) { void this.resume(); diff --git a/plugin-packages/alipay-downgrade/package.json b/plugin-packages/alipay-downgrade/package.json index f490b5cff..e5e0f4776 100644 --- a/plugin-packages/alipay-downgrade/package.json +++ b/plugin-packages/alipay-downgrade/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/effects-plugin-alipay-downgrade", - "version": "1.0.1", + "version": "1.1.0", "description": "Galacean Effects player downgrade plugin for Alipay", "module": "./dist/index.mjs", "main": "./dist/index.js", diff --git a/plugin-packages/alipay-downgrade/vite.config.js b/plugin-packages/alipay-downgrade/vite.config.js index 1b0bc81da..dc9e351f4 100644 --- a/plugin-packages/alipay-downgrade/vite.config.js +++ b/plugin-packages/alipay-downgrade/vite.config.js @@ -36,6 +36,7 @@ export default defineConfig(({ mode }) => { plugins: [ legacy({ targets: ['iOS >= 9'], + modernPolyfills: ['es/global-this'], }), glslInner(), tsconfigPaths(), diff --git a/plugin-packages/editor-gizmo/package.json b/plugin-packages/editor-gizmo/package.json index ec37a0d93..45400cf67 100644 --- a/plugin-packages/editor-gizmo/package.json +++ b/plugin-packages/editor-gizmo/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/effects-plugin-editor-gizmo", - "version": "1.0.1", + "version": "1.1.0", "description": "Galacean Effects player editor gizmo plugin", "module": "./dist/index.mjs", "main": "./dist/index.js", diff --git a/plugin-packages/editor-gizmo/src/geometry/cylinder.ts b/plugin-packages/editor-gizmo/src/geometry/cylinder.ts index acb583574..5e7161f3d 100644 --- a/plugin-packages/editor-gizmo/src/geometry/cylinder.ts +++ b/plugin-packages/editor-gizmo/src/geometry/cylinder.ts @@ -1,6 +1,8 @@ -import { vecNormalize } from '../math/vec'; +import { math } from '@galacean/effects'; import { GeometryData } from './geometry'; +const { Vector3 } = math; + /** * */ @@ -63,9 +65,9 @@ export class CylinderGeometryData extends GeometryData { points.push(px, py, pz); - const normal = vecNormalize([sinTheta, slope, cosTheta]); + const normal = new Vector3(sinTheta, slope, cosTheta).normalize(); - normals.push(...normal); + normals.push(...normal.toArray()); uvs.push(u, 1 - v); diff --git a/plugin-packages/editor-gizmo/src/geometry/frustum.ts b/plugin-packages/editor-gizmo/src/geometry/frustum.ts index 95942cffa..e4ef8eff2 100644 --- a/plugin-packages/editor-gizmo/src/geometry/frustum.ts +++ b/plugin-packages/editor-gizmo/src/geometry/frustum.ts @@ -1,7 +1,9 @@ import type { spec } from '@galacean/effects'; -import { mat3FromRotationZYX, vec3MulMat3ByPoint, vecAdd } from '../math/vec'; +import { math } from '@galacean/effects'; import { GeometryData } from './geometry'; +const { Euler, Vector3, Matrix4 } = math; + /** * */ @@ -29,19 +31,21 @@ export class FrustumGeometryData extends GeometryData { const ratio = aspect; const halfY = far * Math.tan(fovY / 2); const halfX = halfY * ratio; - const point1 = vecAdd([], position, [halfX, halfY, far]) as [number, number, number]; - const point2 = vecAdd([], position, [-halfX, halfY, far]) as [number, number, number]; - const point3 = vecAdd([], position, [-halfX, -halfY, far]) as [number, number, number]; - const point4 = vecAdd([], position, [halfX, -halfY, far]) as [number, number, number]; - - const matrix = mat3FromRotationZYX([], rotation[0], rotation[1], rotation[2]); - - const result1 = vec3MulMat3ByPoint([], point1, matrix, position); - const result2 = vec3MulMat3ByPoint([], point2, matrix, position); - const result3 = vec3MulMat3ByPoint([], point3, matrix, position); - const result4 = vec3MulMat3ByPoint([], point4, matrix, position); + const point1 = new Vector3(halfX, halfY, far); + const point2 = new Vector3(-halfX, halfY, far); + const point3 = new Vector3(-halfX, -halfY, far); + const point4 = new Vector3(halfX, -halfY, far); + const matrix = Euler.fromArray(rotation).toMatrix4(new Matrix4()); + const result1 = matrix.transformPoint(point1).add(position); + const result2 = matrix.transformPoint(point2).add(position); + const result3 = matrix.transformPoint(point3).add(position); + const result4 = matrix.transformPoint(point4).add(position); - this.points.push(...position, ...result1, ...result2, ...result3, ...result4); + this.points.push( + ...position, + ...result1.toArray(), ...result2.toArray(), + ...result3.toArray(), ...result4.toArray() + ); this.indices.push(0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 2, 3, 3, 4, 4, 1); } } diff --git a/plugin-packages/editor-gizmo/src/geometry/sphere.ts b/plugin-packages/editor-gizmo/src/geometry/sphere.ts index d2b691a6a..8b692318e 100644 --- a/plugin-packages/editor-gizmo/src/geometry/sphere.ts +++ b/plugin-packages/editor-gizmo/src/geometry/sphere.ts @@ -1,5 +1,7 @@ +import { math } from '@galacean/effects'; import { GeometryData } from './geometry'; -import { vecNormalize } from '../math/vec'; + +const { Vector3 } = math; /** * @@ -57,9 +59,9 @@ export class SphereGeometryData extends GeometryData { this.points.push(x, y, z); // normal - const normal = vecNormalize([x, y, z]); + const normal = new Vector3(x, y, z).normalize(); - this.normals.push(...normal); + this.normals.push(...normal.toArray()); // uv this.uvs.push(u + uOffset, 1 - v); diff --git a/plugin-packages/editor-gizmo/src/geometry/torus.ts b/plugin-packages/editor-gizmo/src/geometry/torus.ts index 90ea5886c..ae19f7366 100644 --- a/plugin-packages/editor-gizmo/src/geometry/torus.ts +++ b/plugin-packages/editor-gizmo/src/geometry/torus.ts @@ -1,6 +1,8 @@ -import { vecNormalize, vecSub } from '../math/vec'; +import { math } from '@galacean/effects'; import { GeometryData } from './geometry'; +const { Vector3 } = math; + /** * */ @@ -39,9 +41,11 @@ export class TorusGeometryData extends GeometryData { const cx = radius * Math.cos(u); const cy = radius * Math.sin(u); - const normal = vecNormalize(vecSub([], [px, py, pz], [cx, cy, 0])); + const p = new Vector3(px, py, pz); + const c = new Vector3(cx, cy, 0); + const normal = p.subtract(c).normalize(); - this.normals.push(...normal); + this.normals.push(...normal.toArray()); this.uvs.push(i / tubularSegments); this.uvs.push(j / radialSegments); diff --git a/plugin-packages/editor-gizmo/src/gizmo-vfx-item.ts b/plugin-packages/editor-gizmo/src/gizmo-vfx-item.ts index f291b8cd3..96820e9d4 100644 --- a/plugin-packages/editor-gizmo/src/gizmo-vfx-item.ts +++ b/plugin-packages/editor-gizmo/src/gizmo-vfx-item.ts @@ -1,31 +1,24 @@ -import type { Engine, GeometryDrawMode, HitTestCustomParams, Mesh, Ray, SpriteMesh, SpriteVFXItem, Texture, Triangle } from '@galacean/effects'; -import { spec, HitTestType, Transform, VFXItem, glContext, intersectRayBox, intersectRaySphere, intersectRayTriangle, assertExist } from '@galacean/effects'; +import type { Engine, GeometryDrawMode, HitTestCustomParams, Mesh, SpriteMesh, SpriteVFXItem, Texture } from '@galacean/effects'; +import { spec, HitTestType, Transform, VFXItem, glContext, assertExist, math } from '@galacean/effects'; import type { GizmoVFXItemOptions } from './define'; import { GizmoSubType, GizmoVFXItemType } from './define'; import { createModeWireframe, updateWireframeMesh, WireframeGeometryType } from './wireframe'; import { createMeshFromShape } from './shape'; import { createMeshFromSubType } from './mesh'; -import { - vecSquareDistance, - vec3MulMat4, - vecSub, - mat4MulMat4, - computeOrthographicOffCenter, - vec3ToVec2, - vec4MulMat4, - vecAdd, - vecNormalize, - vecMulScalar, -} from './math/vec'; +import { computeOrthographicOffCenter } from './math-utils'; import { intersectRayLine } from './raycast'; import { moveToPointWidthFixDistance } from './util'; const constants = glContext; -type mat4 = spec.mat4; type vec3 = spec.vec3; -type vec2 = spec.vec2; type vec4 = spec.vec4; +type Ray = math.Ray; +type TriangleLike = math.TriangleLike; +type Vector2 = math.Vector2; +type Vector3 = math.Vector3; +type Matrix4 = math.Matrix4; +const { Vector2, Vector3, Matrix4, Ray, Quaternion } = math; /** * 包围盒类型 @@ -43,7 +36,7 @@ export enum BoundingType { export interface GizmoItemBoundingLine { type: BoundingType.line, // 线条包围盒两端点 - points: [vec3, vec3], + points: [Vector3, Vector3], // 射线与线条距离阈值 width: number, } @@ -54,9 +47,9 @@ export interface GizmoItemBoundingLine { export interface GizmoItemBoundingBox { type: BoundingType.box, //包围形状相对于元素位置的偏移,默认[0,0,0] - center?: vec3, + center?: Vector3, //包围盒的xyz长度,当type为box时生效 - size: vec3, + size: Vector3, } /** @@ -65,7 +58,7 @@ export interface GizmoItemBoundingBox { export interface GizmoItemBoundingSphere { type: BoundingType.sphere, //包围形状相对于元素位置的偏移,默认[0,0,0] - center?: vec3, + center?: Vector3, //包围球的半径,当type为sphere时生效 radius: number, } @@ -75,7 +68,7 @@ export interface GizmoItemBoundingSphere { */ export interface GizmoItemBoundingTriangle { type: BoundingType.triangle, - triangles: Triangle[], + triangles: TriangleLike[], backfaceCulling?: boolean, } @@ -100,8 +93,8 @@ export class GizmoVFXItem extends VFXItem { contents?: Map; targetItem?: VFXItem; boundingMap: Map = new Map(); - hitBounding?: { key: string, position: vec3 }; - mat: mat4 = [] as unknown as mat4; + hitBounding?: { key: string, position: Vector3 }; + mat = Matrix4.fromIdentity(); wireframeMesh?: Mesh; spriteMesh?: SpriteMesh; @@ -229,9 +222,9 @@ export class GizmoVFXItem extends VFXItem { // meshesToAdd.push(mesh); // } - if (item.type === spec.ItemType.sprite || item.type === spec.ItemType.filter) { + if (VFXItem.isSprite(item) || VFXItem.isFilterSprite(item)) { const color = this.color.slice(); - const mesh = this.spriteMesh = (item as SpriteVFXItem).createWireframeMesh(item.content, color as vec4); + const mesh = this.spriteMesh = item.createWireframeMesh(item.content, color as vec4); this.wireframeMesh = mesh.mesh; meshesToAdd.push(mesh.mesh); @@ -344,37 +337,37 @@ export class GizmoVFXItem extends VFXItem { if (this.contents) { // 组合几何体 // const targetTransform = this.targetItem.transform.clone(); - const worldPos: vec3 = [0, 0, 0]; - const worldQuat: vec4 = [0, 0, 0, 0]; - const worldSca: vec3 = [1, 1, 1]; + const worldPos = new Vector3(); + const worldQuat = new Quaternion(); + const worldScale = new Vector3(1, 1, 1); this.targetItem.transform.assignWorldTRS(worldPos, worldQuat); const targetTransform = new Transform({ position: worldPos, quat: worldQuat, - scale: worldSca, + scale: worldScale, valid: true, }); // 移动\旋转\缩放gizmo去除近大远小 if (this.subType === GizmoSubType.rotation || this.subType === GizmoSubType.scale || this.subType === GizmoSubType.translation) { const camera = this.composition!.camera; - const cameraPos = camera.position || [0, 0, 0]; + const cameraPos = camera.position || new Vector3(); const itemPos = targetTransform.position; const newPos = moveToPointWidthFixDistance(cameraPos, itemPos); - targetTransform.setPosition(...newPos); + targetTransform.setPosition(newPos.x, newPos.y, newPos.z); } this.mat = targetTransform.getWorldMatrix(); const center = targetTransform.position; for (const [mesh, transform] of this.contents) { - let worldMat4 = [] as unknown as mat4; + let worldMat4: Matrix4; transform.parentTransform = targetTransform; - const position: vec3 = [0, 0, 0]; + const position = new Vector3(); transform.assignWorldTRS(position); // 物体和中心点在屏幕空间上投影的距离 @@ -384,7 +377,7 @@ export class GizmoVFXItem extends VFXItem { if (this.subType === GizmoSubType.viewHelper) { // 正交投影只需要计算物体在XY平面上的投影与中心点的距离 if (mesh.name === 'sprite') { - distanceToCneter = Math.pow(vecSquareDistance(vec3ToVec2(position), vec3ToVec2(center)), 0.5); + distanceToCneter = position.toVector2().distance(center.toVector2()); theta = (this.boundingMap.get('posX') as GizmoItemBoundingSphere).radius; } // 将物体挂到相机的transform上 @@ -397,7 +390,7 @@ export class GizmoVFXItem extends VFXItem { const height = width / aspect; const cameraModelMat4 = this.composition!.camera.getInverseViewMatrix(); const padding = this.size.padding || 1; // 指定viewHelper与屏幕右上角的边距 - let localMat = [] as unknown as mat4; + let localMat: Matrix4; localMat = transform.getWorldMatrix(); const worldTransform: Transform = new Transform({ @@ -405,9 +398,9 @@ export class GizmoVFXItem extends VFXItem { }); worldTransform.cloneFromMatrix(localMat); - worldTransform.setPosition((position[0] + width / 2) - padding, (position[1] + height / 2) - padding, position[2]); + worldTransform.setPosition((position.x + width / 2) - padding, (position.y + height / 2) - padding, position.z); localMat = worldTransform.getWorldMatrix(); - worldMat4 = mat4MulMat4(cameraModelMat4, localMat); + worldMat4 = cameraModelMat4.clone().multiply(localMat); // 正交投影到屏幕上 const proMat5 = computeOrthographicOffCenter(-width / 2, width / 2, -height / 2, height / 2, -distance, distance); @@ -427,17 +420,17 @@ export class GizmoVFXItem extends VFXItem { // 使用distanceToCneter处理坐标轴旋转到中心点附近时的遮挡问题 if (distanceToCneter < theta) { if (distanceToCneter < theta * 0.5) { - mesh.material.setVector2('u_alpha', [0, 0]); + mesh.material.setVector2('u_alpha', new Vector2(0, 0)); } else { - mesh.material.setVector2('u_alpha', [(2 / theta) * distanceToCneter - 1, 0]); + mesh.material.setVector2('u_alpha', new Vector2((2 / theta) * distanceToCneter - 1, 0)); } } } } if (this.content) { // 基础几何体 - const worldPos: vec3 = [0, 0, 0]; - const worldQuat: vec4 = [0, 0, 0, 0]; - const worldSca: vec3 = [1, 1, 1]; + const worldPos = new Vector3(); + const worldQuat = new Quaternion(); + const worldSca = new Vector3(1, 1, 1); this.targetItem.transform.assignWorldTRS(worldPos, worldQuat); @@ -450,11 +443,11 @@ export class GizmoVFXItem extends VFXItem { if (this.subType === GizmoSubType.light || this.subType === GizmoSubType.camera) { const camera = this.composition!.camera; - const cameraPos = camera.position || [0, 0, 0]; + const cameraPos = camera.position || new Vector3(0, 0, 0); const itemPos = targetTransform.position; const newPos = moveToPointWidthFixDistance(cameraPos, itemPos); - targetTransform.setPosition(...newPos); + targetTransform.setPosition(newPos.x, newPos.y, newPos.z); } this.mat = targetTransform.getWorldMatrix(); @@ -474,15 +467,15 @@ export class GizmoVFXItem extends VFXItem { if (boundingMap.size > 0) { const boundingKeys = boundingMap.keys(); - let worldMat4 = [] as unknown as mat4; + let worldMat4: Matrix4; worldMat4 = this.transform.getWorldMatrix(); if (this.targetItem) { //const targetTransform = this.targetItem.transform.clone(); - const worldPos: vec3 = [0, 0, 0]; - const worldQuat: vec4 = [0, 0, 0, 0]; - const worldSca: vec3 = [1, 1, 1]; + const worldPos = new Vector3(); + const worldQuat = new Quaternion(); + const worldSca = new Vector3(1, 1, 1); this.targetItem.transform.assignWorldTRS(worldPos, worldQuat); @@ -506,7 +499,7 @@ export class GizmoVFXItem extends VFXItem { const itemPos = targetTransform.position; const newPos = moveToPointWidthFixDistance(cameraPos, itemPos); - targetTransform.setPosition(...newPos); + targetTransform.setPosition(newPos.x, newPos.y, newPos.z); worldMat4 = targetTransform.getWorldMatrix(); } else { worldMat4 = targetTransform.getWorldMatrix(); @@ -524,25 +517,32 @@ export class GizmoVFXItem extends VFXItem { * @param pointInCanvas 屏幕坐标 * @returns */ - collect (ray: Ray, pointInCanvas: vec2): vec3[] | void { + collect (ray: Ray, pointInCanvas: Vector2): Vector3[] | void { const hitPositions = []; self.hitBounding = undefined; for (const key of boundingKeys) { const bounding = boundingMap.get(key); - let center: vec3 = [0, 0, 0]; - const worldCenter: vec3 = [0, 0, 0]; + const center = new Vector3(); + const worldCenter = new Vector3(); if (bounding?.type === BoundingType.box || bounding?.type === BoundingType.sphere) { - center = bounding?.center ? bounding.center.slice() as vec3 : [0, 0, 0]; - vec3MulMat4(worldCenter, center, worldMat4); + if (bounding?.center) { + center.copyFrom(bounding?.center); + } + worldMat4.projectPoint(center, worldCenter); } - let position; + let position: Vector3 | undefined; switch (bounding?.type) { case BoundingType.box: // 立方体包围盒 - bounding; - position = intersectRayBox([], ray.center, ray.direction, worldCenter, bounding?.size); + { + const boxHalfSize = bounding.size.clone().multiply(0.5); + const boxMax = center.clone().add(boxHalfSize); + const boxMin = center.clone().subtract(boxHalfSize); + + position = ray.intersectBox({ min: boxMin, max: boxMax }, new Vector3()); + } break; case BoundingType.sphere: // 球体包围盒 @@ -565,78 +565,67 @@ export class GizmoVFXItem extends VFXItem { const localTransform = new Transform({ valid: true, }); - let localMat4 = [] as unknown as mat4; localTransform.cloneFromMatrix(worldMat4); const pos = localTransform.position; const padding = self.size.padding || 1; // 指定viewHelper与屏幕右上角的边距 - localTransform.setPosition((pos[0] + width / 2) - padding, (pos[1] + height / 2) - padding, pos[2]); - localMat4 = localTransform.getWorldMatrix(); - const modelMat4 = mat4MulMat4(cameraModelMat4, localMat4); + localTransform.setPosition((pos.x + width / 2) - padding, (pos.y + height / 2) - padding, pos.z); + const localMat4 = localTransform.getWorldMatrix(); + const modelMat4 = cameraModelMat4.clone().multiply(localMat4); - vec3MulMat4(worldCenter, center, modelMat4); + modelMat4.projectPoint(center, worldCenter); // 包围球中心点正交投影到屏幕上 - const screenCenter: vec4 = [0, 0, 0, 0]; - const vpMat4 = mat4MulMat4(proMat4, viewMat4); - const mvpMat4 = mat4MulMat4(vpMat4, modelMat4); - - vec4MulMat4(screenCenter, [...center, 1.0] as vec4, mvpMat4); - const screenCenterX = screenCenter[0] / screenCenter[3]; - const screenCenterY = screenCenter[1] / screenCenter[3]; + const vpMat4 = proMat4.clone().multiply(viewMat4); + const mvpMat4 = vpMat4.clone().multiply(modelMat4); + const screenCenter = mvpMat4.projectPoint(center, new Vector3()); + const screenCenterX = screenCenter.x; + const screenCenterY = screenCenter.y; // 包围球上的点正交投影到屏幕上 const radius = bounding.radius; - const point: vec3 = [0, 0, 0]; - const up = [ - localMat4[1], - localMat4[5], - localMat4[9], - ]; + const up = new Vector3( + localMat4.elements[1], + localMat4.elements[5], + localMat4.elements[9], + ).normalize(); - vecMulScalar(point, vecNormalize(up), radius); - vecAdd(point, center, point); + const point = up.clone().multiply(radius); - const screenPoint: vec4 = [0, 0, 0, 0]; + point.add(center); - vec4MulMat4(screenPoint, [...point, 1.0] as vec4, mvpMat4); - const screenPointX = screenPoint[0] / screenPoint[3]; - const screenPointY = screenPoint[1] / screenPoint[3]; + const screenPoint = mvpMat4.projectPoint(point, new Vector3()); + const screenPointX = screenPoint.x; + const screenPointY = screenPoint.y; // 计算正交投影到屏幕上的包围球的半径 - const vecRadius: vec2 = [0, 0]; + const screenCenter2 = screenCenter.toVector2(); + const screenPoint2 = screenPoint.toVector2(); + const screenRadius = screenCenter2.distance(screenPoint2); - vecSub(vecRadius, [screenCenterX, screenCenterY], [screenPointX, screenPointY]); - const screenRadius = Math.hypot(...vecRadius); - - if (Math.pow(vecSquareDistance(pointInCanvas, [screenCenterX, screenCenterY]), 0.5) < screenRadius * 1.2) { + if (pointInCanvas.distance(screenCenter2) < screenRadius * 1.2) { position = worldCenter; } } else { - bounding; - position = intersectRaySphere([], ray.center, ray.direction, worldCenter, bounding?.radius); + position = ray.intersectSphere({ center: worldCenter, radius: bounding.radius }, new Vector3()); } break; case BoundingType.triangle: {// 三角数组包围盒 const { triangles, backfaceCulling } = bounding; - const tmpPositions = []; + const tmpPositions: Vector3[] = []; let tmpPosition; for (let j = 0; j < triangles.length; j++) { const triangle = triangles[j]; - const worldTriangle: Triangle = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]; - - // 坐标转换为世界坐标 - for (let i = 0; i < 3; i++) { - const worldPosition: vec3 = [0, 0, 0]; - - vec3MulMat4(worldPosition, triangle[i], worldMat4); - worldTriangle[i] = worldPosition; - } + const worldTriangle: TriangleLike = { + p0: worldMat4.projectPoint(triangle.p0 as Vector3, new Vector3()), + p1: worldMat4.projectPoint(triangle.p1 as Vector3, new Vector3()), + p2: worldMat4.projectPoint(triangle.p2 as Vector3, new Vector3()), + }; - tmpPosition = intersectRayTriangle([], ray.center, ray.direction, worldTriangle, backfaceCulling); + tmpPosition = ray.intersectTriangle(worldTriangle, new Vector3(), backfaceCulling); if (tmpPosition) { tmpPositions.push(tmpPosition); } @@ -646,7 +635,7 @@ export class GizmoVFXItem extends VFXItem { let lastDistance: number; tmpPositions.forEach(item => { - const distance = vecSquareDistance(ray.center, item); + const distance = ray.origin.distanceSquared(item); if (!lastDistance || distance < lastDistance) { lastDistance = distance; @@ -660,7 +649,7 @@ export class GizmoVFXItem extends VFXItem { case BoundingType.line: {// 线条包围盒,将线条转换到屏幕空间,计算线条与屏幕交互点的距离,小于阈值判定为点中 const { points, width } = bounding; - position = intersectRayLine([], pointInCanvas, points, width / 2, worldMat4, self.composition!.camera); + position = intersectRayLine(new Vector3(), pointInCanvas, points, width / 2, worldMat4, self.composition!.camera); break; } @@ -671,8 +660,8 @@ export class GizmoVFXItem extends VFXItem { hitPositions.push(position); // 缓存距离相机最近的 BoundingKey if (self.hitBounding) { - const distance = vecSquareDistance(ray.center, position); - const lastDistance = vecSquareDistance(ray.center, self.hitBounding.position); + const distance = ray.origin.distanceSquared(position); + const lastDistance = ray.origin.distanceSquared(self.hitBounding.position); if (distance < lastDistance) { self.hitBounding.key = key; diff --git a/plugin-packages/editor-gizmo/src/math-utils.ts b/plugin-packages/editor-gizmo/src/math-utils.ts new file mode 100644 index 000000000..98853f221 --- /dev/null +++ b/plugin-packages/editor-gizmo/src/math-utils.ts @@ -0,0 +1,113 @@ +import { math } from '@galacean/effects'; + +type Vector2 = math.Vector2; +type Vector3 = math.Vector3; +const { Matrix4, Vector2 } = math; + +export function distanceOfPointAndLine (point: Vector2, line: [Vector2, Vector2]): { distance: number, isInLine: boolean } { + //三角形三个边长 + const AC = new Vector2(); + const BC = new Vector2(); + const AB = new Vector2(); + + AC.subtractVectors(point, line[0]); + BC.subtractVectors(point, line[1]); + AB.subtractVectors(line[1], line[0]); + const lengthAC = AC.length(); + const lengthBC = BC.length(); + const lengthAB = AB.length(); + + //利用海伦公式计算三角形面积 + //周长的一半 + const P = (lengthAC + lengthBC + lengthAB) / 2; + const allArea = Math.abs(Math.sqrt(P * (P - lengthAC) * (P - lengthBC) * (P - lengthAB))); + //普通公式计算三角形面积反推点到线的垂直距离 + const distance = (2 * allArea) / lengthAB; + const l1 = Math.sqrt(Math.pow(lengthAC, 2) - Math.pow(distance, 2)); + const l2 = Math.sqrt(Math.pow(lengthBC, 2) - Math.pow(distance, 2)); + let isInLine = false; + + if (l1 <= lengthAB && l2 <= lengthAB) { + isInLine = true; + } + + return { + distance, + isInLine, + }; +} + +export function projectionOfPointAndLine (point: Vector2, line: [Vector3, Vector3]): Vector3 { + const AC = new Vector2(); + const BC = new Vector2(); + const AB = new Vector2(); + const line0 = line[0].toVector2(); + const line1 = line[1].toVector2(); + + AC.subtractVectors(point, line0); + BC.subtractVectors(point, line1); + AB.subtractVectors(line1, line0); + const lengthAC = AC.length(); + const lengthBC = BC.length(); + const lengthAB = AB.length(); + + //利用海伦公式计算三角形面积 + //周长的一半 + const P = (lengthAC + lengthBC + lengthAB) / 2; + const allArea = Math.abs(Math.sqrt(P * (P - lengthAC) * (P - lengthBC) * (P - lengthAB))); + //普通公式计算三角形面积反推点到线的垂直距离 + const distance = (2 * allArea) / lengthAB; + const l1 = Math.sqrt(Math.pow(lengthAC, 2) - Math.pow(distance, 2)); + const l2 = Math.sqrt(Math.pow(lengthBC, 2) - Math.pow(distance, 2)); + + const worldAB = line[1].clone().subtract(line[0]); + + const worldAP = worldAB.clone().multiply(l1 / (l1 + l2)); + + const worldP = line[0].clone().add(worldAP); + + return worldP; +} + +export function computeOrthographicOffCenter ( + left: number, + right: number, + bottom: number, + top: number, + near: number, + far: number +) { + let a = 1.0 / (right - left); + let b = 1.0 / (top - bottom); + let c = 1.0 / (far - near); + + const tx = -(right + left) * a; + const ty = -(top + bottom) * b; + const tz = -(far + near) * c; + + a *= 2.0; + b *= 2.0; + c *= -2.0; + + return new Matrix4( + a, + 0.0, + 0.0, + 0.0, + // + 0.0, + b, + 0.0, + 0.0, + // + 0.0, + 0.0, + c, + 0.0, + // + tx, + ty, + tz, + 1.0, + ); +} diff --git a/plugin-packages/editor-gizmo/src/math/vec.ts b/plugin-packages/editor-gizmo/src/math/vec.ts deleted file mode 100644 index cab3ebb82..000000000 --- a/plugin-packages/editor-gizmo/src/math/vec.ts +++ /dev/null @@ -1,669 +0,0 @@ -import type { spec } from '@galacean/effects'; - -type mat2 = spec.mat2; -type mat3 = spec.mat3; -type mat4 = spec.mat4; -type vec2 = spec.vec2; -type vec3 = spec.vec3; -type vec4 = spec.vec4; - -export type vec = number[]; - -export { - vec2, vec4, vec3, mat2, mat3, mat4, -}; - -const d2r = Math.PI / 180; -const r2d = 180 / Math.PI; -const cos = Math.cos; -const sin = Math.sin; - -export function vecAdd (out: T | number[], a: T, b: T): T { - for (let i = 0, len = a.length; i < len; i++) { - out[i] = a[i] + b[i]; - } - - return out as T; -} - -export function vecSub (out: T | number[], a: T, b: T): T { - for (let i = 0, len = a.length; i < len; i++) { - out[i] = a[i] - b[i]; - } - - return out as T; -} - -export function vecAddCombine (out: T | number[], a: T, b: T): T { - if (a && b) { - for (let i = 0, len = a.length; i < len; i++) { - out[i] = a[i] + b[i]; - } - - return out as T; - } - - return a || b; -} - -export function vecMulCombine (out: T | number[], a: T, b: T): T { - if (a && b) { - for (let i = 0, len = a.length; i < len; i++) { - out[i] = a[i] * b[i]; - } - } else if (a) { - if (out !== a) { - for (let i = 0; i < a.length; i++) { - out[i] = a[i]; - } - } - } else if (b) { - if (out !== b) { - for (let i = 0; i < b.length; i++) { - out[i] = b[i]; - } - } - } - - return out as T; -} - -export function mat3NormalFromMat4 (out: mat3 | number[], a: mat4): mat3 { - const a00 = a[0], - a01 = a[1], - a02 = a[2], - a03 = a[3]; - const a10 = a[4], - a11 = a[5], - a12 = a[6], - a13 = a[7]; - const a20 = a[8], - a21 = a[9], - a22 = a[10], - a23 = a[11]; - const a30 = a[12], - a31 = a[13], - a32 = a[14], - a33 = a[15]; - const b00 = a00 * a11 - a01 * a10; - const b01 = a00 * a12 - a02 * a10; - const b02 = a00 * a13 - a03 * a10; - const b03 = a01 * a12 - a02 * a11; - const b04 = a01 * a13 - a03 * a11; - const b05 = a02 * a13 - a03 * a12; - const b06 = a20 * a31 - a21 * a30; - const b07 = a20 * a32 - a22 * a30; - const b08 = a20 * a33 - a23 * a30; - const b09 = a21 * a32 - a22 * a31; - const b10 = a21 * a33 - a23 * a31; - const b11 = a22 * a33 - a23 * a32; - // Calculate the determinant - let det = - b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; - - if (!det) { - for (let i = 0; i < 9; i++) { - out[i] = NaN; - } - - return out as mat3; - } - det = 1.0 / det; - out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; - out[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det; - out[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det; - out[3] = (a02 * b10 - a01 * b11 - a03 * b09) * det; - out[4] = (a00 * b11 - a02 * b08 + a03 * b07) * det; - out[5] = (a01 * b08 - a00 * b10 - a03 * b06) * det; - out[6] = (a31 * b05 - a32 * b04 + a33 * b03) * det; - out[7] = (a32 * b02 - a30 * b05 - a33 * b01) * det; - out[8] = (a30 * b04 - a31 * b02 + a33 * b00) * det; - - return out as mat3; -} - -export function vecMinus (out: T | number[], v0: T, v1: T): T { - for (let i = 0, len = v0.length; i < len; i++) { - out[i] = v0[i] - v1[i]; - } - - return out as T; -} - -export function vecSquareDistance (v0: vec, v1: vec): number { - let sum = 0; - - for (let i = 0, len = v0.length; i < len; i++) { - const d = v0[i] - v1[i]; - - sum += d * d; - } - - return sum; -} - -export const NumberEpsilon = Number.EPSILON || Math.pow(2, -32); - -export function vecNormalize (a: T): T { - let sum = 0; - - for (let i = 0, len = a.length; i < len; i++) { - sum += a[i] * a[i]; - } - sum = Math.sqrt(sum); - - return sum === 0 ? a.slice() as T : a.map(b => b / sum) as T; -} - -export function vecMulScalar (out: T | number[], vec: T, a: number): T { - for (let i = 0, len = vec.length; i < len; i++) { - out[i] = vec[i] * a; - } - - return out as T; -} - -export function vecDot (out: vec, a: vec, b?: number | vec): number | vec { - if (isNaN(b)) { - let sum = 0; - - for (let i = 0, len = a.length; i < len; i++) { - sum += out[i] * a[i]; - } - - return sum; - } - for (let i = 0, len = a.length; i < len; i++) { - out[i] = a[i] * (b as number); - } - - return (out); -} - -export function vec3Cross (out: vec3 | number[], a: vec3, b: vec3): vec3 { - const ax = a[0], ay = a[1], az = a[2]; - const bx = b[0], by = b[1], bz = b[2]; - - out[0] = ay * bz - az * by; - out[1] = az * bx - ax * bz; - out[2] = ax * by - ay * bx; - - return out as vec3; -} - -export function vec3MulMat4 (out: vec3 | number[], vec3: vec3, mat4: mat4): vec3 { - const x = vec3[0], y = vec3[1], z = vec3[2]; - let w = mat4[3] * x + mat4[7] * y + mat4[11] * z + mat4[15]; - - w = w || 1.0; - out[0] = (mat4[0] * x + mat4[4] * y + mat4[8] * z + mat4[12]) / w; - out[1] = (mat4[1] * x + mat4[5] * y + mat4[9] * z + mat4[13]) / w; - out[2] = (mat4[2] * x + mat4[6] * y + mat4[10] * z + mat4[14]) / w; - - return out as vec3; -} - -export function vec4MulMat4 (out: vec4 | number[], cartesian: vec4, mat4: mat4): vec4 { - const vX = cartesian[0]; - const vY = cartesian[1]; - const vZ = cartesian[2]; - const vW = cartesian[3]; - - const x = mat4[0] * vX + mat4[4] * vY + mat4[8] * vZ + mat4[12] * vW; - const y = mat4[1] * vX + mat4[5] * vY + mat4[9] * vZ + mat4[13] * vW; - const z = mat4[2] * vX + mat4[6] * vY + mat4[10] * vZ + mat4[14] * vW; - const w = mat4[3] * vX + mat4[7] * vY + mat4[11] * vZ + mat4[15] * vW; - - out[0] = x; - out[1] = y; - out[2] = z; - out[3] = w; - - return out as vec4; -} - -export function vec3TranslateByMat4 (out: vec3 | number[], vec3: vec3, matrix4: mat4): vec3 { - out[0] = vec3[0] + matrix4[12]; - out[1] = vec3[1] + matrix4[13]; - out[2] = vec3[2] + matrix4[14]; - - return out as vec3; -} - -export function vec3RotateByMat4 (out: vec3 | number[], a: vec3, m: mat4): vec3 { - const x = a[0], y = a[1], z = a[2]; - const w = (m[3] * x + m[7] * y + m[11] * z + m[15]) || 1; - - out[0] = (m[0] * x + m[4] * y + m[8] * z) / w; - out[1] = (m[1] * x + m[5] * y + m[9] * z) / w; - out[2] = (m[2] * x + m[6] * y + m[10] * z) / w; - - return out as vec3; -} - -export function vec3MulMat3 (out: vec3 | number[], a: vec3, m: mat3): vec3 { - const x = a[0], y = a[1], z = a[2]; - - out[0] = x * m[0] + y * m[3] + z * m[6]; - out[1] = x * m[1] + y * m[4] + z * m[7]; - out[2] = x * m[2] + y * m[5] + z * m[8]; - - return out as vec3; -} - -export function vec3MulMat3ByPoint (out: vec3 | number[], a: vec3, m: mat3, c: vec3): vec3 { - const x = a[0], y = a[1], z = a[2]; - const deltaX = a[0] - c[0], deltaY = a[1] - c[1], deltaZ = a[2] - c[2]; - const deltaOut = vec3MulMat3([], [deltaX, deltaY, deltaZ], m); - - out[0] = deltaOut[0] + c[0]; - out[1] = deltaOut[1] + c[1]; - out[2] = deltaOut[2] + c[2]; - - return out as vec3; -} - -const tempMat3FromRotationZ: mat3 = [0, 0, 0, 0, 0, 0, 0, 0, 0]; - -export function mat3FromRotationZ (out: mat3 | number[], rad: number): mat3 { - if (!out) { - out = tempMat3FromRotationZ; - } - const s = sin(rad); - const c = cos(rad); - - out[0] = c; - out[1] = s; - out[2] = 0; - out[3] = -s; - out[4] = c; - out[5] = 0; - out[6] = 0; - out[7] = 0; - out[8] = 1; - - return out as mat3; -} - -export function mat3FromRotationZYX (ret: mat3 | number[], x: number, y: number, z: number): mat3 { - const cosX = cos(x * d2r); - const cosY = cos(y * d2r); - const cosZ = cos(z * d2r); - const sinX = sin(x * d2r); - const sinY = sin(y * d2r); - const sinZ = sin(z * d2r); - - ret[0] = cosY * cosZ; - ret[1] = cosY * sinZ; - ret[2] = -sinY; - ret[3] = -cosX * sinZ + sinX * sinY * cosZ; - ret[4] = cosX * cosZ + sinX * sinY * sinZ; - ret[5] = sinX * cosY; - ret[6] = sinZ * sinX + cosX * sinY * cosZ; - ret[7] = -sinX * cosZ + cosX * sinY * sinZ; - ret[8] = cosX * cosY; - - return ret as mat3; -} - -export function mat3FromRotation (ret: mat3 | number[], rotation: vec3): mat3 { - return mat3FromRotationZYX(ret, -rotation[0], -rotation[1], -rotation[2]); -} - -export function invertMat4 (out: mat4 | number[], a: mat4): mat4 { - const a00 = a[0], - a01 = a[1], - a02 = a[2], - a03 = a[3]; - const a10 = a[4], - a11 = a[5], - a12 = a[6], - a13 = a[7]; - const a20 = a[8], - a21 = a[9], - a22 = a[10], - a23 = a[11]; - const a30 = a[12], - a31 = a[13], - a32 = a[14], - a33 = a[15]; - const b00 = a00 * a11 - a01 * a10; - const b01 = a00 * a12 - a02 * a10; - const b02 = a00 * a13 - a03 * a10; - const b03 = a01 * a12 - a02 * a11; - const b04 = a01 * a13 - a03 * a11; - const b05 = a02 * a13 - a03 * a12; - const b06 = a20 * a31 - a21 * a30; - const b07 = a20 * a32 - a22 * a30; - const b08 = a20 * a33 - a23 * a30; - const b09 = a21 * a32 - a22 * a31; - const b10 = a21 * a33 - a23 * a31; - const b11 = a22 * a33 - a23 * a32; - // Calculate the determinant - let det = - b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; - - if (!det) { - for (let i = 0; i < 16; i++) { - out[i] = NaN; - } - - return out as mat4; - } - det = 1.0 / det; - out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; - out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; - out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; - out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; - out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; - out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; - out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; - out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; - out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; - out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; - out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; - out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; - out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; - out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; - out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; - out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; - - return out as mat4; -} - -export function isZeroVec (vec: vec): boolean { - for (let i = 0, len = vec.length; i < len; i++) { - if (Math.abs(vec[i]) > NumberEpsilon) { - return false; - } - } - - return true; -} - -const isArray = Array.isArray; - -export function ensureVec3 (num?: any): vec3 { - return isArray(num) ? [num[0], num[1], num[2]] : [0, 0, 0]; -} - -export function rotateVec2 (out: vec2 | number[], vec2: vec2, angleInRad: number): vec2 { - const c = cos(angleInRad); - const s = sin(angleInRad); - const x = vec2[0]; - const y = vec2[1]; - - out[0] = c * x + s * y; - out[1] = -s * x + c * y; - - return out as vec2; -} - -function rotationZYXFromMat3 (out: vec3 | number[], mat3: mat3): vec3 { - const te = mat3; - const m11 = te[0], m12 = te[3], m13 = te[6]; - const m21 = te[1], m22 = te[4], m23 = te[7]; - const m31 = te[2], m32 = te[5], m33 = te[8]; - - out[1] = Math.asin(clamp(-m31, -1, 1)) * r2d; - if (Math.abs(m31) < 0.9999999) { - out[0] = Math.atan2(m32, m33) * r2d; - out[2] = Math.atan2(m21, m11) * r2d; - } else { - out[0] = 0; - out[2] = Math.atan2(-m12, m22) * r2d; - } - - return out as vec3; -} - -export function rotationFromMat3 (out: vec3 | number[], mat3: mat3) { - rotationZYXFromMat3(out, mat3); - - return out as vec3; -} - -export function mat3MulMat3 (out: mat3 | number[], a: mat3, b: mat3): mat3 { - const a00 = a[0], - a01 = a[1], - a02 = a[2]; - const a10 = a[3], - a11 = a[4], - a12 = a[5]; - const a20 = a[6], - a21 = a[7], - a22 = a[8]; - const b00 = b[0], - b01 = b[1], - b02 = b[2]; - const b10 = b[3], - b11 = b[4], - b12 = b[5]; - const b20 = b[6], - b21 = b[7], - b22 = b[8]; - - out[0] = b00 * a00 + b01 * a10 + b02 * a20; - out[1] = b00 * a01 + b01 * a11 + b02 * a21; - out[2] = b00 * a02 + b01 * a12 + b02 * a22; - out[3] = b10 * a00 + b11 * a10 + b12 * a20; - out[4] = b10 * a01 + b11 * a11 + b12 * a21; - out[5] = b10 * a02 + b11 * a12 + b12 * a22; - out[6] = b20 * a00 + b21 * a10 + b22 * a20; - out[7] = b20 * a01 + b21 * a11 + b22 * a21; - out[8] = b20 * a02 + b21 * a12 + b22 * a22; - - return out as mat3; -} - -export function mat4MulMat4 (a: mat4, b: mat4): mat4 { - const out = []; - const a00 = a[0], - a01 = a[1], - a02 = a[2], - a03 = a[3]; - const a10 = a[4], - a11 = a[5], - a12 = a[6], - a13 = a[7]; - const a20 = a[8], - a21 = a[9], - a22 = a[10], - a23 = a[11]; - const a30 = a[12], - a31 = a[13], - a32 = a[14], - a33 = a[15]; - - const b00 = b[0], - b01 = b[1], - b02 = b[2], - b03 = b[3]; - const b10 = b[4], - b11 = b[5], - b12 = b[6], - b13 = b[7]; - const b20 = b[8], - b21 = b[9], - b22 = b[10], - b23 = b[11]; - const b30 = b[12], - b31 = b[13], - b32 = b[14], - b33 = b[15]; - - out[0] = b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30; - out[1] = b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31; - out[2] = b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32; - out[3] = b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33; - - out[4] = b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30; - out[5] = b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31; - out[6] = b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32; - out[7] = b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33; - - out[8] = b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30; - out[9] = b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31; - out[10] = b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32; - out[11] = b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33; - - out[12] = b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30; - out[13] = b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31; - out[14] = b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32; - out[15] = b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33; - - return out as mat4; -} - -export function clamp (v: number, min: number, max: number): number { - return v > max ? max : (v < min ? min : v); -} - -export function vec3ToVec2 (a: vec3): vec2 { - return [a[0], a[1]]; -} - -export function distanceOfPointAndLine (point: vec2, line: [vec2, vec2]): { distance: number, isInLine: boolean } { - //三角形三个边长 - const AC: vec2 = [0, 0]; - const BC: vec2 = [0, 0]; - const AB: vec2 = [0, 0]; - - vecSub(AC, point, line[0]); - vecSub(BC, point, line[1]); - vecSub(AB, line[1], line[0]); - const lengthAC = Math.hypot(...AC); - const lengthBC = Math.hypot(...BC); - const lengthAB = Math.hypot(...AB); - - //利用海伦公式计算三角形面积 - //周长的一半 - const P = (lengthAC + lengthBC + lengthAB) / 2; - const allArea = Math.abs(Math.sqrt(P * (P - lengthAC) * (P - lengthBC) * (P - lengthAB))); - //普通公式计算三角形面积反推点到线的垂直距离 - const distance = (2 * allArea) / lengthAB; - const l1 = Math.sqrt(Math.pow(lengthAC, 2) - Math.pow(distance, 2)); - const l2 = Math.sqrt(Math.pow(lengthBC, 2) - Math.pow(distance, 2)); - let isInLine = false; - - if (l1 <= lengthAB && l2 <= lengthAB) { - isInLine = true; - } - - return { - distance, - isInLine, - }; -} - -export function projectionOfPointAndLine (point: vec2, line: [vec3, vec3]): vec3 { - const AC: vec2 = [0, 0]; - const BC: vec2 = [0, 0]; - const AB: vec2 = [0, 0]; - - vecSub(AC, point, vec3ToVec2(line[0])); - vecSub(BC, point, vec3ToVec2(line[1])); - vecSub(AB, vec3ToVec2(line[1]), vec3ToVec2(line[0])); - const lengthAC = Math.hypot(...AC); - const lengthBC = Math.hypot(...BC); - const lengthAB = Math.hypot(...AB); - - //利用海伦公式计算三角形面积 - //周长的一半 - const P = (lengthAC + lengthBC + lengthAB) / 2; - const allArea = Math.abs(Math.sqrt(P * (P - lengthAC) * (P - lengthBC) * (P - lengthAB))); - //普通公式计算三角形面积反推点到线的垂直距离 - const distance = (2 * allArea) / lengthAB; - const l1 = Math.sqrt(Math.pow(lengthAC, 2) - Math.pow(distance, 2)); - const l2 = Math.sqrt(Math.pow(lengthBC, 2) - Math.pow(distance, 2)); - - const worldAB: vec3 = [0, 0, 0]; - - vecSub(worldAB, line[1], line[0]); - const worldAP: vec3 = [0, 0, 0]; - - vecMulScalar(worldAP, worldAB, l1 / (l1 + l2)); - const worldP: vec3 = [0, 0, 0]; - - vecAdd(worldP, line[0], worldAP); - - return worldP; -} - -export function computeOrthographicOffCenter ( - left: number, - right: number, - bottom: number, - top: number, - near: number, - far: number -) { - let a = 1.0 / (right - left); - let b = 1.0 / (top - bottom); - let c = 1.0 / (far - near); - - const tx = -(right + left) * a; - const ty = -(top + bottom) * b; - const tz = -(far + near) * c; - - a *= 2.0; - b *= 2.0; - c *= -2.0; - - const result: mat4 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - - result[0] = a; - result[1] = 0.0; - result[2] = 0.0; - result[3] = 0.0; - result[4] = 0.0; - result[5] = b; - result[6] = 0.0; - result[7] = 0.0; - result[8] = 0.0; - result[9] = 0.0; - result[10] = c; - result[11] = 0.0; - result[12] = tx; - result[13] = ty; - result[14] = tz; - result[15] = 1.0; - - return result; -} - -export function computePerspectiveFieldOfView ( - fov: number, - aspectRatio: number, - near: number, - far: number, - reverse: boolean -) { - const result = []; - const invTanFov = 1.0 / Math.tan(fov * 0.5); - - const column0Row0 = reverse ? invTanFov : invTanFov / aspectRatio; - const column1Row1 = reverse ? invTanFov * aspectRatio : invTanFov; - const column2Row2 = (far + near) / (near - far); - const column3Row2 = (2.0 * far * near) / (near - far); - - result[0] = column0Row0; - result[1] = 0.0; - result[2] = 0.0; - result[3] = 0.0; - result[4] = 0.0; - result[5] = column1Row1; - result[6] = 0.0; - result[7] = 0.0; - result[8] = 0.0; - result[9] = 0.0; - result[10] = column2Row2; - result[11] = -1.0; - result[12] = 0.0; - result[13] = 0.0; - result[14] = column3Row2; - result[15] = 0.0; - - return result; - -} diff --git a/plugin-packages/editor-gizmo/src/mesh.ts b/plugin-packages/editor-gizmo/src/mesh.ts index ddf92a210..1a5d5e7cd 100644 --- a/plugin-packages/editor-gizmo/src/mesh.ts +++ b/plugin-packages/editor-gizmo/src/mesh.ts @@ -1,15 +1,17 @@ -import type { Engine, Geometry, spec, Triangle } from '@galacean/effects'; -import { glContext, Material, Mesh, Texture, Transform } from '@galacean/effects'; +import type { Engine, Geometry, spec } from '@galacean/effects'; +import { glContext, Material, Mesh, Texture, Transform, math } from '@galacean/effects'; import type { MeshOption } from './geometry'; import { createGeometry, GeometryType } from './geometry'; import { GizmoSubType } from './define'; import type { GizmoItemBounding } from './gizmo-vfx-item'; import { BoundingType } from './gizmo-vfx-item'; -import { vec3MulMat4 } from './math/vec'; import { color, renderMode } from './constants'; type mat4 = spec.mat4; type vec3 = spec.vec3; +type Vector3 = math.Vector3; +type TriangleLike = math.TriangleLike; +const { Vector2, Vector3, Matrix4 } = math; /** * 根据 gizmoSubType 类型创建几何体 Mesh @@ -168,24 +170,28 @@ function createRotationMesh ( * @param transform - 变换式 * @returns 三角面数组 */ -function getTriangle (geometry: Geometry, transform: Transform): Triangle[] { - const result: Triangle[] = []; +function getTriangle (geometry: Geometry, transform: Transform): TriangleLike[] { + const result: TriangleLike[] = []; const indices = geometry.getIndexData(); const points = geometry.getAttributeData('a_Position'); const mat4 = transform.getWorldMatrix(); if (points && indices && indices.length > 0) { - indices?.forEach((index, i) => { - const position: vec3 = [points[index * 3], points[index * 3 + 1], points[index * 3 + 2]]; - const localPosition: vec3 = [0, 0, 0]; + for (let i = 0; i < indices.length; i += 3) { + const i0 = indices[i] * 3; + const i1 = indices[i + 1] * 3; + const i2 = indices[i + 2] * 3; + const p0 = new Vector3(points[i0], points[i0 + 1], points[i0 + 2]); + const p1 = new Vector3(points[i1], points[i1 + 1], points[i1 + 2]); + const p2 = new Vector3(points[i2], points[i2 + 1], points[i2 + 2]); + + result.push({ + p0: mat4.projectPoint(p0), + p1: mat4.projectPoint(p1), + p2: mat4.projectPoint(p2), + }); - vec3MulMat4(localPosition, position, mat4); - - if (result[Math.floor(i / 3)] === undefined) { - result[Math.floor(i / 3)] = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]; - } - result[Math.floor(i / 3)][i % 3] = localPosition; - }); + } } return result; @@ -385,7 +391,7 @@ function createScaleMesh (engine: Engine, options: MeshOption, boundingMap: Map< createAxisBounding({ width: boxSize.width * 1.2, length: axisSize.width + boxSize.width / 2 }, boundingMap); boundingMap.set('center', { type: BoundingType.sphere, - center: [0, 0, 0], + center: new Vector3(), radius: Math.pow(Math.pow(centerBoxSize.width, 2) + Math.pow(centerBoxSize.height, 2) + Math.pow(centerBoxSize.depth, 2), 0.5) / 2, }); @@ -672,14 +678,14 @@ function createSpriteMesh (engine: Engine, options: MeshOption, texture?: Textur * @returns 纯色材质,无光照,无透明 */ function createMaterial (engine: Engine, color?: vec3, depthTest?: boolean): Material { - const myColor: vec3 = color ? color : [255, 255, 255]; + const myColor = color ? Vector3.fromArray(color) : new Vector3(255, 255, 255); const myDepthTest = depthTest ? depthTest : false; const material = Material.create( engine, { uniformValues: { - u_color: new Float32Array(myColor), + u_color: new Float32Array(myColor.toArray()), u_model: new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]), }, uniformSemantics: { @@ -716,7 +722,7 @@ function createMaterial (engine: Engine, color?: vec3, depthTest?: boolean): Mat }); material.setVector3('u_color', myColor); - material.setMatrix('u_model', [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + material.setMatrix('u_model', Matrix4.IDENTITY); material.depthTest = myDepthTest; material.stencilTest = false; material.blending = false; @@ -733,14 +739,14 @@ function createMaterial (engine: Engine, color?: vec3, depthTest?: boolean): Mat * @returns */ function createHideBackMaterial (engine: Engine, color?: vec3, depthTest?: boolean): Material { - const myColor: vec3 = color ? color : [255, 255, 255]; + const myColor = color ? Vector3.fromArray(color) : new Vector3(255, 255, 255); const myDepthTest = depthTest ? depthTest : false; const material = Material.create( engine, { uniformValues: { - u_color: new Float32Array(myColor), + u_color: new Float32Array(myColor.toArray()), u_model: new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]), u_cameraPos: new Float32Array([0, 0, 0]), u_center: new Float32Array([0, 0, 0]), @@ -792,9 +798,9 @@ function createHideBackMaterial (engine: Engine, color?: vec3, depthTest?: boole }); material.setVector3('u_color', myColor); - material.setMatrix('u_model', [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); - material.setVector3('u_cameraPos', [0, 0, 0]); - material.setVector3('u_center', [0, 0, 0]); + material.setMatrix('u_model', Matrix4.IDENTITY); + material.setVector3('u_cameraPos', Vector3.ZERO); + material.setVector3('u_center', Vector3.ZERO); material.depthTest = myDepthTest; material.stencilTest = false; @@ -813,7 +819,7 @@ function createHideBackMaterial (engine: Engine, color?: vec3, depthTest?: boole * @returns */ function createBlendMaterial (engine: Engine, color?: vec3, depthTest?: boolean, alpha?: number): Material { - const myColor: vec3 = color ? color : [255, 255, 255]; + const myColor = color ? Vector3.fromArray(color) : new Vector3(255, 255, 255); const myDepthTest = depthTest ? depthTest : false; const myAlpha = alpha ? alpha : 1; @@ -821,7 +827,7 @@ function createBlendMaterial (engine: Engine, color?: vec3, depthTest?: boolean, engine, { uniformValues: { - u_color: new Float32Array(myColor), + u_color: new Float32Array(myColor.toArray()), u_alpha: new Float32Array([myAlpha, 0]), u_model: new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]), }, @@ -859,8 +865,8 @@ function createBlendMaterial (engine: Engine, color?: vec3, depthTest?: boolean, }); material.setVector3('u_color', myColor); - material.setVector2('u_alpha', [myAlpha, 0]); - material.setMatrix('u_model', [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + material.setVector2('u_alpha', new Vector2(myAlpha, 0)); + material.setMatrix('u_model', Matrix4.IDENTITY); material.depthTest = myDepthTest; material.stencilTest = false; material.blending = true; @@ -932,19 +938,19 @@ function createSpriteMaterial (engine: Engine, data: vec3 | Texture | undefined, }, }); - material.setMatrix('u_model', [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); - material.setVector2('u_alpha', [1, 0]); + material.setMatrix('u_model', Matrix4.IDENTITY); + material.setVector2('u_alpha', new Vector2(1, 0)); if (data instanceof Texture) { material.setTexture('u_iconTex', data); // uniformValues.u_iconTex = data; } else { - let color: vec3; + let color: Vector3; if (data === undefined) { - color = [255, 255, 255]; + color = new Vector3(255, 255, 255); } else { - color = data as vec3; + color = Vector3.fromArray(data); } material.setVector3('u_color', color); // uniformValues.u_color = new Float32Array(color); @@ -968,17 +974,17 @@ function createSpriteMaterial (engine: Engine, data: vec3 | Texture | undefined, function createAxisBounding (size: { width: number, length: number }, boundingMap: Map) { boundingMap.set('xAxis', { type: BoundingType.line, - points: [[0, 0, 0], [size.length, 0, 0]], + points: [new Vector3(0, 0, 0), new Vector3(size.length, 0, 0)], width: size.width, }); boundingMap.set('yAxis', { type: BoundingType.line, - points: [[0, 0, 0], [0, size.length, 0]], + points: [new Vector3(0, 0, 0), new Vector3(0, size.length, 0)], width: size.width, }); boundingMap.set('zAxis', { type: BoundingType.line, - points: [[0, 0, 0], [0, 0, size.length]], + points: [new Vector3(0, 0, 0), new Vector3(0, 0, size.length)], width: size.width, }); } diff --git a/plugin-packages/editor-gizmo/src/raycast.ts b/plugin-packages/editor-gizmo/src/raycast.ts index 4303edbab..5df4066bb 100644 --- a/plugin-packages/editor-gizmo/src/raycast.ts +++ b/plugin-packages/editor-gizmo/src/raycast.ts @@ -1,10 +1,12 @@ +import type { Camera } from '@galacean/effects'; +import { math } from '@galacean/effects'; +import { distanceOfPointAndLine, projectionOfPointAndLine } from './math-utils'; -import type { Camera, spec } from '@galacean/effects'; -import { vec3MulMat4, distanceOfPointAndLine, projectionOfPointAndLine, vecSub, vec3ToVec2, vecAdd, vecNormalize, vecMulScalar } from './math/vec'; +type Vector2 = math.Vector2; +type Vector3 = math.Vector3; +type Matrix4 = math.Matrix4; -type mat4 = spec.mat4; -type vec2 = spec.vec2; -type vec3 = spec.vec3; +const { Matrix4, Vector2, Vector3 } = math; /** * 线条包围盒射线检测算法 @@ -13,54 +15,48 @@ type vec3 = spec.vec3; * @param points - 线条包围盒两端点位置 * @param radius - 线条包围盒辐射半径 * @param camera - 相机对象 - * @returns 射线与包围盒交点位置 | null + * @returns 射线与包围盒交点位置 | undefined */ export function intersectRayLine ( - out: vec3 | number[], - pointInCanvas: vec2, - points: [p0: vec3, p1: vec3], + out: Vector3, + pointInCanvas: Vector2, + points: [p0: Vector3, p1: Vector3], radius: number, - worldMat4: mat4, + worldMat4: Matrix4, camera: Camera, -): vec3 | null { +): Vector3 | undefined { // 构造一个辐射半径边缘的点 radiusPoint - const radiusPoint: vec3 = [0, 0, 0]; - const up = [ - worldMat4[1], - worldMat4[5], - worldMat4[9], - ]; + const up = new Vector3( + worldMat4.elements[1], + worldMat4.elements[5], + worldMat4.elements[9], + ).normalize(); - vecMulScalar(radiusPoint, vecNormalize(up), radius); - vecAdd(radiusPoint, points[0], radiusPoint); + const radiusPoint = up.clone().multiply(radius); + + radiusPoint.add(points[0]); // 计算 start、end、radius 在屏幕空间上的投影 - const mvpMat4 = camera.getModelViewProjection([], worldMat4); - const screenStart: vec3 = [0, 0, 0]; - const screenEnd: vec3 = [0, 0, 0]; - const screenRadiusPoint: vec3 = [0, 0, 0]; + const mvpMat4 = camera.getModelViewProjection(new Matrix4(), worldMat4); + + const screenStart = mvpMat4.projectPoint(points[0], new Vector3()); + const screenEnd = mvpMat4.projectPoint(points[1], new Vector3()); + const screenRadiusPoint = mvpMat4.projectPoint(radiusPoint, new Vector3()); - vec3MulMat4(screenStart, points[0], mvpMat4); - vec3MulMat4(screenEnd, points[1], mvpMat4); - vec3MulMat4(screenRadiusPoint, radiusPoint, mvpMat4); - const screenRadiusVector: vec3 = [0, 0, 0]; + const screenRadiusVector = screenRadiusPoint.clone().subtract(screenStart); - vecSub(screenRadiusVector, screenRadiusPoint, screenStart); - const screenRadius = Math.hypot(...screenRadiusVector); + const screenRadius = screenRadiusVector.length(); // 计算屏幕交互点与线条的距离 - const { distance, isInLine } = distanceOfPointAndLine(pointInCanvas, [vec3ToVec2(screenStart), vec3ToVec2(screenEnd)]); + const { distance, isInLine } = distanceOfPointAndLine(pointInCanvas, [screenStart.toVector2(), screenEnd.toVector2()]); if (distance <= screenRadius && isInLine) { // 距离小于阈值且交互点在线条上的投影位于线段内 // 计算交互点在线条上的投影并转换为世界坐标 const screenPosition = projectionOfPointAndLine(pointInCanvas, [screenStart, screenEnd]); const inverseViewProjection = camera.getInverseViewProjectionMatrix(); - const worldPosition: vec3 = [0, 0, 0]; - vec3MulMat4(worldPosition, screenPosition, inverseViewProjection); + inverseViewProjection.projectPoint(screenPosition, out); - return out = worldPosition; + return out; } - - return null; } diff --git a/plugin-packages/editor-gizmo/src/shape.ts b/plugin-packages/editor-gizmo/src/shape.ts index 44f0919c2..c48e69f21 100644 --- a/plugin-packages/editor-gizmo/src/shape.ts +++ b/plugin-packages/editor-gizmo/src/shape.ts @@ -1,21 +1,30 @@ -import type { Engine } from '@galacean/effects'; -import { Geometry, Material, Mesh, glContext, type spec } from '@galacean/effects'; -import { mat3FromRotation, vec3MulMat3, vecAdd } from './math/vec'; +import type { Engine, spec } from '@galacean/effects'; +import { Geometry, Material, Mesh, glContext, math } from '@galacean/effects'; type vec3 = spec.vec3; +type Vector3 = math.Vector3; +type Matrix4 = math.Matrix4; + +const { Euler, Vector3, Matrix4 } = math; const rectSize = 0.04; const DEG2RAD = Math.PI / 180; interface GeometryData { - points: vec3[], + points: Vector3[], indices: number[], } -function arcPath (radius: number, arc: number, options: Record = {}): GeometryData { +interface ArcPathOptions { + plane?: string, + translate?: vec3, + rotate?: vec3, +} + +function arcPath (radius: number, arc: number, options: ArcPathOptions = {}): GeometryData { const { plane, translate = [0, 0, 0], rotate = [0, 0, 0] } = options; - const rotation = mat3FromRotation([], rotate); - const points: vec3[] = []; + const rotMat4 = Euler.fromArray(rotate).negate().toMatrix4(new Matrix4()); + const points: Vector3[] = []; const indices = []; addPoint(radius, 0); @@ -34,23 +43,26 @@ function arcPath (radius: number, arc: number, options: Record = {} }; function addPoint (cos: number, sin: number) { - let point: vec3; + const point = new Vector3(); if (plane === 'xz') { - point = [cos, 0, sin]; + point.set(cos, 0, sin); } else if (plane === 'yz') { - point = [0, cos, sin]; + point.set(0, cos, sin); } else { - point = [cos, sin, 0]; + point.set(cos, sin, 0); } - point = vec3MulMat3(point, point, rotation); - points.push(vecAdd(point, point, translate)); + rotMat4.transformNormal(point); + points.push(point.add(translate)); } } function line (p0: vec3, p1: vec3): GeometryData { return { - points: [p0, p1], + points: [ + Vector3.fromArray(p0), + Vector3.fromArray(p1), + ], indices: [0, 1], }; } @@ -60,11 +72,11 @@ function box (width: number, height: number, depth: number, center?: number[]): const h = height / 2; const d = depth / 2; const myCenter = center ?? [0, 0, 0]; - const points: vec3[] = [ + const points: Vector3[] = [ [-w, h, -d], [-w, -h, -d], [-w, h, d], [-w, -h, d], [w, h, -d], [w, -h, -d], [w, h, d], [w, -h, d], ].map(item => { - return [item[0] + myCenter[0], item[1] + myCenter[1], item[2] + myCenter[2]]; + return new Vector3(item[0] + myCenter[0], item[1] + myCenter[1], item[2] + myCenter[2]); }); return { @@ -80,13 +92,16 @@ function box (width: number, height: number, depth: number, center?: number[]): function rect (width: number, height: number, pos?: vec3): GeometryData { const w = width / 2; const h = height / 2; - const points: vec3[] = [[-w, h, 0], [w, h, 0], [w, -h, 0], [-w, -h, 0]]; + const points: Vector3[] = [ + new Vector3(-w, h, 0), + new Vector3(w, h, 0), + new Vector3(w, -h, 0), + new Vector3(-w, -h, 0), + ]; if (pos) { points.forEach(vec => { - vec[0] += pos[0]; - vec[1] += pos[1]; - vec[2] += pos[2]; + vec.add(pos); }); } @@ -107,7 +122,7 @@ function combineGeometries (geometries: GeometryData[]) { if (geometry) { geometry.points.forEach(point => { - points.push(point[0], point[1], point[2]); + points.push(point.x, point.y, point.z); }); geometry.indices.forEach(index => indices.push(index + indicesBase)); indicesBase += geometry.points.length; @@ -310,8 +325,8 @@ function createMesh (engine: Engine, points: Float32Array, indices: Uint16Array, }, }); - material.setVector3('u_color', color); - material.setMatrix('u_model', [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + material.setVector3('u_color', Vector3.fromArray(color)); + material.setMatrix('u_model', Matrix4.IDENTITY); material.depthTest = depthTest; material.stencilTest = false; material.blending = false; diff --git a/plugin-packages/editor-gizmo/src/util.ts b/plugin-packages/editor-gizmo/src/util.ts index 7abe2b168..184be31e5 100644 --- a/plugin-packages/editor-gizmo/src/util.ts +++ b/plugin-packages/editor-gizmo/src/util.ts @@ -1,9 +1,7 @@ - import { Texture, noop } from '@galacean/effects'; -import type { Engine, spec } from '@galacean/effects'; -import { vecSub, vecAdd, vecNormalize, vecMulScalar } from './math/vec'; +import type { Engine, math } from '@galacean/effects'; -type vec3 = spec.vec3; +type Vector3 = math.Vector3; /** * 下载图片 @@ -46,21 +44,14 @@ export function createTexture (engine: Engine, image: HTMLImageElement, hookDest * @param currentPos - 当前点 * @returns 指定点 */ -export function moveToPointWidthFixDistance (targetPos: vec3, currentPos: vec3): vec3 { - const vecCameraItem: vec3 = [0, 0, 0]; - - vecSub(vecCameraItem, currentPos, targetPos); - const normalVecCameraItem = vecNormalize(vecCameraItem); - const newPos: vec3 = [0, 0, 0]; - const scalerVecCameraItem: vec3 = [0, 0, 0]; +export function moveToPointWidthFixDistance (targetPos: Vector3, currentPos: Vector3): Vector3 { + const vecCameraItem = currentPos.clone().subtract(targetPos); - vecMulScalar(scalerVecCameraItem, normalVecCameraItem, 20); - const vecScalerItem: vec3 = [0, 0, 0]; - const vecItemScaler: vec3 = [0, 0, 0]; + const normalVecCameraItem = vecCameraItem.clone().normalize(); + const scalerVecCameraItem = normalVecCameraItem.clone().multiply(20); - vecSub(vecScalerItem, vecCameraItem, scalerVecCameraItem); - vecMulScalar(vecItemScaler, vecScalerItem, -1); - vecAdd(newPos, currentPos, vecItemScaler); + const vecScalerItem = vecCameraItem.clone().subtract(scalerVecCameraItem); + const vecItemScaler = vecScalerItem.clone().multiply(-1); - return newPos; + return currentPos.clone().add(vecItemScaler); } diff --git a/plugin-packages/editor-gizmo/src/wireframe.ts b/plugin-packages/editor-gizmo/src/wireframe.ts index e50ce55d5..70fbb3dea 100644 --- a/plugin-packages/editor-gizmo/src/wireframe.ts +++ b/plugin-packages/editor-gizmo/src/wireframe.ts @@ -1,5 +1,7 @@ import type { ShaderMarcos, Geometry, GeometryDrawMode, Engine, GLEngine, spec } from '@galacean/effects'; -import { GLSLVersion, glContext, GLGeometry, DestroyOptions, Material, Mesh, createShaderWithMarcos, ShaderType } from '@galacean/effects'; +import { GLSLVersion, glContext, GLGeometry, DestroyOptions, Material, Mesh, createShaderWithMarcos, ShaderType, math } from '@galacean/effects'; + +const { Vector4 } = math; export function createParticleWireframe (engine: Engine, mesh: Mesh, color: spec.vec3): Mesh { const geometry = new SharedGeometry( @@ -28,7 +30,7 @@ export function createParticleWireframe (engine: Engine, mesh: Mesh, color: spec const material = Material.create(engine, materialOptions); material.depthTest = mesh.material.depthTest; - material.setVector4('uPreviewColor', [color[0], color[1], color[2], 1]); + material.setVector4('uPreviewColor', new Vector4(color[0], color[1], color[2], 1)); return updateWireframeMesh(mesh, Mesh.create( engine, @@ -128,7 +130,7 @@ export function createModeWireframe (engine: Engine, mesh: Mesh, color: spec.vec const material = Material.create(engine, materialOptions); material.depthTest = mesh.material.depthTest; - material.setVector4('uPreviewColor', [color[0], color[1], color[2], 1]); + material.setVector4('uPreviewColor', new Vector4(color[0], color[1], color[2], 1)); return updateWireframeMesh(mesh, Mesh.create( engine, diff --git a/plugin-packages/model/demo/src/camera.ts b/plugin-packages/model/demo/src/camera.ts index eeaffee5c..9611db385 100644 --- a/plugin-packages/model/demo/src/camera.ts +++ b/plugin-packages/model/demo/src/camera.ts @@ -1,8 +1,9 @@ -// @ts-nocheck -import type { Player } from '@galacean/effects'; -import { Sphere, Vector3, Box3, CameraGestureType, CameraGestureHandlerImp } from '@galacean/effects-plugin-model'; +//@ts-nocheck +import { math } from '@galacean/effects'; +import { CameraGestureType, CameraGestureHandlerImp } from '@galacean/effects-plugin-model'; import { LoaderImplEx } from '@galacean/effects-plugin-model/helper'; -import { createSlider } from './utility'; + +const { Sphere, Vector3, Box3 } = math; let player: Player; let pending = false; @@ -50,7 +51,7 @@ async function getCurrentScene () { sceneAABB = new Box3(sceneMin, sceneMax); sceneRadius = sceneAABB.getBoundingSphere(new Sphere()).radius; sceneCenter = sceneAABB.getCenter(new Vector3()); - const position = sceneCenter.addVector(new Vector3(0, 0, sceneRadius * 3)); + const position = sceneCenter.add(new Vector3(0, 0, sceneRadius * 3)); items.push({ id: '321', diff --git a/plugin-packages/model/package.json b/plugin-packages/model/package.json index 3a0688d4b..904c96b6c 100644 --- a/plugin-packages/model/package.json +++ b/plugin-packages/model/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/effects-plugin-model", - "version": "1.0.1", + "version": "1.1.0", "description": "Galacean Effects player model plugin", "module": "./dist/index.mjs", "main": "./dist/index.js", diff --git a/plugin-packages/model/src/gesture/index.ts b/plugin-packages/model/src/gesture/index.ts index 6718eeb7f..58a0b8743 100644 --- a/plugin-packages/model/src/gesture/index.ts +++ b/plugin-packages/model/src/gesture/index.ts @@ -1,4 +1,4 @@ -import type { Composition, CameraOptions, spec } from '@galacean/effects'; +import type { Composition, CameraOptionsEx, spec } from '@galacean/effects'; import { Transform } from '@galacean/effects'; import type { CameraGestureHandler, @@ -8,7 +8,7 @@ import type { import { CameraGestureType } from './protocol'; import type { ModelVFXItem } from '../plugin/model-vfx-item'; import { PCoordinate, PTransform } from '../runtime/common'; -import { Quaternion, Vector3, Matrix4 } from '../math'; +import { Quaternion, Vector3, Matrix4 } from '../runtime/math'; export class CameraGestureHandlerImp implements CameraGestureHandler { private cameraTransform = new PTransform(); @@ -40,7 +40,7 @@ export class CameraGestureHandlerImp implements CameraGestureHandler { return this.startParams.type; } - onKeyEvent (event: CameraKeyEvent): CameraOptions { + onKeyEvent (event: CameraKeyEvent): CameraOptionsEx { // check event camera ID at first if (this.startParams.target !== event.cameraID) { this.startParams.target = event.cameraID; @@ -68,10 +68,10 @@ export class CameraGestureHandlerImp implements CameraGestureHandler { const xAxis = event.xAxis ?? 0; const yAxis = event.yAxis ?? 0; const zAxis = event.zAxis ?? 0; - const dir = cameraCoordiante.xAxis.multiplyScalar(xAxis); + const dir = cameraCoordiante.xAxis.clone().multiply(xAxis); - dir.addVector(cameraCoordiante.yAxis.multiplyScalar(yAxis)); - dir.addVector(cameraCoordiante.zAxis.multiplyScalar(zAxis)); + dir.add(cameraCoordiante.yAxis.clone().multiply(yAxis)); + dir.add(cameraCoordiante.zAxis.clone().multiply(zAxis)); if (dir.lengthSquared() < 0.00001) { return camera.getOptions(); } @@ -81,7 +81,7 @@ export class CameraGestureHandlerImp implements CameraGestureHandler { const speed = event.speed ?? 0.1; const pos = cameraTransform.getPosition(); - pos.addVector(dir.clone().multiplyScalar(speed)); + pos.add(dir.clone().multiply(speed)); item.transform.setPosition(pos.x, pos.y, pos.z); item.updateTransform(); @@ -94,7 +94,7 @@ export class CameraGestureHandlerImp implements CameraGestureHandler { return camera.getOptions(); } - onXYMoveBegin (x: number, y: number, width: number, height: number, cameraID: string): CameraOptions { + onXYMoveBegin (x: number, y: number, width: number, height: number, cameraID: string): CameraOptionsEx { const args = { type: CameraGestureType.translate, mouseEvent: true, @@ -108,7 +108,7 @@ export class CameraGestureHandlerImp implements CameraGestureHandler { return this.startGesture(args); } - onXYMoving (x: number, y: number, speed?: number): CameraOptions { + onXYMoving (x: number, y: number, speed?: number): CameraOptionsEx { if (!this.startParams.mouseEvent) { return this.composition.camera.getOptions(); } @@ -132,7 +132,7 @@ export class CameraGestureHandlerImp implements CameraGestureHandler { this.endGesture(); } - onZMoveBegin (x: number, y: number, width: number, height: number, cameraID: string): CameraOptions { + onZMoveBegin (x: number, y: number, width: number, height: number, cameraID: string): CameraOptionsEx { const arg = { type: CameraGestureType.scale, mouseEvent: true, @@ -146,7 +146,7 @@ export class CameraGestureHandlerImp implements CameraGestureHandler { return this.startGesture(arg); } - onZMoving (x: number, y: number, speed: number): CameraOptions { + onZMoving (x: number, y: number, speed: number): CameraOptionsEx { if (!this.startParams.mouseEvent) { return this.composition.camera.getOptions(); } @@ -170,7 +170,7 @@ export class CameraGestureHandlerImp implements CameraGestureHandler { this.endGesture(); } - onRotateBegin (x: number, y: number, width: number, height: number, cameraID: string): CameraOptions { + onRotateBegin (x: number, y: number, width: number, height: number, cameraID: string): CameraOptionsEx { const arg = { type: CameraGestureType.rotate_self, mouseEvent: true, @@ -184,7 +184,7 @@ export class CameraGestureHandlerImp implements CameraGestureHandler { return this.startGesture(arg); } - onRotating (x: number, y: number, speed?: number): CameraOptions { + onRotating (x: number, y: number, speed?: number): CameraOptionsEx { if (!this.startParams.mouseEvent) { return this.composition.camera.getOptions(); } @@ -208,7 +208,7 @@ export class CameraGestureHandlerImp implements CameraGestureHandler { this.endGesture(); } - onRotatePointBegin (x: number, y: number, width: number, height: number, point: spec.vec3, cameraID: string): CameraOptions { + onRotatePointBegin (x: number, y: number, width: number, height: number, point: spec.vec3, cameraID: string): CameraOptionsEx { const arg = { type: CameraGestureType.rotate_focus, mouseEvent: true, @@ -223,7 +223,7 @@ export class CameraGestureHandlerImp implements CameraGestureHandler { return this.startGesture(arg); } - onRotatingPoint (x: number, y: number, speed?: number): CameraOptions { + onRotatingPoint (x: number, y: number, speed?: number): CameraOptionsEx { if (!this.startParams.mouseEvent) { return this.composition.camera.getOptions(); } @@ -292,10 +292,11 @@ export class CameraGestureHandlerImp implements CameraGestureHandler { // const transform = new PTransform().fromEffectsTransform(item.transform); const coordinate = new PCoordinate().fromPTransform(transform); - const lookatDir = coordinate.zAxis.clone().multiplyScalar(1.0); + // FIXME: MATH + const lookatDir = Vector3.fromArray(coordinate.zAxis.toArray()).multiply(1.0); // - const newOffset = lookatDir.clone().multiplyScalar(newDistance); - const newPosition = targetPoint.clone().addVector(newOffset); + const newOffset = lookatDir.clone().multiply(newDistance); + const newPosition = targetPoint.clone().add(newOffset); // item.transform.setPosition(newPosition.x, newPosition.y, newPosition.z); @@ -350,7 +351,7 @@ export class CameraGestureHandlerImp implements CameraGestureHandler { this.updateCameraTransform(this.composition.camera.getOptions()); } - private startGesture (args: CameraGestureHandlerParams): CameraOptions { + private startGesture (args: CameraGestureHandlerParams): CameraOptionsEx { this.startParams = args; this.updateCameraTransform(this.composition.camera.getOptions()); @@ -361,7 +362,7 @@ export class CameraGestureHandlerImp implements CameraGestureHandler { return this.composition.camera.getOptions(); } - private moveGesture (arg: CameraGestureHandlerParams): CameraOptions { + private moveGesture (arg: CameraGestureHandlerParams): CameraOptionsEx { if (this.getCurrentType() === arg.type) { const item = this.getItem(); @@ -381,50 +382,52 @@ export class CameraGestureHandlerImp implements CameraGestureHandler { const pos = this.cameraTransform.getPosition(); const newPos = pos.clone(); - newPos.addVector(xAxis.clone().multiplyScalar(-dx * speed)); - newPos.addVector(yAxis.clone().multiplyScalar(dy * speed)); + newPos.add(xAxis.clone().multiply(-dx * speed)); + newPos.add(yAxis.clone().multiply(dy * speed)); item.transform.setPosition(newPos.x, newPos.y, newPos.z); - item.updateTransform(); + item.setTransform(item.transform.position, item.transform.rotation); } else if (arg.type === CameraGestureType.scale) { const pos = this.cameraTransform.getPosition(); const newPos = pos.clone(); - newPos.addVector(zAxis.clone().multiplyScalar(dy * speed)); + newPos.add(zAxis.clone().multiply(dy * speed)); item.transform.setPosition(newPos.x, newPos.y, newPos.z); - item.updateTransform(); + item.setTransform(item.transform.position, item.transform.rotation); } else if (arg.type === CameraGestureType.rotate_self) { const ndx = dx / arg.clientWidth; const ndy = dy / arg.clientHeight; const dxAngle = ndx * Math.PI * speed * 0.5; const dyAngle = ndy * Math.PI * speed * 0.5; - // merge to on rotation? - const newRotation = Quaternion.fromAxisAngle(Vector3.UNIT_Y, -dxAngle, new Quaternion()); - const tempRotation = Quaternion.fromAxisAngle(xAxis, -dyAngle, new Quaternion()); + + const newRotation = Quaternion.fromAxisAngle(Vector3.Y, -dxAngle); + // FIXME: MATH + const tempRotation = Quaternion.fromAxisAngle(xAxis, -dyAngle); newRotation.multiply(tempRotation); + // FIXME: MATH newRotation.multiply(this.cameraTransform.getRotation()); item.transform.setQuaternion(newRotation.x, newRotation.y, newRotation.z, newRotation.w); - item.updateTransform(); + item.setTransform(item.transform.position, item.transform.rotation); } else if (arg.type === CameraGestureType.rotate_focus) { const ndx = dx / arg.clientWidth; const ndy = dy / arg.clientHeight; const dxAngle = ndx * Math.PI * speed; const dyAngle = ndy * Math.PI * speed; - const newRotation = Quaternion.fromAxisAngle(Vector3.UNIT_Y, -dxAngle, new Quaternion()); - const tempRotation = Quaternion.fromAxisAngle(xAxis, -dyAngle, new Quaternion()); + const newRotation = Quaternion.fromAxisAngle(Vector3.Y, -dxAngle); + const tempRotation = Quaternion.fromAxisAngle(xAxis, -dyAngle); newRotation.multiply(tempRotation); const rotateMatrix = newRotation.toMatrix4(new Matrix4()); const targetPoint = Vector3.fromArray(arg.focusPoint as spec.vec3); - const deltaPosition = this.cameraCoordiante.origin.clone().subVector(targetPoint); + const deltaPosition = this.cameraCoordiante.origin.clone().subtract(targetPoint); - rotateMatrix.multiplyByPoint3(deltaPosition); - const newPosition = deltaPosition.addVector(targetPoint); + rotateMatrix.transformPoint(deltaPosition); + const newPosition = deltaPosition.add(targetPoint); newRotation.multiply(this.cameraTransform.getRotation()); item.transform.setPosition(newPosition.x, newPosition.y, newPosition.z); item.transform.setQuaternion(newRotation.x, newRotation.y, newRotation.z, newRotation.w); - item.updateTransform(); + item.setTransform(newPosition, item.transform.rotation); } else { console.warn('not implement'); } @@ -441,7 +444,7 @@ export class CameraGestureHandlerImp implements CameraGestureHandler { this.startParams.target = ''; } - private updateCameraTransform (cameraOptions: CameraOptions) { + private updateCameraTransform (cameraOptions: CameraOptionsEx) { const effectsTransfrom = new Transform(cameraOptions); effectsTransfrom.setValid(true); diff --git a/plugin-packages/model/src/gesture/protocol.ts b/plugin-packages/model/src/gesture/protocol.ts index d493d62b1..1ac65c78d 100644 --- a/plugin-packages/model/src/gesture/protocol.ts +++ b/plugin-packages/model/src/gesture/protocol.ts @@ -1,4 +1,4 @@ -import type { CameraOptions, spec } from '@galacean/effects'; +import type { CameraOptionsEx, spec } from '@galacean/effects'; export interface CameraGestureHandler { @@ -11,7 +11,7 @@ export interface CameraGestureHandler { * @param event - 键盘事件信息 * @returns 当前的相机参数 */ - onKeyEvent (event: CameraKeyEvent): CameraOptions, + onKeyEvent (event: CameraKeyEvent): CameraOptionsEx, /** * 在 XY 方向上平移相机开始函数,在鼠标按下时调用 @@ -22,7 +22,7 @@ export interface CameraGestureHandler { * @param cameraID - 要控制的相机 item id * @returns 当前的相机参数 */ - onXYMoveBegin (x: number, y: number, width: number, height: number, cameraID: string): CameraOptions, + onXYMoveBegin (x: number, y: number, width: number, height: number, cameraID: string): CameraOptionsEx, /** * 在 XY 方向上平移相机开始函数,在鼠标按下拖拽时调用 @@ -31,7 +31,7 @@ export interface CameraGestureHandler { * @param speed - 移动速度,默认是 0.015 * @returns 更新后的相机参数 */ - onXYMoving (x: number, y: number, speed?: number): CameraOptions, + onXYMoving (x: number, y: number, speed?: number): CameraOptionsEx, /** * 结束在 XY 方向上平移相机 @@ -47,7 +47,7 @@ export interface CameraGestureHandler { * @param cameraID - 要控制的相机 item id * @returns 当前的相机参数 */ - onZMoveBegin (x: number, y: number, width: number, height: number, cameraID: string): CameraOptions, + onZMoveBegin (x: number, y: number, width: number, height: number, cameraID: string): CameraOptionsEx, /** * 在 Z 方向上平移相机开始函数,类似缩放 3D 模型,在鼠标按下拖拽时调用 @@ -56,7 +56,7 @@ export interface CameraGestureHandler { * @param speed - 移动速度,默认是 0.015 * @returns 更新后的相机参数 */ - onZMoving (x: number, y: number, speed?: number): CameraOptions, + onZMoving (x: number, y: number, speed?: number): CameraOptionsEx, /** * 结束在 Z 方向上平移相机 @@ -72,7 +72,7 @@ export interface CameraGestureHandler { * @param cameraID - 要控制的相机 item id * @returns 当前的相机参数 */ - onRotateBegin (x: number, y: number, width: number, height: number, cameraID: string): CameraOptions, + onRotateBegin (x: number, y: number, width: number, height: number, cameraID: string): CameraOptionsEx, /** * 相机的自旋转,在鼠标按下拖拽时调用 @@ -81,7 +81,7 @@ export interface CameraGestureHandler { * @param speed - 移动速度,默认是 1 * @returns 更新后的相机参数 */ - onRotating (x: number, y: number, speed?: number): CameraOptions, + onRotating (x: number, y: number, speed?: number): CameraOptionsEx, /** * 相机的自旋转 @@ -97,7 +97,7 @@ export interface CameraGestureHandler { * @param cameraID - 要控制的相机 item id * @returns 当前的相机参数 */ - onRotatePointBegin (x: number, y: number, width: number, height: number, point: spec.vec3, cameraID: string): CameraOptions, + onRotatePointBegin (x: number, y: number, width: number, height: number, point: spec.vec3, cameraID: string): CameraOptionsEx, /** * 相机绕某个点旋转,在鼠标按下拖拽时调用 @@ -106,7 +106,7 @@ export interface CameraGestureHandler { * @param speed - 移动速度,默认是 1 * @returns 更新后的相机参数 */ - onRotatingPoint (x: number, y: number, speed?: number): CameraOptions, + onRotatingPoint (x: number, y: number, speed?: number): CameraOptionsEx, /** * 相机绕某个点旋转 diff --git a/plugin-packages/model/src/gltf/loader-ext.ts b/plugin-packages/model/src/gltf/loader-ext.ts index 27c1e215d..5fa15d00d 100644 --- a/plugin-packages/model/src/gltf/loader-ext.ts +++ b/plugin-packages/model/src/gltf/loader-ext.ts @@ -16,8 +16,7 @@ import { import type { Player, spec } from '@galacean/effects'; import type { LoadSceneOptions, LoadSceneResult } from './protocol'; import { LoaderImpl } from './loader-impl'; -import { Box3, Vector3 } from '../math'; -import { Sphere } from '../math/sphere'; +import { Box3, Vector3, Sphere } from '../runtime/math'; export class LoaderImplEx extends LoaderImpl { override async loadScene (options: LoadSceneOptions): Promise { @@ -143,8 +142,8 @@ export async function loadGLTFScene (options: LoadGLTFSceneOptions) { const sceneAABB = new Box3(sceneMin, sceneMax); const sceneRadius = sceneAABB.getBoundingSphere(new Sphere()).radius; const sceneCenter = sceneAABB.getCenter(new Vector3()); - const position = sceneCenter.addVector(new Vector3(0, 0, sceneRadius * 1.71)); - const cameraPosition = options.camera?.position ?? position.toArray() as spec.vec3; + const position = sceneCenter.add(new Vector3(0, 0, sceneRadius * 1.71)); + const cameraPosition = options.camera?.position ?? position.toArray(); const cameraRotation = options.camera?.rotation ?? [0, 0, 0]; items.push({ diff --git a/plugin-packages/model/src/gltf/loader-helper.ts b/plugin-packages/model/src/gltf/loader-helper.ts index c6031d33b..664953a4a 100644 --- a/plugin-packages/model/src/gltf/loader-helper.ts +++ b/plugin-packages/model/src/gltf/loader-helper.ts @@ -1,16 +1,19 @@ -import type { spec } from '@galacean/effects'; +import type { spec, math } from '@galacean/effects'; import { Transform as EffectsTransform } from '@galacean/effects'; import type { BaseTransform as Transform } from '../index'; -import { Vector3, Matrix4, Quaternion, Euler, EulerOrder } from '../math'; +import { Vector3, Matrix4, Quaternion, Euler, EulerOrder } from '../runtime/math'; + +type Euler = math.Euler; export class LoaderHelper { static getTransformFromMat4 (mat: Matrix4): Transform { - const transform = Matrix4.decompose(mat); + const transform = mat.getTransform(); + const euler = transform.rotation.toEuler(new Euler()); return { - position: transform.translation.xyz, - rotation: LoaderHelper.getEulerFromQuat(transform.rotation), - scale: transform.scale.xyz, + position: transform.translation.toArray(), + rotation: euler.toArray(), + scale: transform.scale.toArray(), }; } @@ -19,33 +22,33 @@ export class LoaderHelper { valid: true, }); - transform.cloneFromMatrix(mat.toArray() as spec.mat4); + transform.cloneFromMatrix(mat); return { - position: transform.position, - rotation: transform.rotation, - scale: transform.scale, + position: transform.position.toArray(), + rotation: transform.rotation.toArray(), + scale: transform.scale.toArray(), }; } static getTransformFromTranslation (t: Vector3): Transform { return { - position: t.xyz, + position: t.toArray(), }; } static getTransformFromDirection (d: Vector3): Transform { const d0 = Vector3.fromArray([1, 0, 0]); const d1 = d.clone().normalize(); - const a = Vector3.cross(d0, d1); + const a = d0.clone().cross(d1); if (a.length() < 0.01) { return {}; } a.normalize(); - const b = Vector3.dot(d1, d0); + const b = d1.dot(d0); const c = Math.acos(b); - const mat = Matrix4.IDENTITY.clone().rotate(c, a) as Matrix4; + const mat = Matrix4.fromRotationAxis(a, c); return LoaderHelper.getTransformFromMat4(mat); } @@ -53,15 +56,15 @@ export class LoaderHelper { static getEffectsTransformFromDirection (d: Vector3): Transform { const d0 = Vector3.fromArray([0, 0, 1]); const d1 = d.clone().normalize(); - const a = Vector3.cross(d0, d1); + const a = d0.clone().cross(d1); if (a.length() < 0.01) { return {}; } a.normalize(); - const b = Vector3.dot(d1, d0); + const b = d1.dot(d0); const c = Math.acos(b); - const mat = Matrix4.IDENTITY.clone().rotate(c, a) as Matrix4; + const mat = Matrix4.fromRotationAxis(a, c); return LoaderHelper.getEffectsTransformFromMat4(mat); } @@ -69,7 +72,7 @@ export class LoaderHelper { static getTransformFromTransDir (t: Vector3, d: Vector3): Transform { const transform = LoaderHelper.getTransformFromDirection(d); - transform.position = t.xyz; + transform.position = t.toArray(); return transform; } @@ -77,7 +80,7 @@ export class LoaderHelper { static getEffectsTransformFromTransDir (t: Vector3, d: Vector3): Transform { const transform = LoaderHelper.getEffectsTransformFromDirection(d); - transform.position = t.xyz; + transform.position = t.toArray(); return transform; } diff --git a/plugin-packages/model/src/gltf/loader-impl.ts b/plugin-packages/model/src/gltf/loader-impl.ts index b82509bd2..5906155eb 100644 --- a/plugin-packages/model/src/gltf/loader-impl.ts +++ b/plugin-packages/model/src/gltf/loader-impl.ts @@ -1,4 +1,4 @@ -import type { TransformProps, Texture, Attribute, Engine } from '@galacean/effects'; +import type { TransformProps, Texture, Attribute, Engine, math } from '@galacean/effects'; import { Transform as EffectsTransform, spec, glContext, Geometry } from '@galacean/effects'; import type { Loader, @@ -27,7 +27,7 @@ import type { ModelSkinOptions, ModelTextureTransform, } from '../index'; -import { Vector3, Box3, Matrix4 } from '../math'; +import { Vector3, Box3, Matrix4 } from '../runtime/math'; import { LoaderHelper } from './loader-helper'; import { WebGLHelper, PluginHelper } from '../utility/plugin-helper'; import type { @@ -51,6 +51,8 @@ import type { PImageBufferData, PSkyboxBufferParams } from '../runtime/skybox'; import { PSkyboxCreator, PSkyboxType } from '../runtime/skybox'; import type { CubeImage } from '@vvfx/resource-detection/dist/src/gltf-tools/gltf-image-based-light'; +type Box3 = math.Box3; + let globalGLTFLoader: Loader; export function getDefaultEffectsGLTFLoader (engine: Engine, options?: LoaderOptions): Loader { @@ -153,11 +155,11 @@ export class LoaderImpl implements Loader { if (node.matrix !== undefined) { if (node.matrix.length !== 16) { throw new Error(`Invalid matrix length ${node.matrix.length} for node ${node}`); } const mat = Matrix4.fromArray(node.matrix); - const trans = mat.decompose(); + const transform = mat.getTransform(); - pos = trans.translation.toArray() as spec.vec3; - quat = trans.rotation.toArray() as spec.vec4; - scale = trans.scale.toArray() as spec.vec3; + pos = transform.translation.toArray(); + quat = transform.rotation.toArray(); + scale = transform.scale.toArray(); } else { if (node.translation !== undefined) { pos = node.translation as spec.vec3; } if (node.rotation !== undefined) { quat = node.rotation as spec.vec4; } @@ -388,8 +390,8 @@ export class LoaderImpl implements Loader { source: this.getRemarkString(), items: modelItems, sceneAABB: { - min: sceneAABB.min.toArray() as spec.vec3, - max: sceneAABB.max.toArray() as spec.vec3, + min: sceneAABB.min.toArray(), + max: sceneAABB.max.toArray(), }, }; } @@ -638,11 +640,11 @@ export class LoaderImpl implements Loader { const transformData: TransformProps = {}; if (node.matrix) { - const trans = Matrix4.fromArray(node.matrix).decompose(); + const trans = Matrix4.fromArray(node.matrix).getTransform(); - transformData.position = trans.translation.toArray() as spec.vec3; - transformData.quat = trans.rotation.toArray() as spec.vec4; - transformData.scale = trans.scale.toArray() as spec.vec3; + transformData.position = trans.translation.toArray(); + transformData.quat = trans.rotation.toArray(); + transformData.scale = trans.scale.toArray(); } else { if (node.translation) { transformData.position = node.translation as spec.vec3; } if (node.rotation) { transformData.quat = node.rotation as spec.vec4; } @@ -657,7 +659,7 @@ export class LoaderImpl implements Loader { const mesh = this._gltfMeshs[node.mesh]; const meshAABB = GLTFHelper.createBoxFromGLTFBound(mesh.bounds as GLTFBounds); - meshAABB.transform(Matrix4.fromArray(nodeTransform.getWorldMatrix())); + meshAABB.applyMatrix4(nodeTransform.getWorldMatrix()); sceneAABB.union(meshAABB); } diff --git a/plugin-packages/model/src/index.ts b/plugin-packages/model/src/index.ts index efa9a89c0..75c35707e 100644 --- a/plugin-packages/model/src/index.ts +++ b/plugin-packages/model/src/index.ts @@ -43,7 +43,6 @@ export type ModelAnimationOptions = spec.ModelAnimationOptions<'studio'>; export * from './gesture'; export * from './gltf'; -export * from './math'; export * from './plugin'; export * from './runtime'; export * from './utility'; diff --git a/plugin-packages/model/src/math/box3.ts b/plugin-packages/model/src/math/box3.ts deleted file mode 100644 index def4db9f2..000000000 --- a/plugin-packages/model/src/math/box3.ts +++ /dev/null @@ -1,343 +0,0 @@ -import type { Sphere } from './sphere'; -import { Matrix4 } from './matrix4'; -import { Vector3 } from './vector3'; - -/** - * 三维包围盒 - */ -class Box3 { - constructor ( - public min: Vector3 = new Vector3(Infinity, Infinity, Infinity), - public max: Vector3 = new Vector3(-Infinity, -Infinity, -Infinity), - ) { } - - set (min: Vector3, max: Vector3): Box3 { - this.min.copyFrom(min); - this.max.copyFrom(max); - - return this; - } - - /** - * 由数组构建三维包围盒 - * @param array - * @returns - */ - setFromArray (array: number[]): Box3 { - let minX = Number(Infinity); - let minY = Number(Infinity); - let minZ = Number(Infinity); - - let maxX = -Infinity; - let maxY = -Infinity; - let maxZ = -Infinity; - - for (let i = 0, l = array.length; i < l; i += 3) { - const x = array[i]; - const y = array[i + 1]; - const z = array[i + 2]; - - if (x < minX) { minX = x; } - if (y < minY) { minY = y; } - if (z < minZ) { minZ = z; } - - if (x > maxX) { maxX = x; } - if (y > maxY) { maxY = y; } - if (z > maxZ) { maxZ = z; } - } - - this.min.set(minX, minY, minZ); - this.max.set(maxX, maxY, maxZ); - - return this; - } - - /** - * 由三维空间点构建三维包围盒 - * @param points - * @returns - */ - setFromPoints (points: Vector3[]): Box3 { - this.makeEmpty(); - - for (let i = 0, il = points.length; i < il; i++) { - this.expandByPoint(points[i]); - } - - return this; - } - - /** - * 由三维空间点(包围盒中心)和大小确定包围盒 - * @param center - * @param size - * @returns - */ - setFromCenterAndSize (center: Vector3, size: Vector3): Box3 { - const halfSize = size.clone().multiplyScalar(0.5); - - this.min.copyFrom(center).subVector(halfSize); - this.max.copyFrom(center).subVector(halfSize); - - return this; - } - - clone (): Box3 { - return new Box3().copyFrom(this); - } - - copyFrom (box: Box3): Box3 { - this.min.copyFrom(box.min); - this.max.copyFrom(box.max); - - return this; - } - - makeEmpty (): Box3 { - this.min.x = this.min.y = this.min.z = Number(Infinity); - this.max.x = this.max.y = this.max.z = -Infinity; - - return this; - } - - isEmpty (): boolean { - // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes - - return (this.max.x < this.min.x) || (this.max.y < this.min.y) || (this.max.z < this.min.z); - } - - /** - * 获取三维包围盒中心 - * @param target - * @returns - */ - getCenter (target: Vector3): Vector3 { - if (this.isEmpty()) { - target.set(0, 0, 0); - } else { - Vector3.add(this.min, this.max, target).multiplyScalar(0.5); - } - - return target; - } - - /** - * 获取三维包围盒大小 - * @param target - * @returns - */ - getSize (target: Vector3): Vector3 { - if (this.isEmpty()) { - target.set(0, 0, 0); - } else { - Vector3.subtract(this.max, this.min, target); - } - - return target; - } - - /** - * 通过三维空间点扩展三维包围盒 - * @param point - * @returns - */ - expandByPoint (point: Vector3): Box3 { - Vector3.min(this.min, point, this.min); - Vector3.max(this.max, point, this.max); - - return this; - } - - /** - * 通过三维向量扩展三维包围盒 - * @param vector - * @returns - */ - expandByVector (vector: Vector3): Box3 { - this.min.subVector(vector); - this.max.addVector(vector); - - return this; - } - - /** - * 通过实数扩展三维包围盒 - * @param scalar - * @returns - */ - expandByScalar (scalar: number): Box3 { - this.min.addScalar(-scalar); - this.max.addScalar(scalar); - - return this; - } - - /** - * 通过包围盒扩展三维包围盒 - * @param box - * @returns - */ - expandByBox (box: Box3): Box3 { - Vector3.min(this.min, box.min, this.min); - Vector3.max(this.max, box.max, this.max); - - return this; - } - - /** - * 判断三维包围盒与三维空间点的关系 - * @param point - * @returns - */ - containsPoint (point: Vector3): boolean { - return !(point.x < this.min.x || point.x > this.max.x - || point.y < this.min.y || point.y > this.max.y - || point.z < this.min.z || point.z > this.max.z); - } - - /** - * 判断 this 是否包含 other - * @param other - * @returns - */ - containsBox (other: Box3): boolean { - return this.min.x <= other.min.x && other.max.x <= this.max.x - && this.min.y <= other.min.y && other.max.y <= this.max.y - && this.min.z <= other.min.z && other.max.z <= this.max.z; - } - - /** - * 判断三维包围盒 this 与 other 是否相交 - * @param other - * @returns - */ - intersectsBox (other: Box3): boolean { - // using 6 splitting planes to rule out intersections. - return !(other.max.x < this.min.x || other.min.x > this.max.x - || other.max.y < this.min.y || other.min.y > this.max.y - || other.max.z < this.min.z || other.min.z > this.max.z); - } - - /** - * 判断三维包围盒和球是否相交 - * @param sphere - * @returns - */ - intersectsSphere (sphere: Sphere) { - // Find the point on the AABB closest to the sphere center. - const _vector = new Vector3(); - - this.clampPoint(sphere.center, _vector); - - // If that point is inside the sphere, the AABB and sphere intersect. - return _vector.distanceSquaredTo(sphere.center) <= (sphere.radius * sphere.radius); - } - - /** - * 限制空间点位于三维包围盒内 - * @param point - * @param target - * @returns - */ - clampPoint (point: Vector3, target: Vector3): Vector3 { - return target.copyFrom(point).clamp(this.min, this.max); - } - - /** - * 三维空间点到三维包围盒的距离 - * @param point - * @returns - */ - distanceToPoint (point: Vector3): number { - const clampedPoint = point.clone().clamp(this.min, this.max); - - return clampedPoint.subVector(point).length(); - } - - /** - * 通过包围盒获取包围球 - * @param target - * @returns - */ - getBoundingSphere (target: Sphere) { - this.getCenter(target.center); - - const _vector = new Vector3(); - - target.radius = this.getSize(_vector).length() * 0.5; - - return target; - } - - /** - * 三维包围盒交集 - * @param box - * @returns - */ - intersect (box: Box3): Box3 { - this.min.max(box.min); - this.max.min(box.max); - - // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. - if (this.isEmpty()) { this.makeEmpty(); } - - return this; - } - - /** - * 三维包围盒并集 - * @param box - * @returns - */ - union (box: Box3): Box3 { - this.min.min(box.min); - this.max.max(box.max); - - return this; - } - - /** - * 通过矩阵变化三维包围盒 - * @param matrix - * @returns - */ - transform (matrix: Matrix4): Box3 { - // transform of empty box is an empty box. - if (this.isEmpty()) { return this; } - - const points: Vector3[] = []; - - // NOTE: I am using a binary pattern to specify all 2^3 combinations below - points[0] = new Vector3(this.min.x, this.min.y, this.min.z); // 000 - points[1] = new Vector3(this.min.x, this.min.y, this.max.z); // 001 - points[2] = new Vector3(this.min.x, this.max.y, this.min.z); // 010 - points[3] = new Vector3(this.min.x, this.max.y, this.max.z); // 011 - points[4] = new Vector3(this.max.x, this.min.y, this.min.z); // 100 - points[5] = new Vector3(this.max.x, this.min.y, this.max.z); // 101 - points[6] = new Vector3(this.max.x, this.max.y, this.min.z); // 110 - points[7] = new Vector3(this.max.x, this.max.y, this.max.z); // 111 - points.forEach(p => Matrix4.multiplyByPoint(matrix, p, p)); - - this.setFromPoints(points); - - return this; - } - - /** - * 根据向量改变三维包围盒位置 - * @param offset - * @returns - */ - translate (offset: Vector3): Box3 { - this.min.addVector(offset); - this.max.addVector(offset); - - return this; - } - - equals (other: Box3): boolean { - return Vector3.equals(other.min, this.min) && Vector3.equals(other.max, this.max); - } -} - -export { Box3 }; diff --git a/plugin-packages/model/src/math/euler.ts b/plugin-packages/model/src/math/euler.ts deleted file mode 100644 index fd645348c..000000000 --- a/plugin-packages/model/src/math/euler.ts +++ /dev/null @@ -1,383 +0,0 @@ -import type { Vector3 } from './vector3'; -import { Matrix4 } from './matrix4'; -import { MathUtils } from './utilities'; -import { Quaternion } from './quaternion'; - -/** - * 欧拉角顺序,默认为 XYZ - */ -enum EulerOrder { - 'XYZ' = 0, - 'XZY' = 1, - 'YXZ' = 2, - 'YZX' = 3, - 'ZXY' = 4, - 'ZYX' = 5, -} - -/** - * 欧拉角 - */ -class Euler { - private _data: Float32Array; - - constructor (x = 0, y = 0, z = 0, private _order = EulerOrder.ZYX) { - this._data = new Float32Array([x, y, z]); - } - - get x (): number { - return this._data[0]; - } - set x (value: number) { - this._data[0] = value; - } - - getX (): number { - return this._data[0]; - } - - setX (value: number) { - this._data[0] = value; - } - - get y (): number { - return this._data[1]; - } - set y (value: number) { - this._data[1] = value; - } - - getY (): number { - return this._data[1]; - } - - setY (value: number) { - this._data[1] = value; - } - - get z (): number { - return this._data[2]; - } - set z (value: number) { - this._data[2] = value; - } - - getZ (): number { - return this._data[2]; - } - - setZ (value: number) { - this._data[2] = value; - } - - get order (): EulerOrder { - return this._order; - } - set order (value: EulerOrder) { - this._order = value; - } - - getOrder (): EulerOrder { - return this._order; - } - - setOrder (value: EulerOrder) { - this._order = value; - } - - set (x: number, y: number, z: number, order?: EulerOrder): Euler { - this._data[0] = x; - this._data[1] = y; - this._data[2] = z; - this._order = order === undefined ? EulerOrder.ZYX : order; - - return this; - } - - /** - * 使用欧拉角内部数据克隆出一个新的欧拉角 - * @returns - */ - clone () { - const result = Euler.clone(this); - - return result; - } - - /** - * 将欧拉角数据拷贝给 result - * @param result - * @returns - */ - copyTo (result: Euler) { - result = Euler.copyTo(this, result); - - return result; - } - - /** - * 将欧拉角数据从 source 拷贝回来 - * @param source - * @returns - */ - copyFrom (source: Euler) { - Euler.copyTo(source, this); - - return this; - } - - /** - * 通过变换矩阵获取欧拉角 - * @param m - * @param order - * @returns - */ - setFromRotationMatrix (m: Matrix4, order: EulerOrder = EulerOrder.XYZ): Euler { - return Euler.setFromRotationMatrix(m, order, this); - } - - /** - * 通过四元数设置欧拉角 - * @param quat - * @param order - * @returns - */ - setFromQuaternion (quat: Quaternion, order: EulerOrder = EulerOrder.XYZ): Euler { - return Euler.setFromQuaternion(quat, order, this); - } - - /** - * 通过三维向量值设置欧拉角(无几何意义) - * @param v - * @param order - * @returns - */ - setFromVector3 (v: Vector3, order?: EulerOrder): Euler { - return this.set(v.x, v.y, v.z, order || this._order); - } - - /** - * 修改欧拉角顺序 - * @param newOrder - * @returns - */ - reorder (newOrder: EulerOrder): Euler { - return Euler.reorder(this, newOrder); - } - - equals (euler: Euler): boolean { - return (euler.x === this.x) && (euler.y === this.y) && (euler.z === this.z) && (euler._order === this._order); - } - - /** - * 通过数组设置欧拉角 - * @param array - * @returns - */ - fromArray (array: number[]): Euler { - this.x = array[0]; - this.y = array[1]; - this.z = array[2]; - if (array[3] !== undefined) { this._order = array[3]; } - - return this; - } - - /** - * 将欧拉角保存为数组 - * @param array - * @returns - */ - toArray (array: number[] = [], offset = 0): number[] { - array[offset] = this.x; - array[offset + 1] = this.y; - array[offset + 2] = this.z; - array[offset + 3] = this._order; - - return array; - } - - /** - * 通过向量保存欧拉角 - * @param array - * @returns - */ - toVector3 (result: Vector3): Vector3 { - result.set(this.x, this.y, this.z); - - return result; - } - - /** - * 从输入欧拉角克隆一个欧拉角 - * @param eular - * @returns - */ - static clone (eular: Euler) { - const result = new Euler(); - - result.x = eular.x; - result.y = eular.y; - result.z = eular.z; - result.order = eular.order; - - return result; - } - - /** - * 欧拉角复制 - * @param eular - * @param result - * @returns - */ - static copyTo (eular: Euler, result: Euler) { - result.x = eular.x; - result.y = eular.y; - result.z = eular.z; - result.order = eular.order; - - return result; - } - - /** - * 从矩阵中的旋转分量设置欧拉角 - * @param m - * @param order - * @param result - * @returns - */ - static setFromRotationMatrix (m: Matrix4, order: EulerOrder, result: Euler): Euler { - const clamp = MathUtils.clamp; - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - const te = m.getData(); - const m11 = te[0]; const m12 = te[4]; - const m13 = te[8]; - const m21 = te[1]; const m22 = te[5]; - const m23 = te[9]; - const m31 = te[2]; const m32 = te[6]; - const m33 = te[10]; - - order = order || result._order; - - switch (order) { - case EulerOrder.XYZ: - - result.y = Math.asin(clamp(m13, -1, 1)); - - if (Math.abs(m13) < 0.9999999) { - result.x = Math.atan2(-m23, m33); - result.z = Math.atan2(-m12, m11); - } else { - result.x = Math.atan2(m32, m22); - result.z = 0; - } - - break; - case EulerOrder.YXZ: - - result.x = Math.asin(-clamp(m23, -1, 1)); - - if (Math.abs(m23) < 0.9999999) { - result.y = Math.atan2(m13, m33); - result.z = Math.atan2(m21, m22); - } else { - result.y = Math.atan2(-m31, m11); - result.z = 0; - } - - break; - case EulerOrder.ZXY: - - result.x = Math.asin(clamp(m32, -1, 1)); - - if (Math.abs(m32) < 0.9999999) { - result.y = Math.atan2(-m31, m33); - result.z = Math.atan2(-m12, m22); - } else { - result.y = 0; - result.z = Math.atan2(m21, m11); - } - - break; - case EulerOrder.ZYX: - - result.y = Math.asin(-clamp(m31, -1, 1)); - - if (Math.abs(m31) < 0.9999999) { - result.x = Math.atan2(m32, m33); - result.z = Math.atan2(m21, m11); - } else { - result.x = 0; - result.z = Math.atan2(-m12, m22); - } - - break; - case EulerOrder.YZX: - - result.z = Math.asin(clamp(m21, -1, 1)); - - if (Math.abs(m21) < 0.9999999) { - result.x = Math.atan2(-m23, m22); - result.y = Math.atan2(-m31, m11); - } else { - result.x = 0; - result.y = Math.atan2(m13, m33); - } - - break; - case EulerOrder.XZY: - - result.z = Math.asin(-clamp(m12, -1, 1)); - - if (Math.abs(m12) < 0.9999999) { - result.x = Math.atan2(m32, m22); - result.y = Math.atan2(m13, m11); - } else { - result.x = Math.atan2(-m23, m33); - result.y = 0; - } - - break; - default: - - console.warn(`Euler.setFromRotationMatrix() encountered an unknown order: ${order}`); - } - - result._order = order; - - return result; - } - - /** - * 通过四元数设置欧拉角 - * @param quat - * @param order - * @param result - * @returns - */ - static setFromQuaternion (quat: Quaternion, order: EulerOrder, result: Euler): Euler { - const matrix = new Matrix4(); - - quat.toMatrix4(matrix); - - return Euler.setFromRotationMatrix(matrix, order, result); - } - - /** - * 修改欧拉角顺序 - * @param euler - * @param newOrder - * @returns - */ - static reorder (euler: Euler, newOrder: EulerOrder): Euler { - const quat = new Quaternion(); - - Quaternion.setFromEuler(euler, quat); - - return Euler.setFromQuaternion(quat, newOrder, euler); - } -} - -export { Euler, EulerOrder }; diff --git a/plugin-packages/model/src/math/index.ts b/plugin-packages/model/src/math/index.ts deleted file mode 100644 index 43176667d..000000000 --- a/plugin-packages/model/src/math/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -export * from './type'; -export * from './vector2'; -export * from './vector3'; -export * from './vector4'; -export * from './matrix2'; -export * from './matrix3'; -export * from './matrix4'; -export * from './quaternion'; -export * from './euler'; -export * from './box3'; -export * from './sphere'; diff --git a/plugin-packages/model/src/math/matrix2.ts b/plugin-packages/model/src/math/matrix2.ts deleted file mode 100644 index 26e42c5c7..000000000 --- a/plugin-packages/model/src/math/matrix2.ts +++ /dev/null @@ -1,422 +0,0 @@ -import type { Mat2DataType } from './type'; -import { Vector2 } from './vector2'; - -/** - * 2x2 矩阵内部按照列主序存储数据 - */ -class Matrix2 { - - private _data: Float32Array; - - /** - * 按照行主序传入矩阵数据 - * @param column0Row0 - 第0行0列 - * @param column1Row0 - 第0行1列 - * @param column0Row1 - * @param column1Row1 - */ - constructor (column0Row0?: number, column1Row0?: number, column0Row1?: number, column1Row1?: number) { - this._data = new Float32Array(4); - this._data[0] = (column0Row0 ?? 0.0); - this._data[1] = (column0Row1 ?? 0.0); - this._data[2] = (column1Row0 ?? 0.0); - this._data[3] = (column1Row1 ?? 0.0); - } - - getData () { - return this._data; - } - - // Matrix2.packedLength = 4; - - static pack (value: Matrix2, array: Mat2DataType, startingIndex?: number) { - startingIndex = (startingIndex ?? 0); - - array[startingIndex++] = value._data[0]; - array[startingIndex++] = value._data[1]; - array[startingIndex++] = value._data[2]; - array[startingIndex++] = value._data[3]; - - return array; - } - - static unpack (array: Mat2DataType, startingIndex: number, result: Matrix2) { - result._data[0] = array[startingIndex++]; - result._data[1] = array[startingIndex++]; - result._data[2] = array[startingIndex++]; - result._data[3] = array[startingIndex++]; - - return result; - } - - static packArray (array: Matrix2[], result: Mat2DataType) { - const length = array.length; - - for (let i = 0; i < length; ++i) { - Matrix2.pack(array[i], result, i * 4); - } - - return result; - } - - static unpackArray (array: Mat2DataType, result: Matrix2[]) { - const length = array.length; - - for (let i = 0; i < length; i += 4) { - const index = i / 4; - - result[index] = Matrix2.unpack(array, i, result[index]); - } - - return result; - } - - static clone (matrix: Matrix2) { - return new Matrix2(matrix._data[0], matrix._data[2], matrix._data[1], matrix._data[3]); - } - - static fromArray (array: Mat2DataType) { - return Matrix2.unpack(array, 0, new Matrix2()); - } - - static fromColumnMajorArray (values: Mat2DataType, result: Matrix2) { - return Matrix2.unpack(values, 0, result); - } - - static fromRowMajorArray (values: Mat2DataType, result: Matrix2) { - if (result === undefined) { - return new Matrix2(values[0], values[1], values[2], values[3]); - } - - result._data[0] = values[0]; - result._data[1] = values[2]; - result._data[2] = values[1]; - result._data[3] = values[3]; - - return result; - } - - static fromScale (scale: Vector2, result: Matrix2) { - result._data[0] = scale.x; - result._data[1] = 0.0; - result._data[2] = 0.0; - result._data[3] = scale.y; - - return result; - } - - static fromUniformScale (scale: number, result: Matrix2) { - result._data[0] = scale; - result._data[1] = 0.0; - result._data[2] = 0.0; - result._data[3] = scale; - - return result; - } - - static fromRotation (angle: number, result: Matrix2) { - const cosAngle = Math.cos(angle); - const sinAngle = Math.sin(angle); - - result._data[0] = cosAngle; - result._data[1] = sinAngle; - result._data[2] = -sinAngle; - result._data[3] = cosAngle; - - return result; - } - - static toArray (matrix: Matrix2, result: Mat2DataType) { - result[0] = matrix._data[0]; - result[1] = matrix._data[1]; - result[2] = matrix._data[2]; - result[3] = matrix._data[3]; - - return result; - } - - static getElement (matrix: Matrix2, column: number, row: number) { - return matrix._data[column * 2 + row]; - } - - static getColumn (matrix: Matrix2, index: number, result: Vector2) { - const startIndex = index * 2; - const x = matrix._data[startIndex]; - const y = matrix._data[startIndex + 1]; - - result.x = x; - result.y = y; - - return result; - } - - static setColumn (matrix: Matrix2, index: number, cartesian: Vector2, result: Matrix2) { - result = matrix.copyTo(result); - const startIndex = index * 2; - - result._data[startIndex] = cartesian.x; - result._data[startIndex + 1] = cartesian.y; - - return result; - } - - static getRow (matrix: Matrix2, index: number, result?: Vector2) { - if (result === undefined) { - result = new Vector2(); - } - const x = matrix._data[index]; - const y = matrix._data[index + 2]; - - result.x = x; - result.y = y; - - return result; - } - - static setRow (matrix: Matrix2, index: number, cartesian: Vector2, result: Matrix2) { - result = matrix.copyTo(result); - result._data[index] = cartesian.x; - result._data[index + 2] = cartesian.y; - - return result; - } - - static scale (matrix: Matrix2, scale: Vector2, result?: Matrix2) { - const existingScale = Matrix2.getScale(matrix, scaleScratch1); - const scaleRatioX = scale.x / existingScale.x; - const scaleRatioY = scale.y / existingScale.y; - - if (result === undefined) { - result = new Matrix2(); - } - result._data[0] = matrix._data[0] * scaleRatioX; - result._data[1] = matrix._data[1] * scaleRatioX; - result._data[2] = matrix._data[2] * scaleRatioY; - result._data[3] = matrix._data[3] * scaleRatioY; - - return result; - } - - static setUniformScale (matrix: Matrix2, scale: number, result?: Matrix2) { - const existingScale = Matrix2.getScale(matrix, scaleScratch2); - const scaleRatioX = scale / existingScale.x; - const scaleRatioY = scale / existingScale.y; - - if (result === undefined) { - result = new Matrix2(); - } - result._data[0] = matrix._data[0] * scaleRatioX; - result._data[1] = matrix._data[1] * scaleRatioX; - result._data[2] = matrix._data[2] * scaleRatioY; - result._data[3] = matrix._data[3] * scaleRatioY; - - return result; - } - - static getScale (matrix: Matrix2, result: Vector2) { - scratchColumn.set(matrix._data[0], matrix._data[1]); - result.x = Vector2.magnitude(scratchColumn); - - scratchColumn.set(matrix._data[2], matrix._data[3]); - result.y = Vector2.magnitude(scratchColumn); - - return result; - } - - static getMaximumScale (matrix: Matrix2) { - Matrix2.getScale(matrix, scaleScratch3); - - return Vector2.maximumComponent(scaleScratch3); - } - - static setRotation (matrix: Matrix2, rotation: Matrix2, result: Matrix2) { - const scale = Matrix2.getScale(matrix, scaleScratch4); - - result._data[0] = rotation._data[0] * scale.x; - result._data[1] = rotation._data[1] * scale.x; - result._data[2] = rotation._data[2] * scale.y; - result._data[3] = rotation._data[3] * scale.y; - - return result; - } - - static getRotation (matrix: Matrix2, result: Matrix2) { - const scale = Matrix2.getScale(matrix, scaleScratch5); - - result._data[0] = matrix._data[0] / scale.x; - result._data[1] = matrix._data[1] / scale.x; - result._data[2] = matrix._data[2] / scale.y; - result._data[3] = matrix._data[3] / scale.y; - - return result; - } - - static multiply (left: Matrix2, right: Matrix2, result: Matrix2) { - const column0Row0 = left._data[0] * right._data[0] + left._data[2] * right._data[1]; - const column1Row0 = left._data[0] * right._data[2] + left._data[2] * right._data[3]; - const column0Row1 = left._data[1] * right._data[0] + left._data[3] * right._data[1]; - const column1Row1 = left._data[1] * right._data[2] + left._data[3] * right._data[3]; - - result._data[0] = column0Row0; - result._data[1] = column0Row1; - result._data[2] = column1Row0; - result._data[3] = column1Row1; - - return result; - } - - static add (left: Matrix2, right: Matrix2, result: Matrix2) { - result._data[0] = left._data[0] + right._data[0]; - result._data[1] = left._data[1] + right._data[1]; - result._data[2] = left._data[2] + right._data[2]; - result._data[3] = left._data[3] + right._data[3]; - - return result; - } - - static subtract (left: Matrix2, right: Matrix2, result: Matrix2) { - result._data[0] = left._data[0] - right._data[0]; - result._data[1] = left._data[1] - right._data[1]; - result._data[2] = left._data[2] - right._data[2]; - result._data[3] = left._data[3] - right._data[3]; - - return result; - } - - static multiplyByVector (matrix: Matrix2, cartesian: Vector2, result: Vector2) { - const x = matrix._data[0] * cartesian.x + matrix._data[2] * cartesian.y; - const y = matrix._data[1] * cartesian.x + matrix._data[3] * cartesian.y; - - result.x = x; - result.y = y; - - return result; - } - - static multiplyByScalar (matrix: Matrix2, scalar: number, result: Matrix2) { - result._data[0] = matrix._data[0] * scalar; - result._data[1] = matrix._data[1] * scalar; - result._data[2] = matrix._data[2] * scalar; - result._data[3] = matrix._data[3] * scalar; - - return result; - } - - static multiplyByScale (matrix: Matrix2, scale: Vector2, result: Matrix2) { - result._data[0] = matrix._data[0] * scale.x; - result._data[1] = matrix._data[1] * scale.x; - result._data[2] = matrix._data[2] * scale.y; - result._data[3] = matrix._data[3] * scale.y; - - return result; - } - - static multiplyByUniformScale (matrix: Matrix2, scale: number, result: Matrix2) { - result._data[0] = matrix._data[0] * scale; - result._data[1] = matrix._data[1] * scale; - result._data[2] = matrix._data[2] * scale; - result._data[3] = matrix._data[3] * scale; - - return result; - } - - static negate (matrix: Matrix2, result: Matrix2) { - result._data[0] = -matrix._data[0]; - result._data[1] = -matrix._data[1]; - result._data[2] = -matrix._data[2]; - result._data[3] = -matrix._data[3]; - - return result; - } - - static transpose (matrix: Matrix2, result: Matrix2) { - const column0Row0 = matrix._data[0]; - const column0Row1 = matrix._data[2]; - const column1Row0 = matrix._data[1]; - const column1Row1 = matrix._data[3]; - - result._data[0] = column0Row0; - result._data[1] = column0Row1; - result._data[2] = column1Row0; - result._data[3] = column1Row1; - - return result; - } - - static abs (matrix: Matrix2, result: Matrix2) { - result._data[0] = Math.abs(matrix._data[0]); - result._data[1] = Math.abs(matrix._data[1]); - result._data[2] = Math.abs(matrix._data[2]); - result._data[3] = Math.abs(matrix._data[3]); - - return result; - } - - static equals (left: Matrix2, right: Matrix2) { - return ( - left === right || - (left._data[0] === right._data[0] && - left._data[1] === right._data[1] && - left._data[2] === right._data[2] && - left._data[3] === right._data[3])); - } - - static equalsArray (matrix: Matrix2, array: Mat2DataType, offset?: number) { - offset = offset ?? 0; - - return ( - matrix._data[0] === array[offset] && - matrix._data[1] === array[offset + 1] && - matrix._data[2] === array[offset + 2] && - matrix._data[3] === array[offset + 3] - ); - } - - static equalsEpsilon (left: Matrix2, right: Matrix2, epsilon: number) { - epsilon = (epsilon ?? 0); - - return ( - left === right || - ( - Math.abs(left._data[0] - right._data[0]) <= epsilon && - Math.abs(left._data[1] - right._data[1]) <= epsilon && - Math.abs(left._data[2] - right._data[2]) <= epsilon && - Math.abs(left._data[3] - right._data[3]) <= epsilon) - ); - } - - copyTo (result: Matrix2) { - - result._data[0] = this._data[0]; - result._data[1] = this._data[1]; - result._data[2] = this._data[2]; - result._data[3] = this._data[3]; - - return result; - - } - - copyFrom (source: Matrix2) { - - this._data[0] = source._data[0]; - this._data[1] = source._data[1]; - this._data[2] = source._data[2]; - this._data[3] = source._data[3]; - - return this; - - } - - static IDENTITY = Object.freeze(new Matrix2(1.0, 0.0, 0.0, 1.0)); - - static ZERO = Object.freeze(new Matrix2(0.0, 0.0, 0.0, 0.0)); -} - -const scaleScratch1 = new Vector2(); -const scaleScratch2 = new Vector2(); -const scratchColumn = new Vector2(); -const scaleScratch3 = new Vector2(); -const scaleScratch4 = new Vector2(); -const scaleScratch5 = new Vector2(); - -export { Matrix2 }; diff --git a/plugin-packages/model/src/math/matrix3.ts b/plugin-packages/model/src/math/matrix3.ts deleted file mode 100644 index efb66264a..000000000 --- a/plugin-packages/model/src/math/matrix3.ts +++ /dev/null @@ -1,932 +0,0 @@ -import { Vector3 } from './vector3'; -import type { Quaternion } from './quaternion'; -import type { Mat3DataType } from './type'; - -class Matrix3 { - private _data: Float32Array; - - constructor ( - column0Row0?: number, - column1Row0?: number, - column2Row0?: number, - column0Row1?: number, - column1Row1?: number, - column2Row1?: number, - column0Row2?: number, - column1Row2?: number, - column2Row2?: number) { - this._data = new Float32Array(9); - this._data[0] = (column0Row0 ?? 0.0); - this._data[1] = (column0Row1 ?? 0.0); - this._data[2] = (column0Row2 ?? 0.0); - this._data[3] = (column1Row0 ?? 0.0); - this._data[4] = (column1Row1 ?? 0.0); - this._data[5] = (column1Row2 ?? 0.0); - this._data[6] = (column2Row0 ?? 0.0); - this._data[7] = (column2Row1 ?? 0.0); - this._data[8] = (column2Row2 ?? 0.0); - } - - getData () { - return this._data; - } - - // Matrix3.packedLength = 9; - - static pack (value: Matrix3, array: Mat3DataType, startingIndex?: number) { - startingIndex = (startingIndex ?? 0); - - array[startingIndex++] = value._data[0]; - array[startingIndex++] = value._data[1]; - array[startingIndex++] = value._data[2]; - array[startingIndex++] = value._data[3]; - array[startingIndex++] = value._data[4]; - array[startingIndex++] = value._data[5]; - array[startingIndex++] = value._data[6]; - array[startingIndex++] = value._data[7]; - array[startingIndex++] = value._data[8]; - - return array; - } - - static unpack (array: Mat3DataType, startingIndex: number, result: Matrix3) { - result._data[0] = array[startingIndex++]; - result._data[1] = array[startingIndex++]; - result._data[2] = array[startingIndex++]; - result._data[3] = array[startingIndex++]; - result._data[4] = array[startingIndex++]; - result._data[5] = array[startingIndex++]; - result._data[6] = array[startingIndex++]; - result._data[7] = array[startingIndex++]; - result._data[8] = array[startingIndex++]; - - return result; - } - - static packArray (array: Matrix3[], result: Mat3DataType) { - const length = array.length; - - for (let i = 0; i < length; ++i) { - Matrix3.pack(array[i], result, i * 9); - } - - return result; - } - - static unpackArray (array: Mat3DataType, result: Matrix3[]) { - const length = array.length; - - for (let i = 0; i < length; i += 9) { - const index = i / 9; - - result[index] = Matrix3.unpack(array, i, result[index]); - } - - return result; - } - - static clone (matrix: Matrix3) { - const result = new Matrix3(); - - result._data[0] = matrix._data[0]; - result._data[1] = matrix._data[1]; - result._data[2] = matrix._data[2]; - result._data[3] = matrix._data[3]; - result._data[4] = matrix._data[4]; - result._data[5] = matrix._data[5]; - result._data[6] = matrix._data[6]; - result._data[7] = matrix._data[7]; - result._data[8] = matrix._data[8]; - - return result; - } - - static copyTo (matrix: Matrix3, result: Matrix3) { - result._data[0] = matrix._data[0]; - result._data[1] = matrix._data[1]; - result._data[2] = matrix._data[2]; - result._data[3] = matrix._data[3]; - result._data[4] = matrix._data[4]; - result._data[5] = matrix._data[5]; - result._data[6] = matrix._data[6]; - result._data[7] = matrix._data[7]; - result._data[8] = matrix._data[8]; - - return result; - } - - static fromArray (array: Mat3DataType) { - return Matrix3.unpack(array, 0, new Matrix3()); - } - - static fromColumnMajorArray (values: Mat3DataType, result: Matrix3) { - return Matrix3.unpack(values, 0, result); - } - - static fromRowMajorArray (values: Mat3DataType, result: Matrix3) { - result._data[0] = values[0]; - result._data[1] = values[3]; - result._data[2] = values[6]; - result._data[3] = values[1]; - result._data[4] = values[4]; - result._data[5] = values[7]; - result._data[6] = values[2]; - result._data[7] = values[5]; - result._data[8] = values[8]; - - return result; - } - - static fromQuaternion (quaternion: Quaternion, result: Matrix3) { - const x2 = quaternion.x * quaternion.x; - const xy = quaternion.x * quaternion.y; - const xz = quaternion.x * quaternion.z; - const xw = quaternion.x * quaternion.w; - const y2 = quaternion.y * quaternion.y; - const yz = quaternion.y * quaternion.z; - const yw = quaternion.y * quaternion.w; - const z2 = quaternion.z * quaternion.z; - const zw = quaternion.z * quaternion.w; - const w2 = quaternion.w * quaternion.w; - - const m00 = x2 - y2 - z2 + w2; - const m01 = 2.0 * (xy - zw); - const m02 = 2.0 * (xz + yw); - - const m10 = 2.0 * (xy + zw); - const m11 = -x2 + y2 - z2 + w2; - const m12 = 2.0 * (yz - xw); - - const m20 = 2.0 * (xz - yw); - const m21 = 2.0 * (yz + xw); - const m22 = -x2 - y2 + z2 + w2; - - result._data[0] = m00; - result._data[1] = m10; - result._data[2] = m20; - result._data[3] = m01; - result._data[4] = m11; - result._data[5] = m21; - result._data[6] = m02; - result._data[7] = m12; - result._data[8] = m22; - - return result; - } - - static fromHeadingPitchRoll (headingPitchRoll: { heading: number, pitch: number, roll: number }, result: Matrix3) { - const cosTheta = Math.cos(-headingPitchRoll.pitch); - const cosPsi = Math.cos(-headingPitchRoll.heading); - const cosPhi = Math.cos(headingPitchRoll.roll); - const sinTheta = Math.sin(-headingPitchRoll.pitch); - const sinPsi = Math.sin(-headingPitchRoll.heading); - const sinPhi = Math.sin(headingPitchRoll.roll); - - const m00 = cosTheta * cosPsi; - const m01 = -cosPhi * sinPsi + sinPhi * sinTheta * cosPsi; - const m02 = sinPhi * sinPsi + cosPhi * sinTheta * cosPsi; - - const m10 = cosTheta * sinPsi; - const m11 = cosPhi * cosPsi + sinPhi * sinTheta * sinPsi; - const m12 = -sinPhi * cosPsi + cosPhi * sinTheta * sinPsi; - - const m20 = -sinTheta; - const m21 = sinPhi * cosTheta; - const m22 = cosPhi * cosTheta; - - result._data[0] = m00; - result._data[1] = m10; - result._data[2] = m20; - result._data[3] = m01; - result._data[4] = m11; - result._data[5] = m21; - result._data[6] = m02; - result._data[7] = m12; - result._data[8] = m22; - - return result; - } - - static fromScale (scale: Vector3, result: Matrix3) { - result._data[0] = scale.x; - result._data[1] = 0.0; - result._data[2] = 0.0; - result._data[3] = 0.0; - result._data[4] = scale.y; - result._data[5] = 0.0; - result._data[6] = 0.0; - result._data[7] = 0.0; - result._data[8] = scale.z; - - return result; - } - - static fromUniformScale (scale: number, result: Matrix3) { - result._data[0] = scale; - result._data[1] = 0.0; - result._data[2] = 0.0; - result._data[3] = 0.0; - result._data[4] = scale; - result._data[5] = 0.0; - result._data[6] = 0.0; - result._data[7] = 0.0; - result._data[8] = scale; - - return result; - } - - static fromCrossProduct (vector: Vector3, result: Matrix3) { - result._data[0] = 0.0; - result._data[1] = vector.z; - result._data[2] = -vector.y; - result._data[3] = -vector.z; - result._data[4] = 0.0; - result._data[5] = vector.x; - result._data[6] = vector.y; - result._data[7] = -vector.x; - result._data[8] = 0.0; - - return result; - } - - static fromRotationX (angle: number, result: Matrix3) { - const cosAngle = Math.cos(angle); - const sinAngle = Math.sin(angle); - - result._data[0] = 1.0; - result._data[1] = 0.0; - result._data[2] = 0.0; - result._data[3] = 0.0; - result._data[4] = cosAngle; - result._data[5] = sinAngle; - result._data[6] = 0.0; - result._data[7] = -sinAngle; - result._data[8] = cosAngle; - - return result; - } - - static fromRotationY (angle: number, result: Matrix3) { - const cosAngle = Math.cos(angle); - const sinAngle = Math.sin(angle); - - result._data[0] = cosAngle; - result._data[1] = 0.0; - result._data[2] = -sinAngle; - result._data[3] = 0.0; - result._data[4] = 1.0; - result._data[5] = 0.0; - result._data[6] = sinAngle; - result._data[7] = 0.0; - result._data[8] = cosAngle; - - return result; - } - - static fromRotationZ (angle: number, result: Matrix3) { - const cosAngle = Math.cos(angle); - const sinAngle = Math.sin(angle); - - result._data[0] = cosAngle; - result._data[1] = sinAngle; - result._data[2] = 0.0; - result._data[3] = -sinAngle; - result._data[4] = cosAngle; - result._data[5] = 0.0; - result._data[6] = 0.0; - result._data[7] = 0.0; - result._data[8] = 1.0; - - return result; - } - - static toArray (matrix: Matrix3, result: Mat3DataType) { - result[0] = matrix._data[0]; - result[1] = matrix._data[1]; - result[2] = matrix._data[2]; - result[3] = matrix._data[3]; - result[4] = matrix._data[4]; - result[5] = matrix._data[5]; - result[6] = matrix._data[6]; - result[7] = matrix._data[7]; - result[8] = matrix._data[8]; - - return result; - } - - static getElement (matrix: Matrix3, column: number, row: number) { - return matrix._data[column * 3 + row]; - } - - static getColumn (matrix: Matrix3, index: number, result: Vector3) { - const startIndex = index * 3; - const x = matrix._data[startIndex]; - const y = matrix._data[startIndex + 1]; - const z = matrix._data[startIndex + 2]; - - result.x = x; - result.y = y; - result.z = z; - - return result; - } - - static setColumn (matrix: Matrix3, index: number, cartesian: Vector3, result: Matrix3) { - result = Matrix3.copyTo(matrix, result); - - const startIndex = index * 3; - - result._data[startIndex] = cartesian.x; - result._data[startIndex + 1] = cartesian.y; - result._data[startIndex + 2] = cartesian.z; - - return result; - } - - static getRow (matrix: Matrix3, index: number, result: Vector3) { - const x = matrix._data[index]; - const y = matrix._data[index + 3]; - const z = matrix._data[index + 6]; - - result.x = x; - result.y = y; - result.z = z; - - return result; - } - - static setRow (matrix: Matrix3, index: number, cartesian: Vector3, result: Matrix3) { - result = Matrix3.copyTo(matrix, result); - result._data[index] = cartesian.x; - result._data[index + 3] = cartesian.y; - result._data[index + 6] = cartesian.z; - - return result; - } - - static scale (matrix: Matrix3, scale: Vector3, result: Matrix3) { - const existingScale = Matrix3.getScale(matrix, scaleScratch1); - const scaleRatioX = scale.x / existingScale.x; - const scaleRatioY = scale.y / existingScale.y; - const scaleRatioZ = scale.z / existingScale.z; - - result._data[0] = matrix._data[0] * scaleRatioX; - result._data[1] = matrix._data[1] * scaleRatioX; - result._data[2] = matrix._data[2] * scaleRatioX; - result._data[3] = matrix._data[3] * scaleRatioY; - result._data[4] = matrix._data[4] * scaleRatioY; - result._data[5] = matrix._data[5] * scaleRatioY; - result._data[6] = matrix._data[6] * scaleRatioZ; - result._data[7] = matrix._data[7] * scaleRatioZ; - result._data[8] = matrix._data[8] * scaleRatioZ; - - return result; - } - - static setUniformScale (matrix: Matrix3, scale: number, result: Matrix3) { - const existingScale = Matrix3.getScale(matrix, scaleScratch2); - const scaleRatioX = scale / existingScale.x; - const scaleRatioY = scale / existingScale.y; - const scaleRatioZ = scale / existingScale.z; - - result._data[0] = matrix._data[0] * scaleRatioX; - result._data[1] = matrix._data[1] * scaleRatioX; - result._data[2] = matrix._data[2] * scaleRatioX; - result._data[3] = matrix._data[3] * scaleRatioY; - result._data[4] = matrix._data[4] * scaleRatioY; - result._data[5] = matrix._data[5] * scaleRatioY; - result._data[6] = matrix._data[6] * scaleRatioZ; - result._data[7] = matrix._data[7] * scaleRatioZ; - result._data[8] = matrix._data[8] * scaleRatioZ; - - return result; - } - - static getScale (matrix: Matrix3, result: Vector3) { - scratchColumn.set(matrix._data[0], matrix._data[1], matrix._data[2]); - result.x = Vector3.magnitude(scratchColumn); - - scratchColumn.set(matrix._data[3], matrix._data[4], matrix._data[5]); - result.y = Vector3.magnitude(scratchColumn); - - scratchColumn.set(matrix._data[6], matrix._data[7], matrix._data[8]); - result.z = Vector3.magnitude(scratchColumn); - - return result; - } - - static getMaximumScale (matrix: Matrix3) { - Matrix3.getScale(matrix, scaleScratch3); - - return Vector3.maximumComponent(scaleScratch3); - } - - static setRotation (matrix: Matrix3, rotation: Matrix3, result: Matrix3) { - const scale = Matrix3.getScale(matrix, scaleScratch4); - - result._data[0] = rotation._data[0] * scale.x; - result._data[1] = rotation._data[1] * scale.x; - result._data[2] = rotation._data[2] * scale.x; - result._data[3] = rotation._data[3] * scale.y; - result._data[4] = rotation._data[4] * scale.y; - result._data[5] = rotation._data[5] * scale.y; - result._data[6] = rotation._data[6] * scale.z; - result._data[7] = rotation._data[7] * scale.z; - result._data[8] = rotation._data[8] * scale.z; - - return result; - } - - static getRotation (matrix: Matrix3, result: Matrix3) { - const scale = Matrix3.getScale(matrix, scaleScratch5); - - result._data[0] = matrix._data[0] / scale.x; - result._data[1] = matrix._data[1] / scale.x; - result._data[2] = matrix._data[2] / scale.x; - result._data[3] = matrix._data[3] / scale.y; - result._data[4] = matrix._data[4] / scale.y; - result._data[5] = matrix._data[5] / scale.y; - result._data[6] = matrix._data[6] / scale.z; - result._data[7] = matrix._data[7] / scale.z; - result._data[8] = matrix._data[8] / scale.z; - - return result; - } - - static multiply (left: Matrix3, right: Matrix3, result: Matrix3) { - const column0Row0 = - left._data[0] * right._data[0] + left._data[3] * right._data[1] + left._data[6] * right._data[2]; - const column0Row1 = - left._data[1] * right._data[0] + left._data[4] * right._data[1] + left._data[7] * right._data[2]; - const column0Row2 = - left._data[2] * right._data[0] + left._data[5] * right._data[1] + left._data[8] * right._data[2]; - - const column1Row0 = - left._data[0] * right._data[3] + left._data[3] * right._data[4] + left._data[6] * right._data[5]; - const column1Row1 = - left._data[1] * right._data[3] + left._data[4] * right._data[4] + left._data[7] * right._data[5]; - const column1Row2 = - left._data[2] * right._data[3] + left._data[5] * right._data[4] + left._data[8] * right._data[5]; - - const column2Row0 = - left._data[0] * right._data[6] + left._data[3] * right._data[7] + left._data[6] * right._data[8]; - const column2Row1 = - left._data[1] * right._data[6] + left._data[4] * right._data[7] + left._data[7] * right._data[8]; - const column2Row2 = - left._data[2] * right._data[6] + left._data[5] * right._data[7] + left._data[8] * right._data[8]; - - result._data[0] = column0Row0; - result._data[1] = column0Row1; - result._data[2] = column0Row2; - result._data[3] = column1Row0; - result._data[4] = column1Row1; - result._data[5] = column1Row2; - result._data[6] = column2Row0; - result._data[7] = column2Row1; - result._data[8] = column2Row2; - - return result; - } - - static add (left: Matrix3, right: Matrix3, result: Matrix3) { - result._data[0] = left._data[0] + right._data[0]; - result._data[1] = left._data[1] + right._data[1]; - result._data[2] = left._data[2] + right._data[2]; - result._data[3] = left._data[3] + right._data[3]; - result._data[4] = left._data[4] + right._data[4]; - result._data[5] = left._data[5] + right._data[5]; - result._data[6] = left._data[6] + right._data[6]; - result._data[7] = left._data[7] + right._data[7]; - result._data[8] = left._data[8] + right._data[8]; - - return result; - } - - static subtract (left: Matrix3, right: Matrix3, result: Matrix3) { - result._data[0] = left._data[0] - right._data[0]; - result._data[1] = left._data[1] - right._data[1]; - result._data[2] = left._data[2] - right._data[2]; - result._data[3] = left._data[3] - right._data[3]; - result._data[4] = left._data[4] - right._data[4]; - result._data[5] = left._data[5] - right._data[5]; - result._data[6] = left._data[6] - right._data[6]; - result._data[7] = left._data[7] - right._data[7]; - result._data[8] = left._data[8] - right._data[8]; - - return result; - } - - static multiplyByVector (matrix: Matrix3, cartesian: Vector3, result: Vector3) { - const vX = cartesian.x; - const vY = cartesian.y; - const vZ = cartesian.z; - - const x = matrix._data[0] * vX + matrix._data[3] * vY + matrix._data[6] * vZ; - const y = matrix._data[1] * vX + matrix._data[4] * vY + matrix._data[7] * vZ; - const z = matrix._data[2] * vX + matrix._data[5] * vY + matrix._data[8] * vZ; - - result.x = x; - result.y = y; - result.z = z; - - return result; - } - - static multiplyByScalar (matrix: Matrix3, scalar: number, result: Matrix3) { - result._data[0] = matrix._data[0] * scalar; - result._data[1] = matrix._data[1] * scalar; - result._data[2] = matrix._data[2] * scalar; - result._data[3] = matrix._data[3] * scalar; - result._data[4] = matrix._data[4] * scalar; - result._data[5] = matrix._data[5] * scalar; - result._data[6] = matrix._data[6] * scalar; - result._data[7] = matrix._data[7] * scalar; - result._data[8] = matrix._data[8] * scalar; - - return result; - } - - static multiplyByScale (matrix: Matrix3, scale: Vector3, result: Matrix3) { - result._data[0] = matrix._data[0] * scale.x; - result._data[1] = matrix._data[1] * scale.x; - result._data[2] = matrix._data[2] * scale.x; - result._data[3] = matrix._data[3] * scale.y; - result._data[4] = matrix._data[4] * scale.y; - result._data[5] = matrix._data[5] * scale.y; - result._data[6] = matrix._data[6] * scale.z; - result._data[7] = matrix._data[7] * scale.z; - result._data[8] = matrix._data[8] * scale.z; - - return result; - } - - static multiplyByUniformScale (matrix: Matrix3, scale: number, result: Matrix3) { - result._data[0] = matrix._data[0] * scale; - result._data[1] = matrix._data[1] * scale; - result._data[2] = matrix._data[2] * scale; - result._data[3] = matrix._data[3] * scale; - result._data[4] = matrix._data[4] * scale; - result._data[5] = matrix._data[5] * scale; - result._data[6] = matrix._data[6] * scale; - result._data[7] = matrix._data[7] * scale; - result._data[8] = matrix._data[8] * scale; - - return result; - } - - static negate (matrix: Matrix3, result: Matrix3) { - result._data[0] = -matrix._data[0]; - result._data[1] = -matrix._data[1]; - result._data[2] = -matrix._data[2]; - result._data[3] = -matrix._data[3]; - result._data[4] = -matrix._data[4]; - result._data[5] = -matrix._data[5]; - result._data[6] = -matrix._data[6]; - result._data[7] = -matrix._data[7]; - result._data[8] = -matrix._data[8]; - - return result; - } - - static transpose (matrix: Matrix3, result: Matrix3) { - const column0Row0 = matrix._data[0]; - const column0Row1 = matrix._data[3]; - const column0Row2 = matrix._data[6]; - const column1Row0 = matrix._data[1]; - const column1Row1 = matrix._data[4]; - const column1Row2 = matrix._data[7]; - const column2Row0 = matrix._data[2]; - const column2Row1 = matrix._data[5]; - const column2Row2 = matrix._data[8]; - - result._data[0] = column0Row0; - result._data[1] = column0Row1; - result._data[2] = column0Row2; - result._data[3] = column1Row0; - result._data[4] = column1Row1; - result._data[5] = column1Row2; - result._data[6] = column2Row0; - result._data[7] = column2Row1; - result._data[8] = column2Row2; - - return result; - } - - static abs (matrix: Matrix3, result: Matrix3) { - if (result === undefined) { - result = new Matrix3(); - } - result._data[0] = Math.abs(matrix._data[0]); - result._data[1] = Math.abs(matrix._data[1]); - result._data[2] = Math.abs(matrix._data[2]); - result._data[3] = Math.abs(matrix._data[3]); - result._data[4] = Math.abs(matrix._data[4]); - result._data[5] = Math.abs(matrix._data[5]); - result._data[6] = Math.abs(matrix._data[6]); - result._data[7] = Math.abs(matrix._data[7]); - result._data[8] = Math.abs(matrix._data[8]); - - return result; - } - - static determinant (matrix: Matrix3) { - const m11 = matrix._data[0]; - const m21 = matrix._data[3]; - const m31 = matrix._data[6]; - const m12 = matrix._data[1]; - const m22 = matrix._data[4]; - const m32 = matrix._data[7]; - const m13 = matrix._data[2]; - const m23 = matrix._data[5]; - const m33 = matrix._data[8]; - - return ( - m11 * (m22 * m33 - m23 * m32) + - m12 * (m23 * m31 - m21 * m33) + - m13 * (m21 * m32 - m22 * m31) - ); - } - - static inverse (matrix: Matrix3, result: Matrix3) { - const m11 = matrix._data[0]; - const m21 = matrix._data[1]; - const m31 = matrix._data[2]; - const m12 = matrix._data[3]; - const m22 = matrix._data[4]; - const m32 = matrix._data[5]; - const m13 = matrix._data[6]; - const m23 = matrix._data[7]; - const m33 = matrix._data[8]; - - const determinant = Matrix3.determinant(matrix); - - // //>>includeStart('debug', pragmas.debug); - // if (Math.abs(determinant) <= CesiumMath.EPSILON15) { - // throw new DeveloperError("matrix is not invertible"); - // } - // //>>includeEnd('debug'); - - result._data[0] = m22 * m33 - m23 * m32; - result._data[1] = m23 * m31 - m21 * m33; - result._data[2] = m21 * m32 - m22 * m31; - result._data[3] = m13 * m32 - m12 * m33; - result._data[4] = m11 * m33 - m13 * m31; - result._data[5] = m12 * m31 - m11 * m32; - result._data[6] = m12 * m23 - m13 * m22; - result._data[7] = m13 * m21 - m11 * m23; - result._data[8] = m11 * m22 - m12 * m21; - - const scale = 1.0 / determinant; - - return Matrix3.multiplyByScalar(result, scale, result); - } - - static inverseTranspose (matrix: Matrix3, result: Matrix3) { - return Matrix3.inverse( - Matrix3.transpose(matrix, scratchTransposeMatrix), - result - ); - } - - static equals (left: Matrix3, right: Matrix3) { - return ( - left === right || - ( - left._data[0] === right._data[0] && - left._data[1] === right._data[1] && - left._data[2] === right._data[2] && - left._data[3] === right._data[3] && - left._data[4] === right._data[4] && - left._data[5] === right._data[5] && - left._data[6] === right._data[6] && - left._data[7] === right._data[7] && - left._data[8] === right._data[8]) - ); - } - - static equalsEpsilon (left: Matrix3, right: Matrix3, epsilon: number) { - epsilon = (epsilon ?? 0); - - return ( - left === right || - ( - Math.abs(left._data[0] - right._data[0]) <= epsilon && - Math.abs(left._data[1] - right._data[1]) <= epsilon && - Math.abs(left._data[2] - right._data[2]) <= epsilon && - Math.abs(left._data[3] - right._data[3]) <= epsilon && - Math.abs(left._data[4] - right._data[4]) <= epsilon && - Math.abs(left._data[5] - right._data[5]) <= epsilon && - Math.abs(left._data[6] - right._data[6]) <= epsilon && - Math.abs(left._data[7] - right._data[7]) <= epsilon && - Math.abs(left._data[8] - right._data[8]) <= epsilon) - ); - } - - static getElementIndex (column: number, row: number) { - return column * 3 + row; - } - - getElement (column: number, row: number) { - return Matrix3.getElement(this, column, row); - } - - clone () { - const result = Matrix3.clone(this); - - return result; - } - - copyTo (result: Matrix3) { - result = Matrix3.copyTo(this, result); - - return result; - } - - copyFrom (source: Matrix3) { - Matrix3.copyTo(source, this); - - return this; - } - - scale (vector: Vector3) { - const result = Matrix3.scale(this, vector, this); - - return result; - } - - multiply (matrix: Matrix3): Matrix3 { - const result = Matrix3.multiply(this, matrix, this); - - return result; - } - - /** - * 将矩阵数据按照列主序导出为number[]对象。 - * @returns - */ - toArray (): number[] { - const array = new Array(9); - const result = Matrix3.toArray(this, array) as number[]; - - return result; - } - - /** - * 将矩阵按照列主序方式导出为Float32Array对象。 - * @returns - */ - toFloat32Array (): Float32Array { - const result = new Float32Array(9); - - Matrix3.pack(this, result, 0); - - return result; - } - - /** - * 矩阵与四维向量相乘。 - * @param vector - * @returns - */ - multiplyByVector3 (vector: Vector3) { - const result = Matrix3.multiplyByVector(this, vector, new Vector3()); - - return result; - } - - /** - * 返回矩阵中指定索引位置数据 - * @param index - * @returns - */ - at (index: number) { - return this._data[index]; - } - - /** - * 矩阵乘一个旋转矩阵,矩阵乘一个绕axis轴旋转angle角度的旋转矩阵 - * @param angle - * @param axis - * @returns - */ - rotate (angle: number, axis: Vector3): Matrix3 | undefined { - let x = axis.x; - let y = axis.y; - let z = axis.z; - - let length = Math.sqrt(x * x + y * y + z * z); - - if (!length) { - return undefined; - } - - if (length !== 1) { - length = 1 / length; - x *= length; - y *= length; - z *= length; - } - - const s = Math.sin(angle); - const c = Math.cos(angle); - - const t = 1.0 - c; - - const a00 = this._data[0]; - const a01 = this._data[1]; - const a02 = this._data[2]; - const a03 = this._data[3]; - - const a10 = this._data[4]; - const a11 = this._data[5]; - const a12 = this._data[6]; - const a13 = this._data[7]; - - const a20 = this._data[8]; - const a21 = this._data[9]; - const a22 = this._data[10]; - const a23 = this._data[11]; - - const b00 = x * x * t + c; - const b01 = y * x * t + z * s; - const b02 = z * x * t - y * s; - - const b10 = x * y * t - z * s; - const b11 = y * y * t + c; - const b12 = z * y * t + x * s; - - const b20 = x * z * t + y * s; - const b21 = y * z * t - x * s; - const b22 = z * z * t + c; - - this._data[0] = a00 * b00 + a10 * b01 + a20 * b02; - this._data[1] = a01 * b00 + a11 * b01 + a21 * b02; - this._data[2] = a02 * b00 + a12 * b01 + a22 * b02; - this._data[3] = a03 * b00 + a13 * b01 + a23 * b02; - - this._data[4] = a00 * b10 + a10 * b11 + a20 * b12; - this._data[5] = a01 * b10 + a11 * b11 + a21 * b12; - this._data[6] = a02 * b10 + a12 * b11 + a22 * b12; - this._data[7] = a03 * b10 + a13 * b11 + a23 * b12; - - this._data[8] = a00 * b20 + a10 * b21 + a20 * b22; - this._data[9] = a01 * b20 + a11 * b21 + a21 * b22; - this._data[10] = a02 * b20 + a12 * b21 + a22 * b22; - this._data[11] = a03 * b20 + a13 * b21 + a23 * b22; - - return this; - } - - /** - * 计算矩阵的逆矩阵,会修改矩阵数据 - */ - inverse () { - const result = Matrix3.inverse(this, this); - - return result; - } - - /** - * 计算矩阵的转置,结果保存在原矩阵中(会修改矩阵数据) - * @returns - */ - transpose () { - const result = Matrix3.transpose(this, this); - - return result; - } - - static IDENTITY = Object.freeze( - new Matrix3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0) - ); - - static ZERO = Object.freeze( - new Matrix3(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) - ); - - static COLUMN0ROW0 = 0; - static COLUMN0ROW1 = 1; - static COLUMN0ROW2 = 2; - static COLUMN1ROW0 = 3; - static COLUMN1ROW1 = 4; - static COLUMN1ROW2 = 5; - static COLUMN2ROW0 = 6; - static COLUMN2ROW1 = 7; - static COLUMN2ROW2 = 8; -} - -const scaleScratch1 = new Vector3(); - -const scaleScratch2 = new Vector3(); - -const scratchColumn = new Vector3(); - -const scaleScratch3 = new Vector3(); - -const scaleScratch4 = new Vector3(); - -const scaleScratch5 = new Vector3(); - -const scratchTransposeMatrix = new Matrix3(); - -export { Matrix3 }; diff --git a/plugin-packages/model/src/math/matrix4.ts b/plugin-packages/model/src/math/matrix4.ts deleted file mode 100644 index bebdcc616..000000000 --- a/plugin-packages/model/src/math/matrix4.ts +++ /dev/null @@ -1,2225 +0,0 @@ -import { Vector3 } from './vector3'; -import { Vector4 } from './vector4'; -import { Matrix3 } from './matrix3'; -import { Quaternion } from './quaternion'; -import type { Mat4DataType } from './type'; -import { MathUtils } from './utilities'; - -/** - * 表示一个 4x4 矩阵 - */ -class Matrix4 { - private _data: Float32Array; - - constructor ( - column0Row0?: number, - column1Row0?: number, - column2Row0?: number, - column3Row0?: number, - column0Row1?: number, - column1Row1?: number, - column2Row1?: number, - column3Row1?: number, - column0Row2?: number, - column1Row2?: number, - column2Row2?: number, - column3Row2?: number, - column0Row3?: number, - column1Row3?: number, - column2Row3?: number, - column3Row3?: number - ) { - this._data = new Float32Array(16); - this._data[0] = (column0Row0 ?? 0.0); - this._data[1] = (column0Row1 ?? 0.0); - this._data[2] = (column0Row2 ?? 0.0); - this._data[3] = (column0Row3 ?? 0.0); - this._data[4] = (column1Row0 ?? 0.0); - this._data[5] = (column1Row1 ?? 0.0); - this._data[6] = (column1Row2 ?? 0.0); - this._data[7] = (column1Row3 ?? 0.0); - this._data[8] = (column2Row0 ?? 0.0); - this._data[9] = (column2Row1 ?? 0.0); - this._data[10] = (column2Row2 ?? 0.0); - this._data[11] = (column2Row3 ?? 0.0); - this._data[12] = (column3Row0 ?? 0.0); - this._data[13] = (column3Row1 ?? 0.0); - this._data[14] = (column3Row2 ?? 0.0); - this._data[15] = (column3Row3 ?? 0.0); - } - - /** - * 获取矩阵内部存储数据的Float32Array对象。 - * @returns - */ - getData () { - return this._data; - } - setData (data: Float32Array) { - this._data = data; - } - - get data () { - return this._data; - } - - lookAt (position: Vector3, target: Vector3, up: Vector3) { - return Matrix4.computeLookAt(position, target, up, this); - } - - perspective (fovY: number, aspect: number, near: number, far: number, reverse: boolean) { - return Matrix4.computePerspective(fovY, aspect, near, far, reverse, this); - } - - orth2d ( - left: number, right: number, - bottom: number, top: number, - near: number, far: number - ) { - return Matrix4.computeOrthographic(left, right, bottom, top, near, far, this); - } - - compose (translation: Vector3, rotation: Quaternion, scale: Vector3) { - return Matrix4.compose(translation, rotation, scale, this); - } - - decompose () { - return Matrix4.decompose(this); - } - - multiplyByPoint3 (result: Vector3) { - - return Matrix4.multiplyByPoint(this, result, result); - } - - setZero () { - const data = this._data; - - for (let i = 0; i < data.length; i++) { - data[i] = 0; - } - } - - setIdentity () { - const data = this._data; - - for (let i = 0; i < data.length; i++) { - data[i] = i % 5 ? 0 : 1; - } - } - - /** - * 打包矩阵数据,将矩阵内部数据从数组指定索引位置打包到数组中。 - * @param value - * @param array - * @param startingIndex - * @returns - */ - static pack (value: Matrix4, array: Mat4DataType, startingIndex?: number) { - startingIndex = (startingIndex ?? 0); - - if (array.length < startingIndex + 16) { - throw '数组长度不够'; - } - - array[startingIndex++] = value._data[0]; - array[startingIndex++] = value._data[1]; - array[startingIndex++] = value._data[2]; - array[startingIndex++] = value._data[3]; - array[startingIndex++] = value._data[4]; - array[startingIndex++] = value._data[5]; - array[startingIndex++] = value._data[6]; - array[startingIndex++] = value._data[7]; - array[startingIndex++] = value._data[8]; - array[startingIndex++] = value._data[9]; - array[startingIndex++] = value._data[10]; - array[startingIndex++] = value._data[11]; - array[startingIndex++] = value._data[12]; - array[startingIndex++] = value._data[13]; - array[startingIndex++] = value._data[14]; - array[startingIndex] = value._data[15]; - - return array; - } - - /** - * 解包矩阵数据,从数组指定索引位置开始解包矩阵数据。 - * @param array - * @param startingIndex - * @param result - * @returns - */ - static unpack (array: Mat4DataType, startingIndex: number, result: Matrix4) { - startingIndex = (startingIndex ?? 0); - - result._data[0] = array[startingIndex++]; - result._data[1] = array[startingIndex++]; - result._data[2] = array[startingIndex++]; - result._data[3] = array[startingIndex++]; - result._data[4] = array[startingIndex++]; - result._data[5] = array[startingIndex++]; - result._data[6] = array[startingIndex++]; - result._data[7] = array[startingIndex++]; - result._data[8] = array[startingIndex++]; - result._data[9] = array[startingIndex++]; - result._data[10] = array[startingIndex++]; - result._data[11] = array[startingIndex++]; - result._data[12] = array[startingIndex++]; - result._data[13] = array[startingIndex++]; - result._data[14] = array[startingIndex++]; - result._data[15] = array[startingIndex]; - - return result; - } - - static packArray (array: Matrix4[], result: Mat4DataType) { - const length = array.length; - - for (let i = 0; i < length; ++i) { - Matrix4.pack(array[i], result, i * 16); - } - - return result; - } - - static unpackArray (array: Mat4DataType, result: Matrix4[]) { - const length = array.length; - - for (let i = 0; i < length; i += 16) { - const index = i / 16; - - result[index] = Matrix4.unpack(array, i, result[index]); - } - - return result; - } - - static clone (matrix: Matrix4 | Readonly) { - const result = new Matrix4(); - - result._data[0] = matrix.getData()[0]; - result._data[1] = matrix.getData()[1]; - result._data[2] = matrix.getData()[2]; - result._data[3] = matrix.getData()[3]; - result._data[4] = matrix.getData()[4]; - result._data[5] = matrix.getData()[5]; - result._data[6] = matrix.getData()[6]; - result._data[7] = matrix.getData()[7]; - result._data[8] = matrix.getData()[8]; - result._data[9] = matrix.getData()[9]; - result._data[10] = matrix.getData()[10]; - result._data[11] = matrix.getData()[11]; - result._data[12] = matrix.getData()[12]; - result._data[13] = matrix.getData()[13]; - result._data[14] = matrix.getData()[14]; - result._data[15] = matrix.getData()[15]; - - return result; - } - - static copyTo (matrix: Matrix4, result: Matrix4) { - result._data[0] = matrix.getData()[0]; - result._data[1] = matrix.getData()[1]; - result._data[2] = matrix.getData()[2]; - result._data[3] = matrix.getData()[3]; - result._data[4] = matrix.getData()[4]; - result._data[5] = matrix.getData()[5]; - result._data[6] = matrix.getData()[6]; - result._data[7] = matrix.getData()[7]; - result._data[8] = matrix.getData()[8]; - result._data[9] = matrix.getData()[9]; - result._data[10] = matrix.getData()[10]; - result._data[11] = matrix.getData()[11]; - result._data[12] = matrix.getData()[12]; - result._data[13] = matrix.getData()[13]; - result._data[14] = matrix.getData()[14]; - result._data[15] = matrix.getData()[15]; - - return result; - } - - static fromArray (array: Mat4DataType) { - return Matrix4.unpack(array, 0, new Matrix4()); - } - - static fromColumnMajorArray (values: Mat4DataType, result: Matrix4) { - return Matrix4.unpack(values, 0, result); - } - - static fromRowMajorArray (values: Mat4DataType, result: Matrix4) { - result._data[0] = values[0]; - result._data[1] = values[4]; - result._data[2] = values[8]; - result._data[3] = values[12]; - result._data[4] = values[1]; - result._data[5] = values[5]; - result._data[6] = values[9]; - result._data[7] = values[13]; - result._data[8] = values[2]; - result._data[9] = values[6]; - result._data[10] = values[10]; - result._data[11] = values[14]; - result._data[12] = values[3]; - result._data[13] = values[7]; - result._data[14] = values[11]; - result._data[15] = values[15]; - - return result; - } - - static fromRotationTranslation (rotation: Matrix3 | Readonly, translation: Vector3, result: Matrix4) { - result._data[0] = rotation.getData()[0]; - result._data[1] = rotation.getData()[1]; - result._data[2] = rotation.getData()[2]; - result._data[3] = 0.0; - result._data[4] = rotation.getData()[3]; - result._data[5] = rotation.getData()[4]; - result._data[6] = rotation.getData()[5]; - result._data[7] = 0.0; - result._data[8] = rotation.getData()[6]; - result._data[9] = rotation.getData()[7]; - result._data[10] = rotation.getData()[8]; - result._data[11] = 0.0; - result._data[12] = translation.x; - result._data[13] = translation.y; - result._data[14] = translation.z; - result._data[15] = 1.0; - - return result; - } - - static fromTranslationQuaternionRotationScale ( - translation: Vector3, - rotation: Quaternion, - scale: Vector3, - result: Matrix4 - ) { - const scaleX = scale.x; - const scaleY = scale.y; - const scaleZ = scale.z; - - const x2 = rotation.x * rotation.x; - const xy = rotation.x * rotation.y; - const xz = rotation.x * rotation.z; - const xw = rotation.x * rotation.w; - const y2 = rotation.y * rotation.y; - const yz = rotation.y * rotation.z; - const yw = rotation.y * rotation.w; - const z2 = rotation.z * rotation.z; - const zw = rotation.z * rotation.w; - const w2 = rotation.w * rotation.w; - - const m00 = x2 - y2 - z2 + w2; - const m01 = 2.0 * (xy - zw); - const m02 = 2.0 * (xz + yw); - - const m10 = 2.0 * (xy + zw); - const m11 = -x2 + y2 - z2 + w2; - const m12 = 2.0 * (yz - xw); - - const m20 = 2.0 * (xz - yw); - const m21 = 2.0 * (yz + xw); - const m22 = -x2 - y2 + z2 + w2; - - result._data[0] = m00 * scaleX; - result._data[1] = m10 * scaleX; - result._data[2] = m20 * scaleX; - result._data[3] = 0.0; - result._data[4] = m01 * scaleY; - result._data[5] = m11 * scaleY; - result._data[6] = m21 * scaleY; - result._data[7] = 0.0; - result._data[8] = m02 * scaleZ; - result._data[9] = m12 * scaleZ; - result._data[10] = m22 * scaleZ; - result._data[11] = 0.0; - result._data[12] = translation.x; - result._data[13] = translation.y; - result._data[14] = translation.z; - result._data[15] = 1.0; - - return result; - } - - static fromTranslationRotationScale ( - translationRotationScale: { translation: Vector3, rotation: Quaternion, scale: Vector3 }, - result: Matrix4 - ) { - return Matrix4.fromTranslationQuaternionRotationScale( - translationRotationScale.translation, - translationRotationScale.rotation, - translationRotationScale.scale, - result - ); - } - - static fromTranslation (translation: Vector3, result: Matrix4) { - return Matrix4.fromRotationTranslation(Matrix3.IDENTITY, translation, result); - } - - static fromScale (scale: Vector3, result: Matrix4) { - result._data[0] = scale.x; - result._data[1] = 0.0; - result._data[2] = 0.0; - result._data[3] = 0.0; - result._data[4] = 0.0; - result._data[5] = scale.y; - result._data[6] = 0.0; - result._data[7] = 0.0; - result._data[8] = 0.0; - result._data[9] = 0.0; - result._data[10] = scale.z; - result._data[11] = 0.0; - result._data[12] = 0.0; - result._data[13] = 0.0; - result._data[14] = 0.0; - result._data[15] = 1.0; - - return result; - } - - static fromUniformScale (scale: number, result: Matrix4) { - result._data[0] = scale; - result._data[1] = 0.0; - result._data[2] = 0.0; - result._data[3] = 0.0; - result._data[4] = 0.0; - result._data[5] = scale; - result._data[6] = 0.0; - result._data[7] = 0.0; - result._data[8] = 0.0; - result._data[9] = 0.0; - result._data[10] = scale; - result._data[11] = 0.0; - result._data[12] = 0.0; - result._data[13] = 0.0; - result._data[14] = 0.0; - result._data[15] = 1.0; - - return result; - } - - static fromRotation (rotation: Matrix3, result: Matrix4) { - result._data[0] = rotation.getData()[0]; - result._data[1] = rotation.getData()[1]; - result._data[2] = rotation.getData()[2]; - result._data[3] = 0.0; - - result._data[4] = rotation.getData()[3]; - result._data[5] = rotation.getData()[4]; - result._data[6] = rotation.getData()[5]; - result._data[7] = 0.0; - - result._data[8] = rotation.getData()[6]; - result._data[9] = rotation.getData()[7]; - result._data[10] = rotation.getData()[8]; - result._data[11] = 0.0; - - result._data[12] = 0.0; - result._data[13] = 0.0; - result._data[14] = 0.0; - result._data[15] = 1.0; - - return result; - } - - static fromCamera (camera: { position: Vector3, direction: Vector3, up: Vector3 }, result: Matrix4) { - const position = camera.position; - const direction = camera.direction; - const up = camera.up; - - Vector3.normalize(direction, fromCameraF); - Vector3.normalize( - Vector3.cross(fromCameraF, up, fromCameraR), - fromCameraR - ); - Vector3.normalize( - Vector3.cross(fromCameraR, fromCameraF, fromCameraU), - fromCameraU - ); - - const sX = fromCameraR.x; - const sY = fromCameraR.y; - const sZ = fromCameraR.z; - const fX = fromCameraF.x; - const fY = fromCameraF.y; - const fZ = fromCameraF.z; - const uX = fromCameraU.x; - const uY = fromCameraU.y; - const uZ = fromCameraU.z; - const positionX = position.x; - const positionY = position.y; - const positionZ = position.z; - const t0 = sX * -positionX + sY * -positionY + sZ * -positionZ; - const t1 = uX * -positionX + uY * -positionY + uZ * -positionZ; - const t2 = fX * positionX + fY * positionY + fZ * positionZ; - - result._data[0] = sX; - result._data[1] = uX; - result._data[2] = -fX; - result._data[3] = 0.0; - result._data[4] = sY; - result._data[5] = uY; - result._data[6] = -fY; - result._data[7] = 0.0; - result._data[8] = sZ; - result._data[9] = uZ; - result._data[10] = -fZ; - result._data[11] = 0.0; - result._data[12] = t0; - result._data[13] = t1; - result._data[14] = t2; - result._data[15] = 1.0; - - return result; - } - - static computePerspectiveFieldOfView ( - fov: number, - aspectRatio: number, - near: number, - far: number, - reverse: boolean, - result: Matrix4, - ) { - const invTanFov = 1.0 / Math.tan(fov * 0.5); - - const column0Row0 = reverse ? invTanFov : invTanFov / aspectRatio; - const column1Row1 = reverse ? invTanFov * aspectRatio : invTanFov; - const column2Row2 = (far + near) / (near - far); - const column3Row2 = (2.0 * far * near) / (near - far); - - result._data[0] = column0Row0; - result._data[1] = 0.0; - result._data[2] = 0.0; - result._data[3] = 0.0; - result._data[4] = 0.0; - result._data[5] = column1Row1; - result._data[6] = 0.0; - result._data[7] = 0.0; - result._data[8] = 0.0; - result._data[9] = 0.0; - result._data[10] = column2Row2; - result._data[11] = -1.0; - result._data[12] = 0.0; - result._data[13] = 0.0; - result._data[14] = column3Row2; - result._data[15] = 0.0; - - return result; - } - - static computeOrthographicOffCenter ( - left: number, - right: number, - bottom: number, - top: number, - near: number, - far: number, - result: Matrix4 - ) { - let a = 1.0 / (right - left); - let b = 1.0 / (top - bottom); - let c = 1.0 / (far - near); - - const tx = -(right + left) * a; - const ty = -(top + bottom) * b; - const tz = -(far + near) * c; - - a *= 2.0; - b *= 2.0; - c *= -2.0; - - result._data[0] = a; - result._data[1] = 0.0; - result._data[2] = 0.0; - result._data[3] = 0.0; - result._data[4] = 0.0; - result._data[5] = b; - result._data[6] = 0.0; - result._data[7] = 0.0; - result._data[8] = 0.0; - result._data[9] = 0.0; - result._data[10] = c; - result._data[11] = 0.0; - result._data[12] = tx; - result._data[13] = ty; - result._data[14] = tz; - result._data[15] = 1.0; - - return result; - } - - static computePerspectiveOffCenter ( - left: number, - right: number, - bottom: number, - top: number, - near: number, - far: number, - result: Matrix4 - ) { - const column0Row0 = (2.0 * near) / (right - left); - const column1Row1 = (2.0 * near) / (top - bottom); - const column2Row0 = (right + left) / (right - left); - const column2Row1 = (top + bottom) / (top - bottom); - const column2Row2 = -(far + near) / (far - near); - const column2Row3 = -1.0; - const column3Row2 = (-2.0 * far * near) / (far - near); - - result._data[0] = column0Row0; - result._data[1] = 0.0; - result._data[2] = 0.0; - result._data[3] = 0.0; - result._data[4] = 0.0; - result._data[5] = column1Row1; - result._data[6] = 0.0; - result._data[7] = 0.0; - result._data[8] = column2Row0; - result._data[9] = column2Row1; - result._data[10] = column2Row2; - result._data[11] = column2Row3; - result._data[12] = 0.0; - result._data[13] = 0.0; - result._data[14] = column3Row2; - result._data[15] = 0.0; - - return result; - } - - static computeInfinitePerspectiveOffCenter ( - left: number, - right: number, - bottom: number, - top: number, - near: number, - result: Matrix4 - ) { - const column0Row0 = (2.0 * near) / (right - left); - const column1Row1 = (2.0 * near) / (top - bottom); - const column2Row0 = (right + left) / (right - left); - const column2Row1 = (top + bottom) / (top - bottom); - const column2Row2 = -1.0; - const column2Row3 = -1.0; - const column3Row2 = -2.0 * near; - - result._data[0] = column0Row0; - result._data[1] = 0.0; - result._data[2] = 0.0; - result._data[3] = 0.0; - result._data[4] = 0.0; - result._data[5] = column1Row1; - result._data[6] = 0.0; - result._data[7] = 0.0; - result._data[8] = column2Row0; - result._data[9] = column2Row1; - result._data[10] = column2Row2; - result._data[11] = column2Row3; - result._data[12] = 0.0; - result._data[13] = 0.0; - result._data[14] = column3Row2; - result._data[15] = 0.0; - - return result; - } - - static computeViewportTransformation ( - viewport: { x: number, y: number, width: number, height: number }, - nearDepthRange: number, - farDepthRange: number, - result: Matrix4 - ) { - const x = (viewport.x ?? 0.0); - const y = (viewport.y ?? 0.0); - const width = (viewport.width ?? 0.0); - const height = (viewport.height ?? 0.0); - - nearDepthRange = (nearDepthRange ?? 0.0); - farDepthRange = (farDepthRange ?? 1.0); - - const halfWidth = width * 0.5; - const halfHeight = height * 0.5; - const halfDepth = (farDepthRange - nearDepthRange) * 0.5; - - const column0Row0 = halfWidth; - const column1Row1 = halfHeight; - const column2Row2 = halfDepth; - const column3Row0 = x + halfWidth; - const column3Row1 = y + halfHeight; - const column3Row2 = nearDepthRange + halfDepth; - const column3Row3 = 1.0; - - result._data[0] = column0Row0; - result._data[1] = 0.0; - result._data[2] = 0.0; - result._data[3] = 0.0; - result._data[4] = 0.0; - result._data[5] = column1Row1; - result._data[6] = 0.0; - result._data[7] = 0.0; - result._data[8] = 0.0; - result._data[9] = 0.0; - result._data[10] = column2Row2; - result._data[11] = 0.0; - result._data[12] = column3Row0; - result._data[13] = column3Row1; - result._data[14] = column3Row2; - result._data[15] = column3Row3; - - return result; - } - - static computeView (position: Vector3, direction: Vector3, up: Vector3, right: Vector3, result: Matrix4) { - result._data[0] = right.x; - result._data[1] = up.x; - result._data[2] = -direction.x; - result._data[3] = 0.0; - result._data[4] = right.y; - result._data[5] = up.y; - result._data[6] = -direction.y; - result._data[7] = 0.0; - result._data[8] = right.z; - result._data[9] = up.z; - result._data[10] = -direction.z; - result._data[11] = 0.0; - result._data[12] = -Vector3.dot(right, position); - result._data[13] = -Vector3.dot(up, position); - result._data[14] = Vector3.dot(direction, position); - result._data[15] = 1.0; - - return result; - } - - static toArray (matrix: Matrix4, result: Mat4DataType) { - result[0] = matrix._data[0]; - result[1] = matrix._data[1]; - result[2] = matrix._data[2]; - result[3] = matrix._data[3]; - result[4] = matrix._data[4]; - result[5] = matrix._data[5]; - result[6] = matrix._data[6]; - result[7] = matrix._data[7]; - result[8] = matrix._data[8]; - result[9] = matrix._data[9]; - result[10] = matrix._data[10]; - result[11] = matrix._data[11]; - result[12] = matrix._data[12]; - result[13] = matrix._data[13]; - result[14] = matrix._data[14]; - result[15] = matrix._data[15]; - - return result; - } - - static getElement (matrix: Matrix4, column: number, row: number) { - return matrix._data[column * 4 + row]; - } - - static getColumn (matrix: Matrix4, index: number, result: Vector4) { - const startIndex = index * 4; - const x = matrix._data[startIndex]; - const y = matrix._data[startIndex + 1]; - const z = matrix._data[startIndex + 2]; - const w = matrix._data[startIndex + 3]; - - result.x = x; - result.y = y; - result.z = z; - result.w = w; - - return result; - } - - static setColumn (matrix: Matrix4, index: number, cartesian: Vector4, result: Matrix4) { - result = Matrix4.copyTo(matrix, result); - - const startIndex = index * 4; - - result._data[startIndex] = cartesian.x; - result._data[startIndex + 1] = cartesian.y; - result._data[startIndex + 2] = cartesian.z; - result._data[startIndex + 3] = cartesian.w; - - return result; - } - - static getRow (matrix: Matrix4, index: number, result: Vector4) { - const x = matrix._data[index]; - const y = matrix._data[index + 4]; - const z = matrix._data[index + 8]; - const w = matrix._data[index + 12]; - - result.x = x; - result.y = y; - result.z = z; - result.w = w; - - return result; - } - - static setRow (matrix: Matrix4, index: number, cartesian: Vector4, result: Matrix4) { - result = Matrix4.copyTo(matrix, result); - result._data[index] = cartesian.x; - result._data[index + 4] = cartesian.y; - result._data[index + 8] = cartesian.z; - result._data[index + 12] = cartesian.w; - - return result; - } - - static setTranslation (matrix: Matrix4, translation: Vector3, result: Matrix4) { - result._data[0] = matrix._data[0]; - result._data[1] = matrix._data[1]; - result._data[2] = matrix._data[2]; - result._data[3] = matrix._data[3]; - - result._data[4] = matrix._data[4]; - result._data[5] = matrix._data[5]; - result._data[6] = matrix._data[6]; - result._data[7] = matrix._data[7]; - - result._data[8] = matrix._data[8]; - result._data[9] = matrix._data[9]; - result._data[10] = matrix._data[10]; - result._data[11] = matrix._data[11]; - - result._data[12] = translation.x; - result._data[13] = translation.y; - result._data[14] = translation.z; - result._data[15] = matrix._data[15]; - - return result; - } - - static scale (matrix: Matrix4, scale: Vector3, result: Matrix4) { - const existingScale = Matrix4.getScale(matrix, scaleScratch1); - const scaleRatioX = scale.x / existingScale.x; - const scaleRatioY = scale.y / existingScale.y; - const scaleRatioZ = scale.z / existingScale.y; - - result._data[0] = matrix._data[0] * scaleRatioX; - result._data[1] = matrix._data[1] * scaleRatioX; - result._data[2] = matrix._data[2] * scaleRatioX; - result._data[3] = matrix._data[3]; - - result._data[4] = matrix._data[4] * scaleRatioY; - result._data[5] = matrix._data[5] * scaleRatioY; - result._data[6] = matrix._data[6] * scaleRatioY; - result._data[7] = matrix._data[7]; - - result._data[8] = matrix._data[8] * scaleRatioZ; - result._data[9] = matrix._data[9] * scaleRatioZ; - result._data[10] = matrix._data[10] * scaleRatioZ; - result._data[11] = matrix._data[11]; - - result._data[12] = matrix._data[12]; - result._data[13] = matrix._data[13]; - result._data[14] = matrix._data[14]; - result._data[15] = matrix._data[15]; - - return result; - } - - static setUniformScale (matrix: Matrix4, scale: number, result: Matrix4) { - const existingScale = Matrix4.getScale(matrix, scaleScratch2); - const scaleRatioX = scale / existingScale.x; - const scaleRatioY = scale / existingScale.y; - const scaleRatioZ = scale / existingScale.z; - - result._data[0] = matrix._data[0] * scaleRatioX; - result._data[1] = matrix._data[1] * scaleRatioX; - result._data[2] = matrix._data[2] * scaleRatioX; - result._data[3] = matrix._data[3]; - - result._data[4] = matrix._data[4] * scaleRatioY; - result._data[5] = matrix._data[5] * scaleRatioY; - result._data[6] = matrix._data[6] * scaleRatioY; - result._data[7] = matrix._data[7]; - - result._data[8] = matrix._data[8] * scaleRatioZ; - result._data[9] = matrix._data[9] * scaleRatioZ; - result._data[10] = matrix._data[10] * scaleRatioZ; - result._data[11] = matrix._data[11]; - - result._data[12] = matrix._data[12]; - result._data[13] = matrix._data[13]; - result._data[14] = matrix._data[14]; - result._data[15] = matrix._data[15]; - - return result; - } - - static getScale (matrix: Matrix4, result: Vector3) { - result.x = Math.hypot(matrix._data[0], matrix._data[1], matrix._data[2]); - result.y = Math.hypot(matrix._data[4], matrix._data[5], matrix._data[6]); - result.z = Math.hypot(matrix._data[8], matrix._data[9], matrix._data[10]); - - return result; - } - - static getMaximumScale (matrix: Matrix4) { - Matrix4.getScale(matrix, scaleScratch3); - - return Vector3.maximumComponent(scaleScratch3); - } - - static setRotation (matrix: Matrix4, rotation: Matrix4, result: Matrix4) { - const scale = Matrix4.getScale(matrix, scaleScratch4); - - result._data[0] = rotation._data[0] * scale.x; - result._data[1] = rotation._data[1] * scale.x; - result._data[2] = rotation._data[2] * scale.x; - result._data[3] = matrix._data[3]; - - result._data[4] = rotation._data[3] * scale.y; - result._data[5] = rotation._data[4] * scale.y; - result._data[6] = rotation._data[5] * scale.y; - result._data[7] = matrix._data[7]; - - result._data[8] = rotation._data[6] * scale.z; - result._data[9] = rotation._data[7] * scale.z; - result._data[10] = rotation._data[8] * scale.z; - result._data[11] = matrix._data[11]; - - result._data[12] = matrix._data[12]; - result._data[13] = matrix._data[13]; - result._data[14] = matrix._data[14]; - result._data[15] = matrix._data[15]; - - return result; - } - - static getRotation (matrix: Matrix4, result: Matrix4) { - const scale = Matrix4.getScale(matrix, scaleScratch5); - - result._data[0] = matrix._data[0] / scale.x; - result._data[1] = matrix._data[1] / scale.x; - result._data[2] = matrix._data[2] / scale.x; - - result._data[3] = matrix._data[4] / scale.y; - result._data[4] = matrix._data[5] / scale.y; - result._data[5] = matrix._data[6] / scale.y; - - result._data[6] = matrix._data[8] / scale.z; - result._data[7] = matrix._data[9] / scale.z; - result._data[8] = matrix._data[10] / scale.z; - - return result; - } - - static getRotationMatrix3 (matrix: Matrix4, result: Matrix3) { - const scale = Matrix4.getScale(matrix, scaleScratch5); - - result.getData()[0] = matrix._data[0] / scale.x; - result.getData()[1] = matrix._data[1] / scale.x; - result.getData()[2] = matrix._data[2] / scale.x; - - result.getData()[3] = matrix._data[4] / scale.y; - result.getData()[4] = matrix._data[5] / scale.y; - result.getData()[5] = matrix._data[6] / scale.y; - - result.getData()[6] = matrix._data[8] / scale.z; - result.getData()[7] = matrix._data[9] / scale.z; - result.getData()[8] = matrix._data[10] / scale.z; - - return result; - } - - static getRotationQuaternion (matrix: Matrix4, result: Quaternion) { - Matrix4.getRotationMatrix3(matrix, scratchMat2Quat); - result = Quaternion.fromRotationMatrix(scratchMat2Quat, result); - - return result; - } - - static multiply (leftM: Matrix4, rightM: Matrix4, result: Matrix4) { - const left = leftM._data; - const right = rightM._data; - - const left0 = left[0]; - const left1 = left[1]; - const left2 = left[2]; - const left3 = left[3]; - const left4 = left[4]; - const left5 = left[5]; - const left6 = left[6]; - const left7 = left[7]; - const left8 = left[8]; - const left9 = left[9]; - const left10 = left[10]; - const left11 = left[11]; - const left12 = left[12]; - const left13 = left[13]; - const left14 = left[14]; - const left15 = left[15]; - - const right0 = right[0]; - const right1 = right[1]; - const right2 = right[2]; - const right3 = right[3]; - const right4 = right[4]; - const right5 = right[5]; - const right6 = right[6]; - const right7 = right[7]; - const right8 = right[8]; - const right9 = right[9]; - const right10 = right[10]; - const right11 = right[11]; - const right12 = right[12]; - const right13 = right[13]; - const right14 = right[14]; - const right15 = right[15]; - - const column0Row0 = - left0 * right0 + left4 * right1 + left8 * right2 + left12 * right3; - const column0Row1 = - left1 * right0 + left5 * right1 + left9 * right2 + left13 * right3; - const column0Row2 = - left2 * right0 + left6 * right1 + left10 * right2 + left14 * right3; - const column0Row3 = - left3 * right0 + left7 * right1 + left11 * right2 + left15 * right3; - - const column1Row0 = - left0 * right4 + left4 * right5 + left8 * right6 + left12 * right7; - const column1Row1 = - left1 * right4 + left5 * right5 + left9 * right6 + left13 * right7; - const column1Row2 = - left2 * right4 + left6 * right5 + left10 * right6 + left14 * right7; - const column1Row3 = - left3 * right4 + left7 * right5 + left11 * right6 + left15 * right7; - - const column2Row0 = - left0 * right8 + left4 * right9 + left8 * right10 + left12 * right11; - const column2Row1 = - left1 * right8 + left5 * right9 + left9 * right10 + left13 * right11; - const column2Row2 = - left2 * right8 + left6 * right9 + left10 * right10 + left14 * right11; - const column2Row3 = - left3 * right8 + left7 * right9 + left11 * right10 + left15 * right11; - - const column3Row0 = - left0 * right12 + left4 * right13 + left8 * right14 + left12 * right15; - const column3Row1 = - left1 * right12 + left5 * right13 + left9 * right14 + left13 * right15; - const column3Row2 = - left2 * right12 + left6 * right13 + left10 * right14 + left14 * right15; - const column3Row3 = - left3 * right12 + left7 * right13 + left11 * right14 + left15 * right15; - - result._data[0] = column0Row0; - result._data[1] = column0Row1; - result._data[2] = column0Row2; - result._data[3] = column0Row3; - result._data[4] = column1Row0; - result._data[5] = column1Row1; - result._data[6] = column1Row2; - result._data[7] = column1Row3; - result._data[8] = column2Row0; - result._data[9] = column2Row1; - result._data[10] = column2Row2; - result._data[11] = column2Row3; - result._data[12] = column3Row0; - result._data[13] = column3Row1; - result._data[14] = column3Row2; - result._data[15] = column3Row3; - - return result; - } - - static mulScalerAddMatrix (leftM: Matrix4, rightM: Matrix4, rightS: number, result: Matrix4) { - const left = leftM._data; - const right = rightM._data; - const target = result._data; - - for (let i = 0; i < 16; i++) { - target[i] = left[i] + right[i] * rightS; - } - - return result; - } - - static add (left: Matrix4, right: Matrix4, result: Matrix4) { - result._data[0] = left._data[0] + right._data[0]; - result._data[1] = left._data[1] + right._data[1]; - result._data[2] = left._data[2] + right._data[2]; - result._data[3] = left._data[3] + right._data[3]; - result._data[4] = left._data[4] + right._data[4]; - result._data[5] = left._data[5] + right._data[5]; - result._data[6] = left._data[6] + right._data[6]; - result._data[7] = left._data[7] + right._data[7]; - result._data[8] = left._data[8] + right._data[8]; - result._data[9] = left._data[9] + right._data[9]; - result._data[10] = left._data[10] + right._data[10]; - result._data[11] = left._data[11] + right._data[11]; - result._data[12] = left._data[12] + right._data[12]; - result._data[13] = left._data[13] + right._data[13]; - result._data[14] = left._data[14] + right._data[14]; - result._data[15] = left._data[15] + right._data[15]; - - return result; - } - - static subtract (left: Matrix4, right: Matrix4, result: Matrix4) { - result._data[0] = left._data[0] - right._data[0]; - result._data[1] = left._data[1] - right._data[1]; - result._data[2] = left._data[2] - right._data[2]; - result._data[3] = left._data[3] - right._data[3]; - result._data[4] = left._data[4] - right._data[4]; - result._data[5] = left._data[5] - right._data[5]; - result._data[6] = left._data[6] - right._data[6]; - result._data[7] = left._data[7] - right._data[7]; - result._data[8] = left._data[8] - right._data[8]; - result._data[9] = left._data[9] - right._data[9]; - result._data[10] = left._data[10] - right._data[10]; - result._data[11] = left._data[11] - right._data[11]; - result._data[12] = left._data[12] - right._data[12]; - result._data[13] = left._data[13] - right._data[13]; - result._data[14] = left._data[14] - right._data[14]; - result._data[15] = left._data[15] - right._data[15]; - - return result; - } - - static multiplyTransformation (leftM: Matrix4, rightM: Matrix4, result: Matrix4) { - const left = leftM._data; - const right = leftM._data; - - const left0 = left[0]; - const left1 = left[1]; - const left2 = left[2]; - const left4 = left[4]; - const left5 = left[5]; - const left6 = left[6]; - const left8 = left[8]; - const left9 = left[9]; - const left10 = left[10]; - const left12 = left[12]; - const left13 = left[13]; - const left14 = left[14]; - - const right0 = right[0]; - const right1 = right[1]; - const right2 = right[2]; - const right4 = right[4]; - const right5 = right[5]; - const right6 = right[6]; - const right8 = right[8]; - const right9 = right[9]; - const right10 = right[10]; - const right12 = right[12]; - const right13 = right[13]; - const right14 = right[14]; - - const column0Row0 = left0 * right0 + left4 * right1 + left8 * right2; - const column0Row1 = left1 * right0 + left5 * right1 + left9 * right2; - const column0Row2 = left2 * right0 + left6 * right1 + left10 * right2; - - const column1Row0 = left0 * right4 + left4 * right5 + left8 * right6; - const column1Row1 = left1 * right4 + left5 * right5 + left9 * right6; - const column1Row2 = left2 * right4 + left6 * right5 + left10 * right6; - - const column2Row0 = left0 * right8 + left4 * right9 + left8 * right10; - const column2Row1 = left1 * right8 + left5 * right9 + left9 * right10; - const column2Row2 = left2 * right8 + left6 * right9 + left10 * right10; - - const column3Row0 = - left0 * right12 + left4 * right13 + left8 * right14 + left12; - const column3Row1 = - left1 * right12 + left5 * right13 + left9 * right14 + left13; - const column3Row2 = - left2 * right12 + left6 * right13 + left10 * right14 + left14; - - result._data[0] = column0Row0; - result._data[1] = column0Row1; - result._data[2] = column0Row2; - result._data[3] = 0.0; - result._data[4] = column1Row0; - result._data[5] = column1Row1; - result._data[6] = column1Row2; - result._data[7] = 0.0; - result._data[8] = column2Row0; - result._data[9] = column2Row1; - result._data[10] = column2Row2; - result._data[11] = 0.0; - result._data[12] = column3Row0; - result._data[13] = column3Row1; - result._data[14] = column3Row2; - result._data[15] = 1.0; - - return result; - } - - static multiplyByMatrix3 (matrix: Matrix4, rotation: Matrix3, result: Matrix4) { - const left0 = matrix._data[0]; - const left1 = matrix._data[1]; - const left2 = matrix._data[2]; - const left4 = matrix._data[4]; - const left5 = matrix._data[5]; - const left6 = matrix._data[6]; - const left8 = matrix._data[8]; - const left9 = matrix._data[9]; - const left10 = matrix._data[10]; - - const right0 = rotation.getData()[0]; - const right1 = rotation.getData()[1]; - const right2 = rotation.getData()[2]; - const right4 = rotation.getData()[3]; - const right5 = rotation.getData()[4]; - const right6 = rotation.getData()[5]; - const right8 = rotation.getData()[6]; - const right9 = rotation.getData()[7]; - const right10 = rotation.getData()[8]; - - const column0Row0 = left0 * right0 + left4 * right1 + left8 * right2; - const column0Row1 = left1 * right0 + left5 * right1 + left9 * right2; - const column0Row2 = left2 * right0 + left6 * right1 + left10 * right2; - - const column1Row0 = left0 * right4 + left4 * right5 + left8 * right6; - const column1Row1 = left1 * right4 + left5 * right5 + left9 * right6; - const column1Row2 = left2 * right4 + left6 * right5 + left10 * right6; - - const column2Row0 = left0 * right8 + left4 * right9 + left8 * right10; - const column2Row1 = left1 * right8 + left5 * right9 + left9 * right10; - const column2Row2 = left2 * right8 + left6 * right9 + left10 * right10; - - result._data[0] = column0Row0; - result._data[1] = column0Row1; - result._data[2] = column0Row2; - result._data[3] = 0.0; - result._data[4] = column1Row0; - result._data[5] = column1Row1; - result._data[6] = column1Row2; - result._data[7] = 0.0; - result._data[8] = column2Row0; - result._data[9] = column2Row1; - result._data[10] = column2Row2; - result._data[11] = 0.0; - result._data[12] = matrix._data[12]; - result._data[13] = matrix._data[13]; - result._data[14] = matrix._data[14]; - result._data[15] = matrix._data[15]; - - return result; - } - - static multiplyByTranslation (matrix: Matrix4, translation: Vector3, result: Matrix4) { - const x = translation.x; - const y = translation.y; - const z = translation.z; - - const tx = x * matrix._data[0] + y * matrix._data[4] + z * matrix._data[8] + matrix._data[12]; - const ty = x * matrix._data[1] + y * matrix._data[5] + z * matrix._data[9] + matrix._data[13]; - const tz = x * matrix._data[2] + y * matrix._data[6] + z * matrix._data[10] + matrix._data[14]; - - result._data[0] = matrix._data[0]; - result._data[1] = matrix._data[1]; - result._data[2] = matrix._data[2]; - result._data[3] = matrix._data[3]; - result._data[4] = matrix._data[4]; - result._data[5] = matrix._data[5]; - result._data[6] = matrix._data[6]; - result._data[7] = matrix._data[7]; - result._data[8] = matrix._data[8]; - result._data[9] = matrix._data[9]; - result._data[10] = matrix._data[10]; - result._data[11] = matrix._data[11]; - result._data[12] = tx; - result._data[13] = ty; - result._data[14] = tz; - result._data[15] = matrix._data[15]; - - return result; - } - - static multiplyByScale (matrix: Matrix4, scale: Vector3, result: Matrix4) { - const scaleX = scale.x; - const scaleY = scale.y; - const scaleZ = scale.z; - - // Faster than Cartesian3.equals - if (scaleX === 1.0 && scaleY === 1.0 && scaleZ === 1.0) { - return Matrix4.copyTo(matrix, result); - } - - if (result === undefined) { - result = new Matrix4(); - } - - result._data[0] = scaleX * matrix._data[0]; - result._data[1] = scaleX * matrix._data[1]; - result._data[2] = scaleX * matrix._data[2]; - result._data[3] = matrix._data[3]; - - result._data[4] = scaleY * matrix._data[4]; - result._data[5] = scaleY * matrix._data[5]; - result._data[6] = scaleY * matrix._data[6]; - result._data[7] = matrix._data[7]; - - result._data[8] = scaleZ * matrix._data[8]; - result._data[9] = scaleZ * matrix._data[9]; - result._data[10] = scaleZ * matrix._data[10]; - result._data[11] = matrix._data[11]; - - result._data[12] = matrix._data[12]; - result._data[13] = matrix._data[13]; - result._data[14] = matrix._data[14]; - result._data[15] = matrix._data[15]; - - return result; - } - - static multiplyByUniformScale (matrix: Matrix4, scale: number, result: Matrix4) { - result._data[0] = matrix._data[0] * scale; - result._data[1] = matrix._data[1] * scale; - result._data[2] = matrix._data[2] * scale; - result._data[3] = matrix._data[3]; - - result._data[4] = matrix._data[4] * scale; - result._data[5] = matrix._data[5] * scale; - result._data[6] = matrix._data[6] * scale; - result._data[7] = matrix._data[7]; - - result._data[8] = matrix._data[8] * scale; - result._data[9] = matrix._data[9] * scale; - result._data[10] = matrix._data[10] * scale; - result._data[11] = matrix._data[11]; - - result._data[12] = matrix._data[12]; - result._data[13] = matrix._data[13]; - result._data[14] = matrix._data[14]; - result._data[15] = matrix._data[15]; - - return result; - } - - static multiplyByVector (matrix: Matrix4, cartesian: Vector4, result: Vector4) { - const vX = cartesian.x; - const vY = cartesian.y; - const vZ = cartesian.z; - const vW = cartesian.w; - - const x = matrix._data[0] * vX + matrix._data[4] * vY + matrix._data[8] * vZ + matrix._data[12] * vW; - const y = matrix._data[1] * vX + matrix._data[5] * vY + matrix._data[9] * vZ + matrix._data[13] * vW; - const z = matrix._data[2] * vX + matrix._data[6] * vY + matrix._data[10] * vZ + matrix._data[14] * vW; - const w = matrix._data[3] * vX + matrix._data[7] * vY + matrix._data[11] * vZ + matrix._data[15] * vW; - - result.x = x; - result.y = y; - result.z = z; - result.w = w; - - return result; - } - - static multiplyByPointAsVector (matrix: Matrix4, cartesian: Vector3, result: Vector3) { - const vX = cartesian.x; - const vY = cartesian.y; - const vZ = cartesian.z; - - const x = matrix._data[0] * vX + matrix._data[4] * vY + matrix._data[8] * vZ; - const y = matrix._data[1] * vX + matrix._data[5] * vY + matrix._data[9] * vZ; - const z = matrix._data[2] * vX + matrix._data[6] * vY + matrix._data[10] * vZ; - - result.x = x; - result.y = y; - result.z = z; - - return result; - } - - /** - * 矩阵与三维点/向量相乘,三维点/向量第四个分量 W 为 1 处理 - * @param matrix - * @param cartesian - * @param result - * @returns - */ - static multiplyByPoint (matrix: Matrix4, cartesian: Vector3, result: Vector3) { - const vX = cartesian.x; - const vY = cartesian.y; - const vZ = cartesian.z; - - const x = matrix._data[0] * vX + matrix._data[4] * vY + matrix._data[8] * vZ + matrix._data[12]; - const y = matrix._data[1] * vX + matrix._data[5] * vY + matrix._data[9] * vZ + matrix._data[13]; - const z = matrix._data[2] * vX + matrix._data[6] * vY + matrix._data[10] * vZ + matrix._data[14]; - - result.x = x; - result.y = y; - result.z = z; - - return result; - } - - static multiplyByScalar (matrix: Matrix4, scalar: number, result: Matrix4) { - result._data[0] = matrix._data[0] * scalar; - result._data[1] = matrix._data[1] * scalar; - result._data[2] = matrix._data[2] * scalar; - result._data[3] = matrix._data[3] * scalar; - result._data[4] = matrix._data[4] * scalar; - result._data[5] = matrix._data[5] * scalar; - result._data[6] = matrix._data[6] * scalar; - result._data[7] = matrix._data[7] * scalar; - result._data[8] = matrix._data[8] * scalar; - result._data[9] = matrix._data[9] * scalar; - result._data[10] = matrix._data[10] * scalar; - result._data[11] = matrix._data[11] * scalar; - result._data[12] = matrix._data[12] * scalar; - result._data[13] = matrix._data[13] * scalar; - result._data[14] = matrix._data[14] * scalar; - result._data[15] = matrix._data[15] * scalar; - - return result; - } - - static negate (matrix: Matrix4, result: Matrix4) { - result._data[0] = -matrix._data[0]; - result._data[1] = -matrix._data[1]; - result._data[2] = -matrix._data[2]; - result._data[3] = -matrix._data[3]; - result._data[4] = -matrix._data[4]; - result._data[5] = -matrix._data[5]; - result._data[6] = -matrix._data[6]; - result._data[7] = -matrix._data[7]; - result._data[8] = -matrix._data[8]; - result._data[9] = -matrix._data[9]; - result._data[10] = -matrix._data[10]; - result._data[11] = -matrix._data[11]; - result._data[12] = -matrix._data[12]; - result._data[13] = -matrix._data[13]; - result._data[14] = -matrix._data[14]; - result._data[15] = -matrix._data[15]; - - return result; - } - - static transpose (matrix: Matrix4, result: Matrix4) { - const matrix1 = matrix._data[1]; - const matrix2 = matrix._data[2]; - const matrix3 = matrix._data[3]; - const matrix6 = matrix._data[6]; - const matrix7 = matrix._data[7]; - const matrix11 = matrix._data[11]; - - result._data[0] = matrix._data[0]; - result._data[1] = matrix._data[4]; - result._data[2] = matrix._data[8]; - result._data[3] = matrix._data[12]; - result._data[4] = matrix1; - result._data[5] = matrix._data[5]; - result._data[6] = matrix._data[9]; - result._data[7] = matrix._data[13]; - result._data[8] = matrix2; - result._data[9] = matrix6; - result._data[10] = matrix._data[10]; - result._data[11] = matrix._data[14]; - result._data[12] = matrix3; - result._data[13] = matrix7; - result._data[14] = matrix11; - result._data[15] = matrix._data[15]; - - return result; - } - - static abs (matrix: Matrix4, result: Matrix4) { - result._data[0] = Math.abs(matrix._data[0]); - result._data[1] = Math.abs(matrix._data[1]); - result._data[2] = Math.abs(matrix._data[2]); - result._data[3] = Math.abs(matrix._data[3]); - result._data[4] = Math.abs(matrix._data[4]); - result._data[5] = Math.abs(matrix._data[5]); - result._data[6] = Math.abs(matrix._data[6]); - result._data[7] = Math.abs(matrix._data[7]); - result._data[8] = Math.abs(matrix._data[8]); - result._data[9] = Math.abs(matrix._data[9]); - result._data[10] = Math.abs(matrix._data[10]); - result._data[11] = Math.abs(matrix._data[11]); - result._data[12] = Math.abs(matrix._data[12]); - result._data[13] = Math.abs(matrix._data[13]); - result._data[14] = Math.abs(matrix._data[14]); - result._data[15] = Math.abs(matrix._data[15]); - - return result; - } - - static equals (left: Matrix4, right: Matrix4) { - // Given that most matrices will be transformation matrices, the elements - // are tested in order such that the test is likely to fail as early - // as possible. I _think_ this is just as friendly to the L1 cache - // as testing in index order. It is certainty faster in practice. - return ( - left === right || - ( - // Translation - left._data[12] === right._data[12] && - left._data[13] === right._data[13] && - left._data[14] === right._data[14] && - // Rotation/scale - left._data[0] === right._data[0] && - left._data[1] === right._data[1] && - left._data[2] === right._data[2] && - left._data[4] === right._data[4] && - left._data[5] === right._data[5] && - left._data[6] === right._data[6] && - left._data[8] === right._data[8] && - left._data[9] === right._data[9] && - left._data[10] === right._data[10] && - // Bottom row - left._data[3] === right._data[3] && - left._data[7] === right._data[7] && - left._data[11] === right._data[11] && - left._data[15] === right._data[15]) - ); - } - - static equalsEpsilon (left: Matrix4, right: Matrix4, epsilon: number) { - return ( - left === right || - ( - Math.abs(left._data[0] - right._data[0]) <= epsilon && - Math.abs(left._data[1] - right._data[1]) <= epsilon && - Math.abs(left._data[2] - right._data[2]) <= epsilon && - Math.abs(left._data[3] - right._data[3]) <= epsilon && - Math.abs(left._data[4] - right._data[4]) <= epsilon && - Math.abs(left._data[5] - right._data[5]) <= epsilon && - Math.abs(left._data[6] - right._data[6]) <= epsilon && - Math.abs(left._data[7] - right._data[7]) <= epsilon && - Math.abs(left._data[8] - right._data[8]) <= epsilon && - Math.abs(left._data[9] - right._data[9]) <= epsilon && - Math.abs(left._data[10] - right._data[10]) <= epsilon && - Math.abs(left._data[11] - right._data[11]) <= epsilon && - Math.abs(left._data[12] - right._data[12]) <= epsilon && - Math.abs(left._data[13] - right._data[13]) <= epsilon && - Math.abs(left._data[14] - right._data[14]) <= epsilon && - Math.abs(left._data[15] - right._data[15]) <= epsilon) - ); - } - - static getTranslation (matrix: Matrix4, result: Vector3) { - result.x = matrix._data[12]; - result.y = matrix._data[13]; - result.z = matrix._data[14]; - - return result; - } - - static getMatrix3 (matrix: Matrix4, result: Matrix3) { - result.getData()[0] = matrix._data[0]; - result.getData()[1] = matrix._data[1]; - result.getData()[2] = matrix._data[2]; - result.getData()[3] = matrix._data[4]; - result.getData()[4] = matrix._data[5]; - result.getData()[5] = matrix._data[6]; - result.getData()[6] = matrix._data[8]; - result.getData()[7] = matrix._data[9]; - result.getData()[8] = matrix._data[10]; - - return result; - } - - static inverse (matrix: Matrix4, result: Matrix4) { - // - // Ported from: - // ftp://download.intel.com/design/PentiumIII/sml/24504301.pdf - // - const src0 = matrix._data[0]; - const src1 = matrix._data[4]; - const src2 = matrix._data[8]; - const src3 = matrix._data[12]; - const src4 = matrix._data[1]; - const src5 = matrix._data[5]; - const src6 = matrix._data[9]; - const src7 = matrix._data[13]; - const src8 = matrix._data[2]; - const src9 = matrix._data[6]; - const src10 = matrix._data[10]; - const src11 = matrix._data[14]; - const src12 = matrix._data[3]; - const src13 = matrix._data[7]; - const src14 = matrix._data[11]; - const src15 = matrix._data[15]; - - // calculate pairs for first 8 elements (cofactors) - let tmp0 = src10 * src15; - let tmp1 = src11 * src14; - let tmp2 = src9 * src15; - let tmp3 = src11 * src13; - let tmp4 = src9 * src14; - let tmp5 = src10 * src13; - let tmp6 = src8 * src15; - let tmp7 = src11 * src12; - let tmp8 = src8 * src14; - let tmp9 = src10 * src12; - let tmp10 = src8 * src13; - let tmp11 = src9 * src12; - - // calculate first 8 elements (cofactors) - const dst0 = - tmp0 * src5 + - tmp3 * src6 + - tmp4 * src7 - - (tmp1 * src5 + tmp2 * src6 + tmp5 * src7); - const dst1 = - tmp1 * src4 + - tmp6 * src6 + - tmp9 * src7 - - (tmp0 * src4 + tmp7 * src6 + tmp8 * src7); - const dst2 = - tmp2 * src4 + - tmp7 * src5 + - tmp10 * src7 - - (tmp3 * src4 + tmp6 * src5 + tmp11 * src7); - const dst3 = - tmp5 * src4 + - tmp8 * src5 + - tmp11 * src6 - - (tmp4 * src4 + tmp9 * src5 + tmp10 * src6); - const dst4 = - tmp1 * src1 + - tmp2 * src2 + - tmp5 * src3 - - (tmp0 * src1 + tmp3 * src2 + tmp4 * src3); - const dst5 = - tmp0 * src0 + - tmp7 * src2 + - tmp8 * src3 - - (tmp1 * src0 + tmp6 * src2 + tmp9 * src3); - const dst6 = - tmp3 * src0 + - tmp6 * src1 + - tmp11 * src3 - - (tmp2 * src0 + tmp7 * src1 + tmp10 * src3); - const dst7 = - tmp4 * src0 + - tmp9 * src1 + - tmp10 * src2 - - (tmp5 * src0 + tmp8 * src1 + tmp11 * src2); - - // calculate pairs for second 8 elements (cofactors) - tmp0 = src2 * src7; - tmp1 = src3 * src6; - tmp2 = src1 * src7; - tmp3 = src3 * src5; - tmp4 = src1 * src6; - tmp5 = src2 * src5; - tmp6 = src0 * src7; - tmp7 = src3 * src4; - tmp8 = src0 * src6; - tmp9 = src2 * src4; - tmp10 = src0 * src5; - tmp11 = src1 * src4; - - // calculate second 8 elements (cofactors) - const dst8 = - tmp0 * src13 + - tmp3 * src14 + - tmp4 * src15 - - (tmp1 * src13 + tmp2 * src14 + tmp5 * src15); - const dst9 = - tmp1 * src12 + - tmp6 * src14 + - tmp9 * src15 - - (tmp0 * src12 + tmp7 * src14 + tmp8 * src15); - const dst10 = - tmp2 * src12 + - tmp7 * src13 + - tmp10 * src15 - - (tmp3 * src12 + tmp6 * src13 + tmp11 * src15); - const dst11 = - tmp5 * src12 + - tmp8 * src13 + - tmp11 * src14 - - (tmp4 * src12 + tmp9 * src13 + tmp10 * src14); - const dst12 = - tmp2 * src10 + - tmp5 * src11 + - tmp1 * src9 - - (tmp4 * src11 + tmp0 * src9 + tmp3 * src10); - const dst13 = - tmp8 * src11 + - tmp0 * src8 + - tmp7 * src10 - - (tmp6 * src10 + tmp9 * src11 + tmp1 * src8); - const dst14 = - tmp6 * src9 + - tmp11 * src11 + - tmp3 * src8 - - (tmp10 * src11 + tmp2 * src8 + tmp7 * src9); - const dst15 = - tmp10 * src10 + - tmp4 * src8 + - tmp9 * src9 - - (tmp8 * src9 + tmp11 * src10 + tmp5 * src8); - - // calculate determinant - let det = src0 * dst0 + src1 * dst1 + src2 * dst2 + src3 * dst3; - - if (Math.abs(det) < MathUtils.EPSILON21) { - // Special case for a zero scale matrix that can occur, for example, - // when a model's node has a [0, 0, 0] scale. - if ( - Matrix3.equalsEpsilon( - Matrix4.getMatrix3(matrix, scratchInverseRotation), - scratchMatrix3Zero, - MathUtils.EPSILON7 - ) && - Vector4.equals( - Matrix4.getRow(matrix, 3, scratchBottomRow), - scratchExpectedBottomRow - ) - ) { - result._data[0] = 0.0; - result._data[1] = 0.0; - result._data[2] = 0.0; - result._data[3] = 0.0; - result._data[4] = 0.0; - result._data[5] = 0.0; - result._data[6] = 0.0; - result._data[7] = 0.0; - result._data[8] = 0.0; - result._data[9] = 0.0; - result._data[10] = 0.0; - result._data[11] = 0.0; - result._data[12] = -matrix._data[12]; - result._data[13] = -matrix._data[13]; - result._data[14] = -matrix._data[14]; - result._data[15] = 1.0; - - return result; - } - - throw 'matrix is not invertible because its determinate is zero'; - } - - // calculate matrix inverse - det = 1.0 / det; - - result._data[0] = dst0 * det; - result._data[1] = dst1 * det; - result._data[2] = dst2 * det; - result._data[3] = dst3 * det; - result._data[4] = dst4 * det; - result._data[5] = dst5 * det; - result._data[6] = dst6 * det; - result._data[7] = dst7 * det; - result._data[8] = dst8 * det; - result._data[9] = dst9 * det; - result._data[10] = dst10 * det; - result._data[11] = dst11 * det; - result._data[12] = dst12 * det; - result._data[13] = dst13 * det; - result._data[14] = dst14 * det; - result._data[15] = dst15 * det; - - return result; - } - - static inverseTransformation (matrix: Matrix4, result: Matrix4) { - //This function is an optimized version of the below 4 lines. - //const rT = Matrix3.transpose(Matrix4.getMatrix3(matrix)); - //const rTN = Matrix3.negate(rT); - //const rTT = Matrix3.multiplyByVector(rTN, Matrix4.getTranslation(matrix)); - //return Matrix4.fromRotationTranslation(rT, rTT, result); - const matrix0 = matrix._data[0]; - const matrix1 = matrix._data[1]; - const matrix2 = matrix._data[2]; - const matrix4 = matrix._data[4]; - const matrix5 = matrix._data[5]; - const matrix6 = matrix._data[6]; - const matrix8 = matrix._data[8]; - const matrix9 = matrix._data[9]; - const matrix10 = matrix._data[10]; - - const vX = matrix._data[12]; - const vY = matrix._data[13]; - const vZ = matrix._data[14]; - - const x = -matrix0 * vX - matrix1 * vY - matrix2 * vZ; - const y = -matrix4 * vX - matrix5 * vY - matrix6 * vZ; - const z = -matrix8 * vX - matrix9 * vY - matrix10 * vZ; - - result._data[0] = matrix0; - result._data[1] = matrix4; - result._data[2] = matrix8; - result._data[3] = 0.0; - result._data[4] = matrix1; - result._data[5] = matrix5; - result._data[6] = matrix9; - result._data[7] = 0.0; - result._data[8] = matrix2; - result._data[9] = matrix6; - result._data[10] = matrix10; - result._data[11] = 0.0; - result._data[12] = x; - result._data[13] = y; - result._data[14] = z; - result._data[15] = 1.0; - - return result; - } - - static inverseTranspose (matrix: Matrix4, result: Matrix4) { - return Matrix4.inverse( - Matrix4.transpose(matrix, scratchTransposeMatrix), - result - ); - } - - static equalsArray (matrix: Matrix4, array: Mat4DataType, offset?: number) { - offset = offset ?? 0; - - return ( - matrix._data[0] === array[offset] && - matrix._data[1] === array[offset + 1] && - matrix._data[2] === array[offset + 2] && - matrix._data[3] === array[offset + 3] && - matrix._data[4] === array[offset + 4] && - matrix._data[5] === array[offset + 5] && - matrix._data[6] === array[offset + 6] && - matrix._data[7] === array[offset + 7] && - matrix._data[8] === array[offset + 8] && - matrix._data[9] === array[offset + 9] && - matrix._data[10] === array[offset + 10] && - matrix._data[11] === array[offset + 11] && - matrix._data[12] === array[offset + 12] && - matrix._data[13] === array[offset + 13] && - matrix._data[14] === array[offset + 14] && - matrix._data[15] === array[offset + 15] - ); - } - - determinant (): number { - const te = this._data; - - const n11 = te[0]; const n12 = te[4]; const n13 = te[8]; const n14 = te[12]; - const n21 = te[1]; const n22 = te[5]; const n23 = te[9]; const n24 = te[13]; - const n31 = te[2]; const n32 = te[6]; const n33 = te[10]; const n34 = te[14]; - const n41 = te[3]; const n42 = te[7]; const n43 = te[11]; const n44 = te[15]; - - return ( - n41 * ( - + n14 * n23 * n32 - - n13 * n24 * n32 - - n14 * n22 * n33 - + n12 * n24 * n33 - + n13 * n22 * n34 - - n12 * n23 * n34 - ) + - n42 * ( - + n11 * n23 * n34 - - n11 * n24 * n33 - + n14 * n21 * n33 - - n13 * n21 * n34 - + n13 * n24 * n31 - - n14 * n23 * n31 - ) + - n43 * ( - + n11 * n24 * n32 - - n11 * n22 * n34 - - n14 * n21 * n32 - + n12 * n21 * n34 - + n14 * n22 * n31 - - n12 * n24 * n31 - ) + - n44 * ( - - n13 * n22 * n31 - - n11 * n23 * n32 - + n11 * n22 * n33 - + n13 * n21 * n32 - - n12 * n21 * n33 - + n12 * n23 * n31 - ) - ); - } - - /** - * 分解矩阵,将矩阵分解为平移、旋转、缩放三部分 - * @param m - 待分解的矩阵 - * @returns - */ - static decompose (m: Matrix4): { translation: Vector3, rotation: Quaternion, scale: Vector3 } { - const trans = Matrix4.getTranslation(m, new Vector3()); - const scale = Matrix4.getScale(m, new Vector3()); - // if determine is negative, we need to invert one scale - const det = m.determinant(); - - if (det < 0) { scale.x = - scale.x; } - - Matrix4.copyTo(m, _m1); - - const invSX = 1 / scale.x; - const invSY = 1 / scale.y; - const invSZ = 1 / scale.z; - - _m1._data[0] *= invSX; - _m1._data[1] *= invSX; - _m1._data[2] *= invSX; - - _m1._data[4] *= invSY; - _m1._data[5] *= invSY; - _m1._data[6] *= invSY; - - _m1._data[8] *= invSZ; - _m1._data[9] *= invSZ; - _m1._data[10] *= invSZ; - - const rotation = Matrix4.getRotationQuaternion(_m1, new Quaternion()); - - return { - translation: trans, - rotation: rotation, - scale: scale, - }; - } - - /** - * 从平移、旋转、缩放合成一个新变换矩阵 - * @param translation - 平移向量 - * @param rotation - 旋转量,使用四元数表示 - * @param scale - 缩放向量 - * @returns - */ - static compose (translation: Vector3, rotation: Quaternion, scale: Vector3, result: Matrix4) { - result = Matrix4.fromTranslationQuaternionRotationScale(translation, rotation, scale, result); - - return result; - } - - /** - * 计算透视投影矩阵 - * @param fovY - * @param aspect - * @param near - * @param far - * @returns - */ - static computePerspective (fovY: number, aspect: number, near: number, far: number, reverse: boolean, result: Matrix4) { - result = Matrix4.computePerspectiveFieldOfView(fovY, aspect, near, far, reverse, result); - - return result; - } - - /** - * 计算正交投影矩阵 - * @param left - * @param right - * @param bottom - * @param top - * @param near - * @param far - * @param result - * @returns - */ - static computeOrthographic (left: number, right: number, bottom: number, top: number, near: number, far: number, result: Matrix4) { - result = Matrix4.computeOrthographicOffCenter(left, right, bottom, top, near, far, result); - - return result; - } - - /** - * 通过 LookAt 参数计算相机的视图矩阵 - * @param position - * @param target - * @param up - * @param result - * @returns - */ - static computeLookAt (position: Vector3, target: Vector3, up: Vector3, result: Matrix4) { - - const direction = Vector3.subtract(target, position, new Vector3()); - - result = Matrix4.fromCamera({ - position: position, - direction: direction, - up: up, - }, result); - - return result; - } - - /** - * 使用矩阵内部数据构造一个新矩阵 - * @returns - */ - clone () { - const result = Matrix4.clone(this); - - return result; - } - - /** - * 将矩阵数据拷贝到 result 中 - * @param result - * @returns - */ - copyTo (result: Matrix4) { - result = Matrix4.copyTo(this, result); - - return result; - } - - /** - * 将矩阵数据从 source 拷贝回来 - * @param source - * @returns - */ - copyFrom (source: Matrix4) { - Matrix4.copyTo(source, this); - - return this; - } - - /** - * 平移矩阵,相当于该矩阵与使用 vector 构造的平移矩阵相乘 - * @param vector - * @returns 🎯计算结果将保存在调用矩阵中,并作为函数返回值返回。 - */ - translate (vector: Vector3) { - const result = Matrix4.multiplyByTranslation(this, vector, this); - - return result; - } - - /** - * 使用 vector 缩放矩阵,相当于该矩阵与使用 vector 构造的缩放矩阵相乘。 - * @param vector - * @returns 🎯计算结果将保存在调用矩阵中,并作为函数返回值返回。 - */ - scale (vector: Vector3) { - const result = Matrix4.scale(this, vector, this); - - return result; - } - - /** - * 计算矩阵与输入矩阵相乘的结果。 - * @param matrix - * @returns 🎯计算结果将保存在调用矩阵中,并作为函数返回值返回。 - */ - multiply (matrix: Matrix4): Matrix4 { - const result = Matrix4.multiply(this, matrix, this); - - return result; - } - - /** - * 将矩阵数据按照列主序导出为 number[] 对象。 - * @returns - */ - toArray (): number[] { - const array = new Array(16); - const result = Matrix4.toArray(this, array) as number[]; - - return result; - } - - /** - * 将矩阵按照列主序方式导出为 Float32Array 对象。 - * @returns - */ - toFloat32Array (): Float32Array { - const result = Matrix4.pack(this, new Float32Array(16), 0) as Float32Array; - - return result; - } - - /** - * 矩阵与四维向量相乘。 - * @param vector - * @returns - */ - multiplyByVector4 (vector: Vector4) { - const result = Matrix4.multiplyByVector(this, vector, new Vector4()); - - return result; - } - - /** - * 矩阵与三维向量相乘,内部将三维向量第四个分量当作 1 来处理。 - * @param vector - * @returns - */ - multiplyByVector3 (vector: Vector3) { - const result = Matrix4.multiplyByPoint(this, vector, new Vector3()); - - return result; - } - - /** - * 返回矩阵中指定索引位置数据 - * @param index - * @returns - */ - at (index: number) { - return this._data[index]; - } - - /** - * 矩阵乘一个旋转矩阵,矩阵乘一个绕 axis 轴旋转 angle 角度的旋转矩阵 - * @param angle - * @param axis - * @returns - */ - rotate (angle: number, axis: Vector3): Matrix4 | undefined { - let x = axis.x; - let y = axis.y; - let z = axis.z; - - let length = Math.sqrt(x * x + y * y + z * z); - - if (!length) { - return undefined; - } - - if (length !== 1) { - length = 1 / length; - x *= length; - y *= length; - z *= length; - } - - const s = Math.sin(angle); - const c = Math.cos(angle); - - const t = 1.0 - c; - - const a00 = this._data[0]; - const a01 = this._data[1]; - const a02 = this._data[2]; - const a03 = this._data[3]; - - const a10 = this._data[4]; - const a11 = this._data[5]; - const a12 = this._data[6]; - const a13 = this._data[7]; - - const a20 = this._data[8]; - const a21 = this._data[9]; - const a22 = this._data[10]; - const a23 = this._data[11]; - - const b00 = x * x * t + c; - const b01 = y * x * t + z * s; - const b02 = z * x * t - y * s; - - const b10 = x * y * t - z * s; - const b11 = y * y * t + c; - const b12 = z * y * t + x * s; - - const b20 = x * z * t + y * s; - const b21 = y * z * t - x * s; - const b22 = z * z * t + c; - - this._data[0] = a00 * b00 + a10 * b01 + a20 * b02; - this._data[1] = a01 * b00 + a11 * b01 + a21 * b02; - this._data[2] = a02 * b00 + a12 * b01 + a22 * b02; - this._data[3] = a03 * b00 + a13 * b01 + a23 * b02; - - this._data[4] = a00 * b10 + a10 * b11 + a20 * b12; - this._data[5] = a01 * b10 + a11 * b11 + a21 * b12; - this._data[6] = a02 * b10 + a12 * b11 + a22 * b12; - this._data[7] = a03 * b10 + a13 * b11 + a23 * b12; - - this._data[8] = a00 * b20 + a10 * b21 + a20 * b22; - this._data[9] = a01 * b20 + a11 * b21 + a21 * b22; - this._data[10] = a02 * b20 + a12 * b21 + a22 * b22; - this._data[11] = a03 * b20 + a13 * b21 + a23 * b22; - - return this; - } - - /** - * 计算矩阵的逆矩阵,会修改矩阵数据 - */ - inverse () { - const result = Matrix4.inverse(this, this); - - return result; - } - - /** - * 计算矩阵的转置,结果保存在原矩阵中(会修改矩阵数据) - * @returns - */ - transpose () { - const result = Matrix4.transpose(this, this); - - return result; - } - - /** - * 4x4 单位矩阵 - */ - static IDENTITY = Object.freeze( - new Matrix4( - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0 - ) - ); - - /** - * 数据都为 0 的矩阵 - */ - static ZERO = Object.freeze( - new Matrix4( - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ) - ); -} - -const fromCameraF = new Vector3(); -const fromCameraR = new Vector3(); -const fromCameraU = new Vector3(); - -const scaleScratch1 = new Vector3(); - -const scaleScratch2 = new Vector3(); - -const scratchColumn = new Vector3(); - -const scaleScratch3 = new Vector3(); - -const scaleScratch4 = new Vector3(); - -const scaleScratch5 = new Vector3(); - -const scratchInverseRotation = new Matrix3(); -const scratchMatrix3Zero = new Matrix3(); -const scratchBottomRow = new Vector4(); -const scratchExpectedBottomRow = new Vector4(0.0, 0.0, 0.0, 1.0); -const scratchTransposeMatrix = new Matrix4(); - -const scratchMat2Quat = new Matrix3(); - -const _m1 = new Matrix4(); - -export { Matrix4 }; - diff --git a/plugin-packages/model/src/math/quaternion.ts b/plugin-packages/model/src/math/quaternion.ts deleted file mode 100644 index 108761a1d..000000000 --- a/plugin-packages/model/src/math/quaternion.ts +++ /dev/null @@ -1,824 +0,0 @@ -import { Matrix4 } from './matrix4'; -import { Matrix3 } from './matrix3'; -import type { Vec4DataType } from './type'; -import { Vector3 } from './vector3'; -import type { Euler } from './euler'; -import { EulerOrder } from './euler'; -import { MathUtils } from './utilities'; - -/** - * 使用四元数表示一个旋转 - */ -class Quaternion { - private _data: Float32Array; - - constructor (x?: number, y?: number, z?: number, w?: number) { - this._data = new Float32Array([x ?? 0.0, y ?? 0.0, z ?? 0.0, w ?? 0.0]); - } - - get x (): number { - return this._data[0]; - } - set x (value: number) { - this._data[0] = value; - } - - getX (): number { - return this._data[0]; - } - - setX (value: number) { - this._data[0] = value; - } - - get y (): number { - return this._data[1]; - } - set y (value: number) { - this._data[1] = value; - } - - getY (): number { - return this._data[1]; - } - - setY (value: number) { - this._data[1] = value; - } - - get z (): number { - return this._data[2]; - } - set z (value: number) { - this._data[2] = value; - } - - getZ (): number { - return this._data[2]; - } - - setZ (value: number) { - this._data[2] = value; - } - - get w () { - return this._data[3]; - } - set w (value: number) { - this._data[3] = value; - } - - getW () { - return this._data[3]; - } - - setW (value: number) { - this._data[3] = value; - } - - /** - * 构造一个四元素表示绕 axis 轴旋转 angle 角度 - * @param axis - * @param angle - * @param result - * @returns - */ - static fromAxisAngle (axis: Vector3 | Readonly, angle: number, result: Quaternion) { - const halfAngle = angle / 2.0; - const s = Math.sin(halfAngle); - - fromAxisAngleScratch = Vector3.normalize(axis, fromAxisAngleScratch); - - const x = fromAxisAngleScratch.x * s; - const y = fromAxisAngleScratch.y * s; - const z = fromAxisAngleScratch.z * s; - const w = Math.cos(halfAngle); - - result.x = x; - result.y = y; - result.z = z; - result.w = w; - - return result; - } - - /** - * 从旋转矩阵构造一个四元数 - * @param matrix - * @param result - * @returns - */ - static fromRotationMatrix (matrix: Matrix3, result: Quaternion) { - let root; - let x; - let y; - let z; - let w; - - const m00 = matrix.getData()[Matrix3.COLUMN0ROW0]; - const m11 = matrix.getData()[Matrix3.COLUMN1ROW1]; - const m22 = matrix.getData()[Matrix3.COLUMN2ROW2]; - const trace = m00 + m11 + m22; - - if (trace > 0.0) { - // |w| > 1/2, may as well choose w > 1/2 - root = Math.sqrt(trace + 1.0); // 2w - w = 0.5 * root; - root = 0.5 / root; // 1/(4w) - - x = (matrix.getData()[Matrix3.COLUMN1ROW2] - matrix.getData()[Matrix3.COLUMN2ROW1]) * root; - y = (matrix.getData()[Matrix3.COLUMN2ROW0] - matrix.getData()[Matrix3.COLUMN0ROW2]) * root; - z = (matrix.getData()[Matrix3.COLUMN0ROW1] - matrix.getData()[Matrix3.COLUMN1ROW0]) * root; - } else { - // |w| <= 1/2 - const next = fromRotationMatrixNext; - - let i = 0; - - if (m11 > m00) { - i = 1; - } - if (m22 > m00 && m22 > m11) { - i = 2; - } - const j = next[i]; - const k = next[j]; - - root = Math.sqrt( - matrix.getData()[Matrix3.getElementIndex(i, i)] - - matrix.getData()[Matrix3.getElementIndex(j, j)] - - matrix.getData()[Matrix3.getElementIndex(k, k)] + - 1.0 - ); - - const quat = fromRotationMatrixQuat; - - quat[i] = 0.5 * root; - root = 0.5 / root; - w = - (matrix.getData()[Matrix3.getElementIndex(k, j)] - - matrix.getData()[Matrix3.getElementIndex(j, k)]) * - root; - quat[j] = - (matrix.getData()[Matrix3.getElementIndex(j, i)] + - matrix.getData()[Matrix3.getElementIndex(i, j)]) * - root; - quat[k] = - (matrix.getData()[Matrix3.getElementIndex(k, i)] + - matrix.getData()[Matrix3.getElementIndex(i, k)]) * - root; - - x = -quat[0]; - y = -quat[1]; - z = -quat[2]; - } - - result.x = x; - result.y = y; - result.z = z; - result.w = w; - - return result; - } - - /** - * 从相机 heading、pitch、roll 构造一个四元数。heading 表示绕 z 轴旋转角度、pitch 表示绕 y 轴旋转角度、roll 表示绕 x 轴旋转角度 - * @param headingPitchRoll - * @param result - * @returns - */ - static fromHeadingPitchRoll (headingPitchRoll: { heading: number, pitch: number, roll: number }, result: Quaternion) { - scratchRollQuaternion = Quaternion.fromAxisAngle( - Vector3.UNIT_X, - headingPitchRoll.roll, - scratchHPRQuaternion - ); - scratchPitchQuaternion = Quaternion.fromAxisAngle( - Vector3.UNIT_Y, - -headingPitchRoll.pitch, - result - ); - result = Quaternion.multiply( - scratchPitchQuaternion, - scratchRollQuaternion, - scratchPitchQuaternion - ); - scratchHeadingQuaternion = Quaternion.fromAxisAngle( - Vector3.UNIT_Z, - -headingPitchRoll.heading, - scratchHPRQuaternion - ); - - return Quaternion.multiply(scratchHeadingQuaternion, result, result); - } - - /** - * 将欧拉角转成四元数 - * @param euler - * @param result - * @returns - */ - static setFromEuler (euler: Euler, result: Quaternion) { - const x = euler.x; - const y = euler.y; - const z = euler.z; - const order = euler.order; - - const cos = Math.cos; - const sin = Math.sin; - - const c1 = cos(x / 2); - const c2 = cos(y / 2); - const c3 = cos(z / 2); - - const s1 = sin(x / 2); - const s2 = sin(y / 2); - const s3 = sin(z / 2); - - switch (order) { - case EulerOrder.XYZ: - result.x = s1 * c2 * c3 + c1 * s2 * s3; - result.y = c1 * s2 * c3 - s1 * c2 * s3; - result.z = c1 * c2 * s3 + s1 * s2 * c3; - result.w = c1 * c2 * c3 - s1 * s2 * s3; - - break; - case EulerOrder.YXZ: - result.x = s1 * c2 * c3 + c1 * s2 * s3; - result.y = c1 * s2 * c3 - s1 * c2 * s3; - result.z = c1 * c2 * s3 - s1 * s2 * c3; - result.w = c1 * c2 * c3 + s1 * s2 * s3; - - break; - case EulerOrder.ZXY: - result.x = s1 * c2 * c3 - c1 * s2 * s3; - result.y = c1 * s2 * c3 + s1 * c2 * s3; - result.z = c1 * c2 * s3 + s1 * s2 * c3; - result.w = c1 * c2 * c3 - s1 * s2 * s3; - - break; - case EulerOrder.ZYX: - result.x = s1 * c2 * c3 - c1 * s2 * s3; - result.y = c1 * s2 * c3 + s1 * c2 * s3; - result.z = c1 * c2 * s3 - s1 * s2 * c3; - result.w = c1 * c2 * c3 + s1 * s2 * s3; - - break; - case EulerOrder.YZX: - result.x = s1 * c2 * c3 + c1 * s2 * s3; - result.y = c1 * s2 * c3 + s1 * c2 * s3; - result.z = c1 * c2 * s3 - s1 * s2 * c3; - result.w = c1 * c2 * c3 - s1 * s2 * s3; - - break; - case EulerOrder.XZY: - result.x = s1 * c2 * c3 - c1 * s2 * s3; - result.y = c1 * s2 * c3 - s1 * c2 * s3; - result.z = c1 * c2 * s3 + s1 * s2 * c3; - result.w = c1 * c2 * c3 + s1 * s2 * s3; - - break; - default: - console.warn(`Quaternion.setFromEuler() encountered an unknown order: ${order}`); - } - - return result; - } - - /** - * 四元数打包成数组 - * @param value - * @param array - * @param startingIndex - * @returns - */ - static pack (value: Quaternion, array: Vec4DataType, startingIndex?: number) { - startingIndex = (startingIndex ?? 0); - - array[startingIndex++] = value.x; - array[startingIndex++] = value.y; - array[startingIndex++] = value.z; - array[startingIndex] = value.w; - - return array; - } - - /** - * 从数组中解包四元数 - * @param array - * @param startingIndex - * @param result - * @returns - */ - static unpack (array: Vec4DataType, startingIndex: number, result: Quaternion) { - result.x = array[startingIndex++]; - result.y = array[startingIndex++]; - result.z = array[startingIndex++]; - result.w = array[startingIndex]; - - return result; - } - - /** - * 从数组构造一个四元数 - * @param array - * @param startingIndex - * @param result - * @returns - */ - static fromArray (array: Vec4DataType) { - return Quaternion.unpack(array, 0, new Quaternion()); - } - - /** - * 两个四元数相乘 - * @param left - * @param right - * @param result - * @returns - */ - static multiply (left: Quaternion, right: Quaternion, result: Quaternion) { - const leftX = left.x; - const leftY = left.y; - const leftZ = left.z; - const leftW = left.w; - - const rightX = right.x; - const rightY = right.y; - const rightZ = right.z; - const rightW = right.w; - - const x = leftW * rightX + leftX * rightW + leftY * rightZ - leftZ * rightY; - const y = leftW * rightY - leftX * rightZ + leftY * rightW + leftZ * rightX; - const z = leftW * rightZ + leftX * rightY - leftY * rightX + leftZ * rightW; - const w = leftW * rightW - leftX * rightX - leftY * rightY - leftZ * rightZ; - - result.x = x; - result.y = y; - result.z = z; - result.w = w; - - return result; - } - - /** - * 从输入四元数克隆一个四元数 - * @param quaternion - * @param result - * @returns - */ - static clone (quaternion: Quaternion | Readonly) { - const result = new Quaternion(); - - result.x = quaternion.x; - result.y = quaternion.y; - result.z = quaternion.z; - result.w = quaternion.w; - - return result; - } - - static copyTo (quaternion: Quaternion | Readonly, result: Quaternion) { - result.x = quaternion.x; - result.y = quaternion.y; - result.z = quaternion.z; - result.w = quaternion.w; - - return result; - } - - /** - * 四元数的共轭 - * @param quaternion - * @param result - * @returns - */ - static conjugate (quaternion: Quaternion, result: Quaternion) { - result.x = -quaternion.x; - result.y = -quaternion.y; - result.z = -quaternion.z; - result.w = quaternion.w; - - return result; - } - - /** - * 四元数的模的平方 - * @param quaternion - * @returns - */ - static magnitudeSquared (quaternion: Quaternion) { - return ( - quaternion.x * quaternion.x + - quaternion.y * quaternion.y + - quaternion.z * quaternion.z + - quaternion.w * quaternion.w - ); - } - - /** - * 四元数的模 - * @param quaternion - * @returns - */ - static magnitude (quaternion: Quaternion) { - return Math.sqrt(Quaternion.magnitudeSquared(quaternion)); - } - - /** - * 单位化四元数 - * @param quaternion - * @param result - * @returns - */ - static normalize (quaternion: Quaternion, result: Quaternion) { - const inverseMagnitude = 1.0 / Quaternion.magnitude(quaternion); - const x = quaternion.x * inverseMagnitude; - const y = quaternion.y * inverseMagnitude; - const z = quaternion.z * inverseMagnitude; - const w = quaternion.w * inverseMagnitude; - - result.x = x; - result.y = y; - result.z = z; - result.w = w; - - return result; - } - - /** - * 四元数求逆 - * @param quaternion - * @param result - * @returns - */ - static inverse (quaternion: Quaternion, result: Quaternion) { - const magnitudeSquared = Quaternion.magnitudeSquared(quaternion); - - result = Quaternion.conjugate(quaternion, result); - - return Quaternion.multiplyByScalar(result, 1.0 / magnitudeSquared, result); - } - - static add (left: Quaternion, right: Quaternion, result: Quaternion) { - result.x = left.x + right.x; - result.y = left.y + right.y; - result.z = left.z + right.z; - result.w = left.w + right.w; - - return result; - } - - static subtract (left: Quaternion, right: Quaternion, result: Quaternion) { - result.x = left.x - right.x; - result.y = left.y - right.y; - result.z = left.z - right.z; - result.w = left.w - right.w; - - return result; - } - - static negate (quaternion: Quaternion, result: Quaternion) { - result.x = -quaternion.x; - result.y = -quaternion.y; - result.z = -quaternion.z; - result.w = -quaternion.w; - - return result; - } - - static dot (left: Quaternion, right: Quaternion) { - return ( - left.x * right.x + left.y * right.y + left.z * right.z + left.w * right.w - ); - } - - static multiplyByScalar (quaternion: Quaternion, scalar: number, result: Quaternion) { - result.x = quaternion.x * scalar; - result.y = quaternion.y * scalar; - result.z = quaternion.z * scalar; - result.w = quaternion.w * scalar; - - return result; - } - - static computeAxis (quaternion: Quaternion, result: Vector3) { - const w = quaternion.w; - - if (Math.abs(w - 1.0) < MathUtils.EPSILON6) { - result.x = result.y = result.z = 0; - - return result; - } - - const scalar = 1.0 / Math.sqrt(1.0 - w * w); - - result.x = quaternion.x * scalar; - result.y = quaternion.y * scalar; - result.z = quaternion.z * scalar; - - return result; - } - - /** - * 计算旋转角度 - * @param quaternion - * @returns - */ - static computeAngle (quaternion: Quaternion) { - if (Math.abs(quaternion.w - 1.0) < MathUtils.EPSILON6) { - return 0.0; - } - - return 2.0 * Math.acos(quaternion.w); - } - - /** - * 线性插值 - * @param start - * @param end - * @param t - * @param result - * @returns - */ - static lerp (start: Quaternion, end: Quaternion, t: number, result: Quaternion) { - lerpScratch = Quaternion.multiplyByScalar(end, t, lerpScratch); - result = Quaternion.multiplyByScalar(start, 1.0 - t, result); - - return Quaternion.add(lerpScratch, result, result); - } - - /** - * 球面线性插值 - * @param start - * @param end - * @param t - * @param result - * @returns - */ - static slerp (start: Quaternion, end: Quaternion, t: number, result: Quaternion) { - let dot = Quaternion.dot(start, end); - // The angle between start must be acute. Since q and -q represent - // the same rotation, negate q to get the acute angle. - let r = end; - - if (dot < 0.0) { - dot = -dot; - r = slerpEndNegated = Quaternion.negate(end, slerpEndNegated); - } - - // dot > 0, as the dot product approaches 1, the angle between the - // quaternions vanishes. use linear interpolation. - if (1.0 - dot < MathUtils.EPSILON6) { - return Quaternion.lerp(start, r, t, result); - } - - const theta = Math.acos(dot); - - slerpScaledP = Quaternion.multiplyByScalar( - start, - Math.sin((1 - t) * theta), - slerpScaledP - ); - slerpScaledR = Quaternion.multiplyByScalar( - r, - Math.sin(t * theta), - slerpScaledR - ); - result = Quaternion.add(slerpScaledP, slerpScaledR, result); - - return Quaternion.multiplyByScalar(result, 1.0 / Math.sin(theta), result); - } - - static equals (left: Quaternion, right: Quaternion) { - return ( - left === right || - ( - left.x === right.x && - left.y === right.y && - left.z === right.z && - left.w === right.w) - ); - } - - static equalsEpsilon (left: Quaternion, right: Quaternion, epsilon?: number) { - epsilon = (epsilon ?? 0); - - return ( - left === right || - ( - Math.abs(left.x - right.x) <= epsilon && - Math.abs(left.y - right.y) <= epsilon && - Math.abs(left.z - right.z) <= epsilon && - Math.abs(left.w - right.w) <= epsilon) - ); - } - - /** - * 将四元数转变为一个 4x4 矩阵 - * @param result - * @returns - */ - toMatrix4 (result: Matrix4) { - const x = this.x; - const y = this.y; - const z = this.z; - const w = this.w; - - const x2 = x + x; - const y2 = y + y; - const z2 = z + z; - - const xx = x * x2; - const xy = x * y2; - const xz = x * z2; - const yy = y * y2; - const yz = y * z2; - const zz = z * z2; - const wx = w * x2; - const wy = w * y2; - const wz = w * z2; - - result = Matrix4.fromColumnMajorArray([ - 1 - (yy + zz), - xy + wz, - xz - wy, - 0, - - xy - wz, - 1 - (xx + zz), - yz + wx, - 0, - - xz + wy, - yz - wx, - 1 - (xx + yy), - 0, - - 0, - 0, - 0, - 1, - ], result); - - return result; - } - - /** - * 使用四元数内部数据克隆出一个新的四元数 - * @returns - */ - clone () { - const result = Quaternion.clone(this); - - return result; - } - - /** - * 将四元数数据拷贝给 result - * @param result - */ - copyTo (result: Quaternion) { - result = Quaternion.copyTo(this, result); - - return result; - } - - /** - * 将四元数数据从 source 拷贝回来 - * @param source - */ - copyFrom (source: Quaternion) { - Quaternion.copyTo(source, this); - - return this; - } - - /** - * 绕 x 轴旋转角度分量 - * @returns - */ - roll (): number { - const x = this.x; - const y = this.y; - const z = this.z; - const w = this.w; - - return Math.atan2(2.0 * (x * y + w * z), w * w + x * x - y * y - z * z); - } - - /** - * 绕 y 轴旋转分量 - * @returns - */ - pitch (): number { - const x = this.x; - const y = this.y; - const z = this.z; - const w = this.w; - - return Math.atan2(2.0 * (y * z + w * x), w * w - x * x - y * y + z * z); - } - - /** - * 绕 z 轴旋转分量 - * @returns - */ - yaw (): number { - return Math.asin(2.0 * (this.x * this.z - this.w * this.y)); - } - - inverse (): Quaternion { - const result = Quaternion.inverse(this, this); - - return result; - } - - conjugate (): Quaternion { - const result = Quaternion.conjugate(this, this); - - return result; - } - - length (): number { - const result = Quaternion.magnitude(this); - - return result; - } - - normalize (): Quaternion { - const result = Quaternion.normalize(this, this); - - return result; - } - - add (other: Quaternion): Quaternion { - this.x += other.x; - this.y += other.y; - this.z += other.z; - this.w += other.w; - - return this; - } - - multiply (other: Quaternion): Quaternion { - const result = Quaternion.multiply(this, other, this); - - return result; - } - - set (x: number, y: number, z: number, w: number): Quaternion { - this.x = x; - this.y = y; - this.z = z; - this.w = w; - - return this; - } - - setFromEuler (euler: Euler): Quaternion { - const result = Quaternion.setFromEuler(euler, this); - - return result; - } - - toArray () { - const result = [ - this._data[0], - this._data[1], - this._data[2], - this._data[3], - ]; - - return result; - } - - static IDENTITY = Object.freeze(new Quaternion(0.0, 0.0, 0.0, 1.0)); - - static ZERO = Object.freeze(new Quaternion(0.0, 0.0, 0.0, 0.0)); -} - -let fromAxisAngleScratch = new Vector3(); - -const fromRotationMatrixNext = [1, 2, 0]; - -const fromRotationMatrixQuat = new Array(3); - -const scratchHPRQuaternion = new Quaternion(); -let scratchHeadingQuaternion = new Quaternion(); -let scratchPitchQuaternion = new Quaternion(); -let scratchRollQuaternion = new Quaternion(); - -const sampledQuaternionAxis = new Vector3(); -const sampledQuaternionRotation = new Vector3(); -const sampledQuaternionTempQuaternion = new Quaternion(); -const sampledQuaternionQuaternion0 = new Quaternion(); -const sampledQuaternionQuaternion0Conjugate = new Quaternion(); - -let lerpScratch = new Quaternion(); - -let slerpEndNegated = new Quaternion(); -let slerpScaledP = new Quaternion(); -let slerpScaledR = new Quaternion(); - -export { Quaternion }; diff --git a/plugin-packages/model/src/math/sphere.ts b/plugin-packages/model/src/math/sphere.ts deleted file mode 100644 index bb3fec718..000000000 --- a/plugin-packages/model/src/math/sphere.ts +++ /dev/null @@ -1,215 +0,0 @@ -import { Box3 } from './box3'; -import { Vector3 } from './vector3'; - -/** - * 球体的几何表示 - */ -class Sphere { - /** 球半径 */ - radius: number; - /** 球中心点 */ - center: Vector3; - - constructor (center: Vector3 = new Vector3(), radius = -1) { - this.center = center; - this.radius = radius; - } - - set (center: Vector3, radius: number): Sphere { - this.center.copyFrom(center); - this.radius = radius; - - return this; - } - - /** - * 从顶点数组中构造一个球 - * @param points - * @param optionalCenter - * @returns - */ - setFromPoints (points: Vector3[], optionalCenter?: Vector3): Sphere { - const center = this.center; - - if (optionalCenter !== undefined) { - center.copyFrom(optionalCenter); - } else { - new Box3().setFromPoints(points).getCenter(center); - } - - let maxRadiusSq = 0; - - for (let i = 0, il = points.length; i < il; i++) { - maxRadiusSq = Math.max(maxRadiusSq, center.distanceSquaredTo(points[i])); - } - - this.radius = Math.sqrt(maxRadiusSq); - - return this; - } - - copyFrom (sphere: Sphere): Sphere { - this.center.copyFrom(sphere.center); - this.radius = sphere.radius; - - return this; - } - - isEmpty (): boolean { - return (this.radius < 0); - } - - makeEmpty (): Sphere { - this.center.set(0, 0, 0); - this.radius = -1; - - return this; - } - - /** - * 判断点是否在球内(包含球面) - * @param point - * @returns - */ - containsPoint (point: Vector3): boolean { - return (point.distanceSquaredTo(this.center) <= (this.radius * this.radius)); - } - - /** - * 点到球的距离 - * @param point - * @returns - */ - distanceToPoint (point: Vector3): number { - return (point.distanceTo(this.center) - this.radius); - } - - /** - * 判断球 this 与球 other 是否相交 - * @param other - * @returns - */ - intersectsSphere (other: Sphere): boolean { - const radiusSum = this.radius + other.radius; - - return other.center.distanceSquaredTo(this.center) <= (radiusSum * radiusSum); - } - - /** - * 判断球与包围盒是否相交 - * @param box - * @returns - */ - intersectsBox (box: Box3): boolean { - return box.intersectsSphere(this); - } - - /** - * 限制点只能取球内值 - * @param point - * @param target - * @returns - */ - clampPoint (point: Vector3, target: Vector3): Vector3 { - const deltaLengthSq = this.center.distanceSquaredTo(point); - - target.copyFrom(point); - - if (deltaLengthSq > (this.radius * this.radius)) { - target.subVector(this.center).normalize(); - target.multiplyScalar(this.radius).addVector(this.center); - } - - return target; - } - - /** - * 获取球体的包围盒 - * @param target - * @returns - */ - getBoundingBox (target: Box3): Box3 { - if (target === undefined) { target = new Box3(); } - - if (this.isEmpty()) { - // Empty sphere produces empty bounding box - target.makeEmpty(); - - return target; - } - - target.set(this.center, this.center); - target.expandByScalar(this.radius); - - return target; - } - - /** - * 球的三维空间平移 - * @param offset - * @returns - */ - translate (offset: Vector3): Sphere { - this.center.addVector(offset); - - return this; - } - - /** - * 通过空间点扩展/缩放球 - * @param offset - * @returns - */ - expandByPoint (point: Vector3): Sphere { - // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L649-L671 - const toPoint = point.clone().subVector(this.center); - const lengthSq = toPoint.lengthSquared(); - - if (lengthSq > (this.radius * this.radius)) { - const length = Math.sqrt(lengthSq); - const missingRadiusHalf = (length - this.radius) * 0.5; - - // Nudge this sphere towards the target point. Add half the missing distance to radius, - // and the other half to position. This gives a tighter enclosure, instead of if - // the whole missing distance were just added to radius. - - this.center.addVector(toPoint.multiplyScalar(missingRadiusHalf / length)); - this.radius += missingRadiusHalf; - } - - return this; - } - - /** - * 球的并集 - * @param offset - * @returns - */ - union (sphere: Sphere): Sphere { - // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L759-L769 - - // To enclose another sphere into this sphere, we only need to enclose two points: - // 1) Enclose the farthest point on the other sphere into this sphere. - // 2) Enclose the opposite point of the farthest point into this sphere. - - const v1 = new Vector3(); - const toFarthestPoint = sphere.center.clone().subVector(this.center); - - toFarthestPoint.normalize().multiplyScalar(sphere.radius); - - this.expandByPoint(v1.copyFrom(sphere.center).addVector(toFarthestPoint)); - this.expandByPoint(v1.copyFrom(sphere.center).subVector(toFarthestPoint)); - - return this; - } - - equals (sphere: Sphere): boolean { - return Vector3.equals(sphere.center, this.center) && (sphere.radius === this.radius); - } - - clone () { - return new Sphere().copyFrom(this); - } -} - -export { Sphere }; diff --git a/plugin-packages/model/src/math/type.ts b/plugin-packages/model/src/math/type.ts deleted file mode 100644 index 8eac69b8a..000000000 --- a/plugin-packages/model/src/math/type.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 二维向量内部数据类型 - */ -type Vec2DataType = number[] | [number, number] | Float32Array; - -/** -* 三维向量内部数据类型 -*/ -type Vec3DataType = number[] | [number, number, number] | Float32Array; - -/** -* 四维向量内部数据类型 -*/ -type Vec4DataType = number[] | [number, number, number, number] | Float32Array; - -/** -* 二维矩阵内部数据类型 -*/ -type Mat2DataType = number[] | [number, number, number, number] | Float32Array; - -/** -* 三维矩阵内部数据类型 -*/ -type Mat3DataType = number[] | [number, number, number, number, number, number, number, number, number] | Float32Array; - -/** -* 四维矩阵内部数据类型 -*/ -type Mat4DataType = number[] | [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number] | Float32Array; - -/** - * 四元数内部数据类型 - */ -type QuatDataType = number[] | [number, number, number, number] | Float32Array; - -export { - Vec2DataType, Vec3DataType, Vec4DataType, Mat2DataType, Mat3DataType, Mat4DataType, QuatDataType, -}; diff --git a/plugin-packages/model/src/math/utilities/index.ts b/plugin-packages/model/src/math/utilities/index.ts deleted file mode 100644 index 004b5c98c..000000000 --- a/plugin-packages/model/src/math/utilities/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -class MathUtils { - static EPSILON6 = 0.000001; - static EPSILON7 = 0.0000001; - static EPSILON21 = 0.000000000000000000001; - - static acosClamped (value: number) { - return Math.acos(MathUtils.clamp(value, -1.0, 1.0)); - } - - static clamp (value: number, min: number, max: number) { - return value < min ? min : value > max ? max : value; - } - - static equalsEpsilon ( - left: number, - right: number, - relativeEpsilon: number, - absoluteEpsilon: number - ) { - relativeEpsilon = (relativeEpsilon ?? 0.0); - absoluteEpsilon = (absoluteEpsilon ?? relativeEpsilon); - const absDiff = Math.abs(left - right); - - return ( - absDiff <= absoluteEpsilon || - absDiff <= relativeEpsilon * Math.max(Math.abs(left), Math.abs(right)) - ); - } -} - -export { MathUtils }; diff --git a/plugin-packages/model/src/math/vector2.ts b/plugin-packages/model/src/math/vector2.ts deleted file mode 100644 index cdf38ee6f..000000000 --- a/plugin-packages/model/src/math/vector2.ts +++ /dev/null @@ -1,826 +0,0 @@ -import type { Vec2DataType } from './type'; -import { MathUtils } from './utilities/index'; - -/** - * 表示二维向量/二维点,内部使用 Float32Array 保存数据。 - */ -class Vector2 { - private _data: Float32Array; - - constructor (x?: number, y?: number) { - this._data = new Float32Array([x ?? 0.0, y ?? 0.0]); - } - - get x (): number { - return this._data[0]; - } - set x (value: number) { - this._data[0] = value; - } - - getX (): number { - return this._data[0]; - } - - setX (value: number) { - this._data[0] = value; - } - - get y (): number { - return this._data[1]; - } - set y (value: number) { - this._data[1] = value; - } - - getY (): number { - return this._data[1]; - } - - setY (value: number) { - this._data[1] = value; - } - - set (x: number, y: number) { - this._data[0] = x; - this._data[1] = y; - } - - get xy (): [number, number] { - return [this._data[0], this._data[1]]; - } - - set xy (data: [number, number]) { - this._data[0] = data[0]; - this._data[1] = data[1]; - } - - /** - * - * @param x - * @param y - * @returns - */ - static fromElements (x: number, y: number) { - const result = new Vector2(x, y); - - result.x = x; - result.y = y; - - return result; - } - - /** - * - * @param cartesian - * @returns - */ - static clone (cartesian: Vector2 | Readonly) { - const result = new Vector2(cartesian.x, cartesian.y); - - return result; - } - - /** - * 将二维向量打包到数组对象中。 - * @param value - 待打包的二维向量。 - * @param array - 保存打包向量数据。 - * @param startingIndex - 保存数据数组元素起始索引,默认在数组起始位置保存数据。 - * @returns 返回打包好的二维数据。 - */ - static pack (value: Vector2 | Readonly, array: Vec2DataType, startingIndex?: number) { - startingIndex = startingIndex ?? 0; - - array[startingIndex++] = value.x; - array[startingIndex] = value.y; - - return array; - } - - /** - * 从数组中解包二维向量 - * @param array - 待解包的数组 - * @param startingIndex - 从数组第几个位置开始解包二维向量 - * @param result - 保存解包后的二维向量 - * @returns 返回解包后的二维向量 - */ - static unpack (array: Vec2DataType, startingIndex: number, result: Vector2) { - result.x = array[startingIndex++]; - result.y = array[startingIndex]; - - return result; - } - - /** - * 将二维向量数组打包到数组中 - * @param array - 需要打包的二维向量数组 - * @param result - 保存打包后的数组 - * @returns - */ - static packArray (array: Vector2[] | Readonly[], result: Vec2DataType) { - const length = array.length; - - for (let i = 0; i < length; ++i) { - Vector2.pack(array[i], result, i * 2); - } - - return result; - } - - /** - * 将数组解包成二维向量数组 - * @param array - * @param result - * @returns - */ - static unpackArray (array: Vec2DataType, result: Vector2[]) { - const length = array.length; - - for (let i = 0; i < length; i += 2) { - const index = i / 2; - - result[index] = Vector2.unpack(array, i, result[index]); - } - - return result; - } - - /** - * - * @param array - * @returns - */ - static fromArray (array: Vec2DataType) { - return Vector2.unpack(array, 0, new Vector2()); - } - - /** - * 返回二维向量 x 和 y 的最大值 - * @param cartesian - * @returns - */ - static maximumComponent (cartesian: Vector2 | Readonly) { - return Math.max(cartesian.x, cartesian.y); - } - - /** - * 返回二维向量 x 和 y 的最小值 - * @param cartesian - * @returns - */ - static minimumComponent (cartesian: Vector2 | Readonly) { - return Math.min(cartesian.x, cartesian.y); - } - - /** - * 返回一个新的二维向量,保存输入两个向量各分量的最小值 - * @param first - * @param second - * @param result - * @returns - */ - static minimumByComponent (first: Vector2 | Readonly, second: Vector2 | Readonly, result: Vector2) { - result.x = Math.min(first.x, second.x); - result.y = Math.min(first.y, second.y); - - return result; - } - - /** - * 返回一个新的二维向量,保存输入两个向量各分量的最大值 - * @param first - * @param second - * @param result - * @returns - */ - static maximumByComponent (first: Vector2 | Readonly, second: Vector2 | Readonly, result: Vector2) { - result.x = Math.max(first.x, second.x); - result.y = Math.max(first.y, second.y); - - return result; - } - - /** - * 返回二维向量模的平方 - * @param cartesian - * @returns - */ - static magnitudeSquared (cartesian: Vector2 | Readonly) { - return cartesian.x * cartesian.x + cartesian.y * cartesian.y; - } - - /** - * 返回二维向量的模 - * @param cartesian - * @returns - */ - static magnitude (cartesian: Vector2 | Readonly) { - return Math.sqrt(Vector2.magnitudeSquared(cartesian)); - } - - /** - * 返回两个二维向量的距离 - * @param left - * @param right - * @returns - */ - static distance (left: Vector2 | Readonly, right: Vector2 | Readonly) { - Vector2.subtract(left, right, distanceScratch); - - return Vector2.magnitude(distanceScratch); - } - - /** - * 返回两个二维向量距离的平方 - * @param left - * @param right - * @returns - */ - static distanceSquared (left: Vector2 | Readonly, right: Vector2 | Readonly) { - Vector2.subtract(left, right, distanceScratch); - - return Vector2.magnitudeSquared(distanceScratch); - } - - /** - * 单位化二维向量 - * @param cartesian - * @param result - * @returns - */ - static normalize (cartesian: Vector2 | Readonly, result: Vector2) { - const magnitude = Vector2.magnitude(cartesian); - - result.x = cartesian.x / magnitude; - result.y = cartesian.y / magnitude; - - return result; - } - - /** - * 计算两个二维向量的点积 - * @param left - * @param right - * @returns - */ - static dot (left: Vector2 | Readonly, right: Vector2 | Readonly) { - return left.x * right.x + left.y * right.y; - } - - /** - * 计算两个二维向量的叉积 - * @param left - * @param right - * @returns - */ - static cross (left: Vector2 | Readonly, right: Vector2 | Readonly) { - return left.x * right.y - left.y * right.x; - } - - /** - * 两个二维向量对应元素相乘 - * @param left - * @param right - * @param result - * @returns - */ - static multiplyComponents (left: Vector2 | Readonly, right: Vector2 | Readonly, result: Vector2) { - result.x = left.x * right.x; - result.y = left.y * right.y; - - return result; - } - - /** - * 两个二维向量对应元素相除(内部不会检查除数为 0 情况) - * @param left - * @param right - * @param result - * @returns - */ - static divideComponents (left: Vector2 | Readonly, right: Vector2 | Readonly, result: Vector2) { - result.x = left.x / right.x; - result.y = left.y / right.y; - - return result; - } - - /** - * 两个二维向量对应元素相加 - * @param left - * @param right - * @param result - * @returns - */ - static add (left: Vector2 | Readonly, right: Vector2 | Readonly, result: Vector2) { - result.x = left.x + right.x; - result.y = left.y + right.y; - - return result; - } - - /** - * 两个二维向量对应元素相减 - * @param left - * @param right - * @param result - * @returns - */ - static subtract (left: Vector2 | Readonly, right: Vector2 | Readonly, result: Vector2) { - result.x = left.x - right.x; - result.y = left.y - right.y; - - return result; - } - - /** - * 二维向量元素乘一个标量 - * @param cartesian - * @param scalar - * @param result - * @returns - */ - static multiplyByScalar (cartesian: Vector2 | Readonly, scalar: number, result: Vector2) { - result.x = cartesian.x * scalar; - result.y = cartesian.y * scalar; - - return result; - } - - /** - * 二维向量元素除一个标量 - * @param cartesian - * @param scalar - * @param result - * @returns - */ - static divideByScalar (cartesian: Vector2 | Readonly, scalar: number, result: Vector2) { - result.x = cartesian.x / scalar; - result.y = cartesian.y / scalar; - - return result; - } - - /** - * 二维向量各元素相反数 - * @param cartesian - * @param result - * @returns - */ - static negate (cartesian: Vector2 | Readonly, result: Vector2) { - result.x = -cartesian.x; - result.y = -cartesian.y; - - return result; - } - - /** - * 二维向量各元素取绝对值 - * @param cartesian - * @param result - * @returns - */ - static abs (cartesian: Vector2 | Readonly, result: Vector2) { - result.x = Math.abs(cartesian.x); - result.y = Math.abs(cartesian.y); - - return result; - } - - /** - * 计算两个向量的线性插值结果 - * @param start - * @param end - * @param t - * @param result - * @returns - */ - static lerp (start: Vector2 | Readonly, end: Vector2 | Readonly, t: number, result: Vector2) { - Vector2.multiplyByScalar(end, t, lerpScratch); - - result = Vector2.multiplyByScalar(start, 1.0 - t, result); - - return Vector2.add(lerpScratch, result, result); - } - - /** - * 计算两个二维向量的夹角角度 - * @param left - * @param right - * @returns - */ - static angleBetween (left: Vector2 | Readonly, right: Vector2 | Readonly) { - Vector2.normalize(left, angleBetweenScratch); - Vector2.normalize(right, angleBetweenScratch2); - - return MathUtils.acosClamped( - Vector2.dot(angleBetweenScratch, angleBetweenScratch2) - ); - } - - static mostOrthogonalAxis (cartesian: Vector2 | Readonly, result: Vector2) { - const f = Vector2.normalize(cartesian, mostOrthogonalAxisScratch); - - Vector2.abs(f, f); - - if (f.x <= f.y) { - Vector2.UNIT_X.copyTo(result); - } else { - Vector2.UNIT_Y.copyTo(result); - } - - return result; - } - - /** - * 比较两个二维向量对应元素是否相等 - * @param left - * @param right - * @returns - */ - static equals (left: Vector2 | Readonly, right: Vector2 | Readonly) { - return ( - left === right || - left.x === right.x && - left.y === right.y); - } - - /** - * - * @param cartesian - * @param array - * @param offset - * @returns - */ - static equalsArray (cartesian: Vector2 | Readonly, array: Vec2DataType, offset?: number) { - offset = offset ?? 0; - - return cartesian.x === array[offset] && cartesian.y === array[offset + 1]; - } - - /** - * 二维向量对应元素是否相等 - * @param left - * @param right - * @param relativeEpsilon - * @param absoluteEpsilon - * @returns - */ - static equalsEpsilon ( - left: Vector2 | Readonly, - right: Vector2 | Readonly, - relativeEpsilon: number, - absoluteEpsilon: number - ) { - return ( - left === right || - ( - MathUtils.equalsEpsilon( - left.x, - right.x, - relativeEpsilon, - absoluteEpsilon - ) && - MathUtils.equalsEpsilon( - left.y, - right.y, - relativeEpsilon, - absoluteEpsilon - )) - ); - } - - static max (a: Vector2, b: Vector2, result: Vector2) { - Vector2.maximumByComponent(a, b, result); - - return result; - } - - static min (a: Vector2, b: Vector2, result: Vector2) { - Vector2.minimumByComponent(a, b, result); - - return result; - } - - static mix (a: Vector2, b: Vector2, t: number, result: Vector2) { - Vector2.lerp(a, b, t, result); - - return result; - } - - static floor (a: Vector2, result: Vector2) { - result.x = Math.floor(a.x); - result.y = Math.floor(a.y); - - return result; - } - - static ceil (a: Vector2, result: Vector2) { - result.x = Math.ceil(a.x); - result.y = Math.ceil(a.y); - - return result; - } - - static round (a: Vector2, result: Vector2) { - result.x = Math.round(a.x); - result.y = Math.round(a.y); - - return result; - } - - /** - * 将向量导出 number[] 对象 - * @returns - */ - toArray (): number[] { - const array = new Array(2); - const result = Vector2.pack(this, array) as number[]; - - return result; - } - - /** - * 克隆出一个新的二维向量 - * @returns - */ - clone () { - const result = Vector2.clone(this); - - return result; - } - - /** - * 将二维向量数据拷贝给 result - * @param result - * @returns - */ - copyTo (result: Vector2) { - result.x = this.x; - result.y = this.y; - - return result; - } - - /** - * 与标量相加 - * @param value - * @returns - */ - addScalar (value: number) { - this.x += value; - this.y += value; - - return this; - } - - /** - * 与二维向量相加 - * @param vector - * @returns - */ - addVector (vector: Vector2) { - const result = Vector2.add(this, vector, this); - - return result; - } - - /** - * 与标量相减 - * @param value - * @returns - */ - subScalar (value: number) { - this.x -= value; - this.y -= value; - - return this; - } - - /** - * 与向量相减 - * @param vector - * @returns - */ - subVector (vector: Vector2) { - const result = Vector2.subtract(this, vector, this); - - return result; - } - - /** - * 与标量相乘 - * @param value - * @returns - */ - multiplyScalar (value: number) { - const result = Vector2.multiplyByScalar(this, value, this); - - return result; - } - - /** - * 与向量相乘 - * @param vector - * @returns - */ - multiplyVector (vector: Vector2) { - const result = Vector2.multiplyComponents(this, vector, this); - - return result; - } - - /** - * 与向量相除 - * @param vector - * @returns - */ - divideVector (vector: Vector2) { - const result = Vector2.divideComponents(this, vector, this); - - return result; - } - - /** - * 与标量相除 - * @param value - * @returns - */ - divideScalar (value: number) { - const result = Vector2.divideByScalar(this, value, this); - - return result; - } - - /** - * 分量中最小值 - * @returns - */ - min () { - const result = Vector2.minimumComponent(this); - - return result; - } - - /** - * 分量中最大值 - * @returns - */ - max () { - const result = Vector2.maximumComponent(this); - - return result; - } - - /** - * 分别使用 min 和 max 对应的分量对向量进行 clamp 运算 - * @param min - * @param max - * @returns - */ - clamp (min: Vector2, max: Vector2): Vector2 { - this.x = Math.max(min.x, Math.min(max.x, this.x)); - this.y = Math.max(min.y, Math.min(max.y, this.y)); - - return this; - } - - /** - * 各分量分别进行 floor 运算 - * @returns - */ - floor (): Vector2 { - const result = Vector2.floor(this, this); - - return result; - } - - /** - * 各分量分别进行 ceil 运算 - * @returns - */ - ceil (): Vector2 { - const result = Vector2.ceil(this, this); - - return result; - } - - /** - * 各分量分别进行 round 运算 - * @returns - */ - round (): Vector2 { - const result = Vector2.round(this, this); - - return result; - } - - /** - * 各分量分别执行相反数运算 - * @returns - */ - negate (): Vector2 { - const result = Vector2.negate(this, this); - - return result; - } - - /** - * 与另一个向量点积运算 - * @param v - * @returns - */ - dot (v: Vector2) { - const result = Vector2.dot(this, v); - - return result; - } - - /** - * 与另一个向量叉积运算 - * @param v - * @returns - */ - cross (v: Vector2): number { - const result = Vector2.cross(this, v); - - return result; - } - - /** - * 计算向量长度 - * @returns - */ - length () { - const result = Vector2.magnitude(this); - - return result; - } - - /** - * 计算向量长度平方 - * @returns - */ - lengthSquared () { - const result = Vector2.magnitudeSquared(this); - - return result; - } - - /** - * 单位化向量 - * @returns - */ - normalize () { - const result = Vector2.normalize(this, this); - - return result; - } - - /** - * 计算向量与量一个向量夹角角度,弧度 - * @param v - * @returns - */ - angleTo (v: Vector2) { - const result = Vector2.angleBetween(this, v); - - return result; - } - - distanceTo (v: Vector2) { - const result = Vector2.distance(this, v); - - return result; - } - - distanceSquaredTo (v: Vector2) { - const result = Vector2.distanceSquared(this, v); - - return result; - } - - getData () { - return this._data; - } - - static ZERO = Object.freeze(new Vector2(0.0, 0.0)); - - static ONE = Object.freeze(new Vector2(1.0, 1.0)); - - static UNIT_X = Object.freeze(new Vector2(1.0, 0.0)); - - static UNIT_Y = Object.freeze(new Vector2(0.0, 1.0)); -} - -const distanceScratch = new Vector2(); - -const lerpScratch = new Vector2(); - -const angleBetweenScratch = new Vector2(); - -const angleBetweenScratch2 = new Vector2(); - -const mostOrthogonalAxisScratch = new Vector2(); - -export { Vector2 }; diff --git a/plugin-packages/model/src/math/vector3.ts b/plugin-packages/model/src/math/vector3.ts deleted file mode 100644 index 7d6d05199..000000000 --- a/plugin-packages/model/src/math/vector3.ts +++ /dev/null @@ -1,776 +0,0 @@ -import type { Quaternion } from './quaternion'; -import type { Vec3DataType } from './type'; -import { MathUtils } from './utilities/index'; - -class Vector3 { - private _data: Float32Array; - - constructor (x?: number, y?: number, z?: number) { - this._data = new Float32Array([x ?? 0.0, y ?? 0.0, z ?? 0.0]); - } - - get x (): number { - return this._data[0]; - } - set x (value: number) { - this._data[0] = value; - } - - getX (): number { - return this._data[0]; - } - - setX (value: number) { - this._data[0] = value; - } - - get y (): number { - return this._data[1]; - } - set y (value: number) { - this._data[1] = value; - } - - getY (): number { - return this._data[1]; - } - - setY (value: number) { - this._data[1] = value; - } - - get z (): number { - return this._data[2]; - } - set z (value: number) { - this._data[2] = value; - } - - getZ (): number { - return this._data[2]; - } - - setZ (value: number) { - this._data[2] = value; - } - - set (x: number, y: number, z: number) { - this._data[0] = x; - this._data[1] = y; - this._data[2] = z; - } - - get xyz (): [number, number, number] { - return [ - this._data[0], - this._data[1], - this._data[2], - ]; - } - - set xyz (data: [number, number, number]) { - this._data[0] = data[0]; - this._data[1] = data[1]; - this._data[2] = data[2]; - } - - static fromElements (x: number, y: number, z: number) { - const result = new Vector3(x, y, z); - - result.x = x; - result.y = y; - result.z = z; - - return result; - } - - static clone (cartesian: Vector3 | Readonly) { - return new Vector3(cartesian.x, cartesian.y, cartesian.z); - } - - static pack (value: Vector3, array: Vec3DataType, startingIndex?: number) { - startingIndex = (startingIndex ?? 0); - - array[startingIndex++] = value.x; - array[startingIndex++] = value.y; - array[startingIndex] = value.z; - - return array; - } - - static unpack (array: Vec3DataType, startingIndex: number, result: Vector3) { - result.x = array[startingIndex++]; - result.y = array[startingIndex++]; - result.z = array[startingIndex]; - - return result; - } - - static packArray (array: Vector3[], result: Vec3DataType) { - const length = array.length; - - for (let i = 0; i < length; ++i) { - Vector3.pack(array[i], result, i * 3); - } - - return result; - } - - static unpackArray (array: Vec3DataType, result: Vector3[]) { - const length = array.length; - - for (let i = 0; i < length; i += 3) { - const index = i / 3; - - result[index] = Vector3.unpack(array, i, result[index]); - } - - return result; - } - - /** - * - * @param array - * @returns - */ - static fromArray (array: Vec3DataType) { - return this.unpack(array, 0, new Vector3()); - } - - static maximumComponent (cartesian: Vector3) { - return Math.max(cartesian.x, cartesian.y, cartesian.z); - } - - static minimumComponent (cartesian: Vector3) { - return Math.min(cartesian.x, cartesian.y, cartesian.z); - } - - static minComponentIndex (cartesian: Vector3) { - if (cartesian.x < cartesian.y) { - if (cartesian.x < cartesian.z) { return 0; } else { return 2; } - } else { - if (cartesian.y < cartesian.z) { return 1; } else { return 2; } - } - } - - static tryZUpVector (lookatDir: Vector3, result: Vector3) { - const dir = lookatDir.clone().normalize(); - const dotList: number[] = []; - const upList = [ - new Vector3(0, 1, 0), - new Vector3(1, 0, 0), - new Vector3(0, 0, 1), - ]; - - upList.forEach(up => { - dotList.push(Math.abs(dir.dot(up))); - }); - - if (dotList[0] < 0.99) { - result.set(0, 1, 0); - } else { - let lastDot = 99999; - - dotList.forEach((dot, index) => { - if (lastDot > dot) { - result.copyFrom(upList[index]); - lastDot = dot; - } - }); - } - - return result; - } - - static computeUpVector (lookatDir: Vector3, result: Vector3) { - const dir = lookatDir.clone().normalize(); - const minIndex = Vector3.minComponentIndex(Vector3.abs(dir, dir)); - - if (minIndex !== 1) { - result.set(0, 1, 0); - } else { - result.set(dir.z, 0, -dir.x); - result.normalize(); - } - - return result; - } - - static minimumByComponent (first: Vector3, second: Vector3, result: Vector3) { - result.x = Math.min(first.x, second.x); - result.y = Math.min(first.y, second.y); - result.z = Math.min(first.z, second.z); - - return result; - } - - static maximumByComponent (first: Vector3, second: Vector3, result: Vector3) { - result.x = Math.max(first.x, second.x); - result.y = Math.max(first.y, second.y); - result.z = Math.max(first.z, second.z); - - return result; - } - - static magnitudeSquared (cartesian: Vector3 | Readonly) { - return ( - cartesian.x * cartesian.x + - cartesian.y * cartesian.y + - cartesian.z * cartesian.z - ); - } - - static magnitude (cartesian: Vector3 | Readonly) { - return Math.sqrt(Vector3.magnitudeSquared(cartesian)); - } - - static distance (left: Vector3, right: Vector3) { - Vector3.subtract(left, right, distanceScratch); - - return Vector3.magnitude(distanceScratch); - } - - static distanceSquared (left: Vector3, right: Vector3) { - Vector3.subtract(left, right, distanceScratch); - - return Vector3.magnitudeSquared(distanceScratch); - } - - static normalize (cartesian: Vector3 | Readonly, result: Vector3) { - const magnitude = Vector3.magnitude(cartesian); - - if (magnitude > 1e-5) { - result.x = cartesian.x / magnitude; - result.y = cartesian.y / magnitude; - result.z = cartesian.z / magnitude; - } else { - result.x = cartesian.x; - result.y = cartesian.y; - result.z = cartesian.z; - } - - return result; - } - - static dot (left: Vector3, right: Vector3) { - return left.x * right.x + left.y * right.y + left.z * right.z; - } - - static multiplyComponents (left: Vector3, right: Vector3, result: Vector3) { - result.x = left.x * right.x; - result.y = left.y * right.y; - result.z = left.z * right.z; - - return result; - } - - static divideComponents (left: Vector3, right: Vector3, result: Vector3) { - result.x = left.x / right.x; - result.y = left.y / right.y; - result.z = left.z / right.z; - - return result; - } - - static add (left: Vector3, right: Vector3, result: Vector3) { - result.x = left.x + right.x; - result.y = left.y + right.y; - result.z = left.z + right.z; - - return result; - } - - static subtract (left: Vector3, right: Vector3, result: Vector3) { - result.x = left.x - right.x; - result.y = left.y - right.y; - result.z = left.z - right.z; - - return result; - } - - static multiplyByScalar (cartesian: Vector3, scalar: number, result: Vector3) { - result.x = cartesian.x * scalar; - result.y = cartesian.y * scalar; - result.z = cartesian.z * scalar; - - return result; - } - - static divideByScalar (cartesian: Vector3, scalar: number, result: Vector3) { - result.x = cartesian.x / scalar; - result.y = cartesian.y / scalar; - result.z = cartesian.z / scalar; - - return result; - } - - static negate (cartesian: Vector3, result: Vector3) { - result.x = -cartesian.x; - result.y = -cartesian.y; - result.z = -cartesian.z; - - return result; - } - - static abs (cartesian: Vector3, result: Vector3) { - result.x = Math.abs(cartesian.x); - result.y = Math.abs(cartesian.y); - result.z = Math.abs(cartesian.z); - - return result; - } - - static clamp (target: Vector3, min: Vector3, max: Vector3) { - Vector3.max(target, min, target); - Vector3.min(target, max, target); - - return target; - } - - static lerp (start: Vector3, end: Vector3, t: number, result: Vector3) { - Vector3.multiplyByScalar(end, t, lerpScratch); - result = Vector3.multiplyByScalar(start, 1.0 - t, result); - - return Vector3.add(lerpScratch, result, result); - } - - static angleBetween (left: Vector3, right: Vector3) { - Vector3.normalize(left, angleBetweenScratch); - Vector3.normalize(right, angleBetweenScratch2); - const cosine = Vector3.dot(angleBetweenScratch, angleBetweenScratch2); - const sine = Vector3.magnitude( - Vector3.cross( - angleBetweenScratch, - angleBetweenScratch2, - angleBetweenScratch - ) - ); - - return Math.atan2(sine, cosine); - } - - static mostOrthogonalAxis (cartesian: Vector3, result: Vector3) { - const f = Vector3.normalize(cartesian, mostOrthogonalAxisScratch); - - Vector3.abs(f, f); - - if (f.x <= f.y) { - if (f.x <= f.z) { - Vector3.UNIT_X.copyTo(result); - } else { - Vector3.UNIT_Z.copyTo(result); - } - } else if (f.y <= f.z) { - Vector3.UNIT_Y.copyTo(result); - } else { - Vector3.UNIT_Z.copyTo(result); - } - - return result; - } - - static projectVector (a: Vector3, b: Vector3, result: Vector3) { - const scalar = Vector3.dot(a, b) / Vector3.dot(b, b); - - return Vector3.multiplyByScalar(b, scalar, result); - } - - static equals (left: Vector3, right: Vector3) { - return ( - left === right || - (left.x === right.x && - left.y === right.y && - left.z === right.z) - ); - } - - static equalsArray (cartesian: Vector3, array: Vec3DataType, offset?: number) { - offset = offset ?? 0; - - return ( - cartesian.x === array[offset] && - cartesian.y === array[offset + 1] && - cartesian.z === array[offset + 2] - ); - } - - static equalsEpsilon ( - left: Vector3, - right: Vector3, - relativeEpsilon: number, - absoluteEpsilon: number - ) { - return ( - left === right || - ( - MathUtils.equalsEpsilon( - left.x, - right.x, - relativeEpsilon, - absoluteEpsilon - ) && - MathUtils.equalsEpsilon( - left.y, - right.y, - relativeEpsilon, - absoluteEpsilon - ) && - MathUtils.equalsEpsilon( - left.z, - right.z, - relativeEpsilon, - absoluteEpsilon - )) - ); - } - - static cross (left: Vector3, right: Vector3, result?: Vector3) { - const leftX = left.x; - const leftY = left.y; - const leftZ = left.z; - const rightX = right.x; - const rightY = right.y; - const rightZ = right.z; - - const x = leftY * rightZ - leftZ * rightY; - const y = leftZ * rightX - leftX * rightZ; - const z = leftX * rightY - leftY * rightX; - - if (result === undefined) { - result = new Vector3(); - } - - result.x = x; - result.y = y; - result.z = z; - - return result; - } - - static midpoint (left: Vector3, right: Vector3, result: Vector3) { - result.x = (left.x + right.x) * 0.5; - result.y = (left.y + right.y) * 0.5; - result.z = (left.z + right.z) * 0.5; - - return result; - } - - /** - * 计算两个向量各分量最小值 - * @param a - * @param b - * @returns - */ - static min (a: Vector3, b: Vector3, result: Vector3) { - Vector3.minimumByComponent(a, b, result); - - return result; - } - - /** - * 计算两个向量各分量最大值 - * @param a - * @param b - * @returns - */ - static max (a: Vector3, b: Vector3, result: Vector3) { - Vector3.maximumByComponent(a, b, result); - - return result; - } - - /** - * 混合两个向量 - * @param a - * @param b - * @param t - * @returns - */ - static mix (a: Vector3, b: Vector3, t: number, result: Vector3) { - Vector3.lerp(a, b, t, result); - - return result; - } - - static floor (a: Vector3, result: Vector3) { - result.x = Math.floor(a.x); - result.y = Math.floor(a.y); - result.z = Math.floor(a.z); - - return result; - } - - static ceil (a: Vector3, result: Vector3) { - result.x = Math.ceil(a.x); - result.y = Math.ceil(a.y); - result.z = Math.ceil(a.z); - - return result; - } - - static round (a: Vector3, result: Vector3) { - result.x = Math.round(a.x); - result.y = Math.round(a.y); - result.z = Math.round(a.z); - - return result; - } - - /** - * 将三维向量数据拷贝给 result - * @param result - */ - copyTo (result: Vector3) { - result.x = this.x; - result.y = this.y; - result.z = this.z; - - return result; - } - - /** - * 将三维向量数据从 source 拷贝回来 - * @param source - */ - copyFrom (source: Vector3) { - this.x = source.x; - this.y = source.y; - this.z = source.z; - - return this; - } - - /** - * 三维向量数据取 min - * @param vec - */ - min (vec: Vector3) { - Vector3.min(this, vec, this); - - return this; - } - - /** - * 三维向量数据取 max - * @param vec - */ - max (vec: Vector3) { - Vector3.max(this, vec, this); - - return this; - } - - /** - * 将三维向量数据从 clamp - * @param min - * @param max - */ - clamp (min: Vector3, max: Vector3) { - Vector3.clamp(this, min, max); - - return this; - } - - /** - * 将三维向量导出为 number[] 对象 - */ - toArray (): number[] { - const array = new Array(3); - const result = Vector3.pack(this, array) as number[]; - - return result; - } - - /** - * 使用向量数据克隆一个新的三维向量 - * @returns - */ - clone () { - const result = Vector3.clone(this); - - return result; - } - - /** - * 单位化三维向量,会直接修改原向量数据 - */ - normalize () { - const result = Vector3.normalize(this, this); - - return result; - } - - /** - * 返回三维向量长度 - * @returns - */ - length () { - const result = Vector3.magnitude(this); - - return result; - } - - sum () { - return this.x + this.y + this.z; - } - - /** - * 返回向量长度的平方 - * @returns - */ - lengthSquared () { - const result = Vector3.magnitudeSquared(this); - - return result; - } - - floor () { - const result = Vector3.floor(this, this); - - return result; - } - - ceil () { - const result = Vector3.ceil(this, this); - - return result; - } - - round () { - const result = Vector3.round(this, this); - - return result; - } - - negate () { - const result = Vector3.negate(this, this); - - return result; - } - - addScalar (value: number) { - this.x += value; - this.y += value; - this.z += value; - - return this; - } - - addVector (vector: Vector3) { - const result = Vector3.add(this, vector, this); - - return this; - } - - subScalar (value: number) { - this.x -= value; - this.y -= value; - this.z -= value; - - return this; - } - - subVector (vector: Vector3) { - const result = Vector3.subtract(this, vector, this); - - return result; - } - - multiplyScalar (value: number) { - const result = Vector3.multiplyByScalar(this, value, this); - - return result; - } - - multiplyVector (vector: Vector3) { - const result = Vector3.multiplyComponents(this, vector, this); - - return result; - } - - divideScalar (value: number) { - const result = Vector3.divideByScalar(this, value, this); - - return result; - } - - divideVector (vector: Vector3) { - const result = Vector3.divideComponents(this, vector, this); - - return result; - } - - dot (vector: Vector3) { - const result = Vector3.dot(this, vector); - - return result; - } - - cross (vector: Vector3) { - const result = Vector3.cross(this, vector); - - return result; - } - - distanceTo (v: Vector3) { - const result = Vector3.distance(this, v); - - return result; - } - - distanceSquaredTo (v: Vector3) { - const result = Vector3.distanceSquared(this, v); - - return result; - } - - angleTo (v: Vector3) { - const result = Vector3.angleBetween(this, v); - - return result; - } - - getData () { - return this._data; - } - applyQuaternion (q: Quaternion, center: Vector3 = new Vector3()): this { - const x = this.x; - const y = this.y; - const z = this.z; - const { x: qx, y: qy, z: qz, w: qw } = q; - const { x: centerX, y: centerY, z: centerZ } = center; - - const ix = qw * (x - centerX) + qy * (z - centerZ) - qz * (y - centerY); - const iy = qw * (y - centerY) + qz * (x - centerX) - qx * (z - centerZ); - const iz = qw * (z - centerZ) + qx * (y - centerY) - qy * (x - centerX); - const iw = - qx * (x - centerX) - qy * (y - centerY) - qz * (z - centerZ); - - this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy + centerX; - this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz + centerY; - this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx + centerZ; - - return this; - } - - static ZERO = Object.freeze(new Vector3(0.0, 0.0, 0.0)); - - static ONE = Object.freeze(new Vector3(1.0, 1.0, 1.0)); - - static UNIT_X = Object.freeze(new Vector3(1.0, 0.0, 0.0)); - - static UNIT_Y = Object.freeze(new Vector3(0.0, 1.0, 0.0)); - - static UNIT_Z = Object.freeze(new Vector3(0.0, 0.0, 1.0)); -} - -const distanceScratch = new Vector3(); - -const lerpScratch = new Vector3(); - -const angleBetweenScratch = new Vector3(); - -const angleBetweenScratch2 = new Vector3(); - -const mostOrthogonalAxisScratch = new Vector3(); - -export { Vector3 }; diff --git a/plugin-packages/model/src/math/vector4.ts b/plugin-packages/model/src/math/vector4.ts deleted file mode 100644 index 1009993c6..000000000 --- a/plugin-packages/model/src/math/vector4.ts +++ /dev/null @@ -1,628 +0,0 @@ -import type { Vec4DataType } from './type'; -import { MathUtils } from './utilities/index'; - -/** - * 四维向量 - */ -class Vector4 { - private _data: Float32Array; - - constructor (x?: number, y?: number, z?: number, w?: number) { - this._data = new Float32Array([x ?? 0.0, y ?? 0.0, z ?? 0.0, w ?? 1.0]); - } - - get x (): number { - return this._data[0]; - } - set x (value: number) { - this._data[0] = value; - } - - getX (): number { - return this._data[0]; - } - - setX (value: number) { - this._data[0] = value; - } - - get y (): number { - return this._data[1]; - } - set y (value: number) { - this._data[1] = value; - } - - getY (): number { - return this._data[1]; - } - - setY (value: number) { - this._data[1] = value; - } - - get z (): number { - return this._data[2]; - } - set z (value: number) { - this._data[2] = value; - } - - getZ (): number { - return this._data[2]; - } - - setZ (value: number) { - this._data[2] = value; - } - - get w () { - return this._data[3]; - } - set w (value: number) { - this._data[3] = value; - } - - getW () { - return this._data[3]; - } - - setW (value: number) { - this._data[3] = value; - } - - set (x: number, y: number, z: number, w: number) { - this._data[0] = x; - this._data[1] = y; - this._data[2] = z; - this._data[3] = w; - } - - static fromElements (x: number, y: number, z: number, w: number, result?: Vector4) { - if (result == undefined) { - return new Vector4(x, y, z, w); - } - result.x = x; - result.y = y; - result.z = z; - result.w = w; - - return result; - } - - static clone (cartesian: Vector4 | Readonly) { - return new Vector4(cartesian.x, cartesian.y, cartesian.z, cartesian.w); - } - - // Cartesian4.packedLength = 4; - - static pack (value: Vector4, array: Vec4DataType, startingIndex?: number) { - startingIndex = (startingIndex ?? 0); - - array[startingIndex++] = value.x; - array[startingIndex++] = value.y; - array[startingIndex++] = value.z; - array[startingIndex] = value.w; - - return array; - } - - static unpack (array: Vec4DataType, startingIndex: number, result: Vector4) { - result.x = array[startingIndex++]; - result.y = array[startingIndex++]; - result.z = array[startingIndex++]; - result.w = array[startingIndex]; - - return result; - } - - static packArray (array: Vector4[], result: Vec4DataType) { - const length = array.length; - - for (let i = 0; i < length; ++i) { - Vector4.pack(array[i], result, i * 4); - } - - return result; - } - - static unpackArray (array: Vec4DataType, result: Vector4[]) { - const length = array.length; - - for (let i = 0; i < length; i += 4) { - const index = i / 4; - - result[index] = Vector4.unpack(array, i, result[index]); - } - - return result; - } - - static fromArray (array: Vec4DataType) { - return Vector4.unpack(array, 0, new Vector4()); - } - - static maximumComponent (cartesian: Vector4) { - return Math.max(cartesian.x, cartesian.y, cartesian.z, cartesian.w); - } - - static minimumComponent (cartesian: Vector4) { - return Math.min(cartesian.x, cartesian.y, cartesian.z, cartesian.w); - } - - static minimumByComponent (first: Vector4, second: Vector4, result: Vector4) { - result.x = Math.min(first.x, second.x); - result.y = Math.min(first.y, second.y); - result.z = Math.min(first.z, second.z); - result.w = Math.min(first.w, second.w); - - return result; - } - - static maximumByComponent (first: Vector4, second: Vector4, result: Vector4) { - result.x = Math.max(first.x, second.x); - result.y = Math.max(first.y, second.y); - result.z = Math.max(first.z, second.z); - result.w = Math.max(first.w, second.w); - - return result; - } - - static magnitudeSquared (cartesian: Vector4) { - return ( - cartesian.x * cartesian.x + - cartesian.y * cartesian.y + - cartesian.z * cartesian.z + - cartesian.w * cartesian.w - ); - } - - static magnitude (cartesian: Vector4) { - return Math.sqrt(Vector4.magnitudeSquared(cartesian)); - } - - static distance (left: Vector4, right: Vector4) { - Vector4.subtract(left, right, distanceScratch); - - return Vector4.magnitude(distanceScratch); - } - - static distanceSquared (left: Vector4, right: Vector4) { - Vector4.subtract(left, right, distanceScratch); - - return Vector4.magnitudeSquared(distanceScratch); - } - - static normalize (cartesian: Vector4, result: Vector4) { - const magnitude = Vector4.magnitude(cartesian); - - result.x = cartesian.x / magnitude; - result.y = cartesian.y / magnitude; - result.z = cartesian.z / magnitude; - result.w = cartesian.w / magnitude; - - return result; - } - - static dot (left: Vector4, right: Vector4) { - return ( - left.x * right.x + left.y * right.y + left.z * right.z + left.w * right.w - ); - } - - static multiplyComponents (left: Vector4, right: Vector4, result: Vector4) { - result.x = left.x * right.x; - result.y = left.y * right.y; - result.z = left.z * right.z; - result.w = left.w * right.w; - - return result; - } - - static divideComponents (left: Vector4, right: Vector4, result: Vector4) { - result.x = left.x / right.x; - result.y = left.y / right.y; - result.z = left.z / right.z; - result.w = left.w / right.w; - - return result; - } - - static add (left: Vector4, right: Vector4, result: Vector4) { - result.x = left.x + right.x; - result.y = left.y + right.y; - result.z = left.z + right.z; - result.w = left.w + right.w; - - return result; - } - - static subtract (left: Vector4, right: Vector4, result: Vector4) { - result.x = left.x - right.x; - result.y = left.y - right.y; - result.z = left.z - right.z; - result.w = left.w - right.w; - - return result; - } - - static multiplyByScalar (cartesian: Vector4, scalar: number, result: Vector4) { - result.x = cartesian.x * scalar; - result.y = cartesian.y * scalar; - result.z = cartesian.z * scalar; - result.w = cartesian.w * scalar; - - return result; - } - - static divideByScalar (cartesian: Vector4, scalar: number, result: Vector4) { - result.x = cartesian.x / scalar; - result.y = cartesian.y / scalar; - result.z = cartesian.z / scalar; - result.w = cartesian.w / scalar; - - return result; - } - - static negate (cartesian: Vector4, result: Vector4) { - result.x = -cartesian.x; - result.y = -cartesian.y; - result.z = -cartesian.z; - result.w = -cartesian.w; - - return result; - } - - static abs (cartesian: Vector4, result: Vector4) { - result.x = Math.abs(cartesian.x); - result.y = Math.abs(cartesian.y); - result.z = Math.abs(cartesian.z); - result.w = Math.abs(cartesian.w); - - return result; - } - - static lerp (start: Vector4, end: Vector4, t: number, result: Vector4) { - Vector4.multiplyByScalar(end, t, lerpScratch); - result = Vector4.multiplyByScalar(start, 1.0 - t, result); - - return Vector4.add(lerpScratch, result, result); - } - - static mostOrthogonalAxis (cartesian: Vector4, result: Vector4) { - const f = Vector4.normalize(cartesian, mostOrthogonalAxisScratch); - - Vector4.abs(f, f); - - if (f.x <= f.y) { - if (f.x <= f.z) { - if (f.x <= f.w) { - Vector4.UNIT_X.copyTo(result); - } else { - Vector4.UNIT_W.copyTo(result); - } - } else if (f.z <= f.w) { - Vector4.UNIT_Z.copyTo(result); - } else { - Vector4.UNIT_W.copyTo(result); - } - } else if (f.y <= f.z) { - if (f.y <= f.w) { - Vector4.UNIT_Y.copyTo(result); - } else { - Vector4.UNIT_W.copyTo(result); - } - } else if (f.z <= f.w) { - Vector4.UNIT_Z.copyTo(result); - } else { - Vector4.UNIT_W.copyTo(result); - } - - return result; - } - - static equals (left: Vector4, right: Vector4) { - return ( - left === right || - ( - left.x === right.x && - left.y === right.y && - left.z === right.z && - left.w === right.w) - ); - } - - static equalsArray (cartesian: Vector4, array: Vec4DataType, offset?: number) { - offset = offset ?? 0; - - return ( - cartesian.x === array[offset] && - cartesian.y === array[offset + 1] && - cartesian.z === array[offset + 2] && - cartesian.w === array[offset + 3] - ); - } - - static equalsEpsilon ( - left: Vector4, - right: Vector4, - relativeEpsilon: number, - absoluteEpsilon: number - ) { - return ( - left === right || - ( - MathUtils.equalsEpsilon( - left.x, - right.x, - relativeEpsilon, - absoluteEpsilon - ) && - MathUtils.equalsEpsilon( - left.y, - right.y, - relativeEpsilon, - absoluteEpsilon - ) && - MathUtils.equalsEpsilon( - left.z, - right.z, - relativeEpsilon, - absoluteEpsilon - ) && - MathUtils.equalsEpsilon( - left.w, - right.w, - relativeEpsilon, - absoluteEpsilon - )) - ); - } - - /** - * 将四维向量导出为 number[] 对象 - * @returns - */ - toArray (): number[] { - const array = new Array(4); - const result = Vector4.pack(this, array) as number[]; - - return result; - } - - /** - * 计算两个向量各分量最小值 - * @param a - * @param b - * @returns - */ - static min (a: Vector4, b: Vector4, result: Vector4) { - Vector4.minimumByComponent(a, b, result); - - return result; - } - - /** - * 计算两个向量各分量最大值 - * @param a - * @param b - * @returns - */ - static max (a: Vector4, b: Vector4, result: Vector4) { - Vector4.maximumByComponent(a, b, result); - - return result; - } - - /** - * 混合两个向量 - * @param a - * @param b - * @param t - * @returns - */ - static mix (a: Vector4, b: Vector4, t: number, result: Vector4) { - Vector4.lerp(a, b, t, result); - - return result; - } - - static floor (a: Vector4, result: Vector4) { - result.x = Math.floor(a.x); - result.y = Math.floor(a.y); - result.z = Math.floor(a.z); - result.w = Math.floor(a.w); - - return result; - } - - static ceil (a: Vector4, result: Vector4) { - result.x = Math.ceil(a.x); - result.y = Math.ceil(a.y); - result.z = Math.ceil(a.z); - result.w = Math.ceil(a.w); - - return result; - } - - static round (a: Vector4, result: Vector4) { - result.x = Math.round(a.x); - result.y = Math.round(a.y); - result.z = Math.round(a.z); - result.w = Math.round(a.w); - - return result; - } - - /** - * 将三维向量数据拷贝给 result - * @param result - */ - copyTo (result: Vector4) { - result.x = this.x; - result.y = this.y; - result.z = this.z; - result.w = this.w; - - return result; - } - - /** - * 使用向量数据克隆一个新的四维向量 - * @returns - */ - clone () { - const result = Vector4.clone(this); - - return result; - } - - /** - * 单位化三维向量,会直接修改原向量数据 - */ - normalize () { - const result = Vector4.normalize(this, this); - - return result; - } - - /** - * 返回三维向量长度 - * @returns - */ - length () { - const result = Vector4.magnitude(this); - - return result; - } - - /** - * 返回向量长度的平方 - * @returns - */ - lengthSquared () { - const result = Vector4.magnitudeSquared(this); - - return result; - } - - floor () { - const result = Vector4.floor(this, this); - - return result; - } - - ceil () { - const result = Vector4.ceil(this, this); - - return result; - } - - round () { - const result = Vector4.round(this, this); - - return result; - } - - negate () { - const result = Vector4.negate(this, this); - - return result; - } - - addScalar (value: number) { - this.x += value; - this.y += value; - this.z += value; - this.w += value; - - return this; - } - - addVector (vector: Vector4) { - const result = Vector4.add(this, vector, this); - - return result; - } - - subScalar (value: number) { - this.x -= value; - this.y -= value; - this.z -= value; - this.w -= value; - - return this; - } - - subVector (vector: Vector4) { - const result = Vector4.subtract(this, vector, this); - - return result; - } - - multiplyScalar (value: number) { - const result = Vector4.multiplyByScalar(this, value, this); - - return result; - } - - multiplyVector (vector: Vector4) { - const result = Vector4.multiplyComponents(this, vector, this); - - return result; - } - - divideScalar (value: number) { - const result = Vector4.divideByScalar(this, value, this); - - return result; - } - - divideVector (vector: Vector4) { - const result = Vector4.divideComponents(this, vector, this); - - return result; - } - - dot (vector: Vector4) { - const result = Vector4.dot(this, vector); - - return result; - } - - distanceTo (v: Vector4) { - const result = Vector4.distance(this, v); - - return result; - } - - distanceSquaredTo (v: Vector4) { - const result = Vector4.distanceSquared(this, v); - - return result; - } - - getData () { - return this._data; - } - - static ZERO = Object.freeze(new Vector4(0.0, 0.0, 0.0, 0.0)); - - static ONE = Object.freeze(new Vector4(1.0, 1.0, 1.0, 1.0)); - - static UNIT_X = Object.freeze(new Vector4(1.0, 0.0, 0.0, 0.0)); - - static UNIT_Y = Object.freeze(new Vector4(0.0, 1.0, 0.0, 0.0)); - - static UNIT_Z = Object.freeze(new Vector4(0.0, 0.0, 1.0, 0.0)); - - static UNIT_W = Object.freeze(new Vector4(0.0, 0.0, 0.0, 1.0)); -} - -const distanceScratch = new Vector4(); - -const lerpScratch = new Vector4(); - -const mostOrthogonalAxisScratch = new Vector4(); - -export { Vector4 }; diff --git a/plugin-packages/model/src/plugin/model-plugin.ts b/plugin-packages/model/src/plugin/model-plugin.ts index c2058314d..b9fb05fda 100644 --- a/plugin-packages/model/src/plugin/model-plugin.ts +++ b/plugin-packages/model/src/plugin/model-plugin.ts @@ -5,13 +5,13 @@ import type { RenderFrame, VFXItem, } from '@galacean/effects'; -import { AbstractPlugin, DEG2RAD, PLAYER_OPTIONS_ENV_EDITOR, spec } from '@galacean/effects'; +import { AbstractPlugin, PLAYER_OPTIONS_ENV_EDITOR, spec } from '@galacean/effects'; import { PCamera, PSceneManager } from '../runtime'; import { ModelVFXItem } from './model-vfx-item'; import { CompositionCache } from '../runtime/cache'; import { VFX_ITEM_TYPE_3D } from './const'; import { PluginHelper } from '../utility/plugin-helper'; -import { Matrix4, Vector3 } from '../math'; +import { Vector3, DEG2RAD } from '../runtime/math'; import { PCoordinate, PObjectType, PTransform } from '../runtime/common'; export class ModelPlugin extends AbstractPlugin { @@ -158,36 +158,36 @@ export class ModelPlugin extends AbstractPlugin { // 更加相机的具体参数,计算出合适的相机观察位置 // 但只会在第一帧进行更新,主要是用于测试使用 // this.autoAdjustScene = false; - const cameraParams = composition.camera; - const cameraTransform = new PTransform().fromEffectsTransform(cameraParams); + const cameraObject = composition.camera; + const cameraTransform = new PTransform().fromMatrix4(cameraObject.getViewMatrix()); const cameraCoordinate = new PCoordinate().fromPTransform(cameraTransform); const cameraDirection = cameraCoordinate.zAxis.clone(); - const cameraFov = cameraParams.fov ?? 45; - const cameraAspect = cameraParams.aspect ?? 1.0; + const cameraFov = cameraObject.fov ?? 45; + const cameraAspect = cameraObject.aspect ?? 1.0; // const sceneAABB = sceneManager.getSceneAABB(); - const newAABB = sceneAABB.clone().transform(cameraTransform.getMatrix()); - const newSize = newAABB.getSize(new Vector3()).multiplyScalar(0.5); + const newAABB = sceneAABB.clone().applyMatrix4(cameraTransform.getMatrix()); + const newSize = newAABB.getSize(new Vector3()).multiply(0.5); const newWidth = newSize.x; const newHeight = newSize.y; const finalHeight = newHeight * Math.max(newWidth / newHeight / cameraAspect, 1.0); const center = sceneAABB.getCenter(new Vector3()); const offset = finalHeight / Math.tan(cameraFov * 0.5 * DEG2RAD); - const position = center.clone().addVector(cameraDirection.clone().multiplyScalar(offset + newSize.z)); + const position = center.clone().add(cameraDirection.clone().multiply(offset + newSize.z)); // 更新相机的位置,主要是composition的camera数据,以及camera item数据 - composition.camera.position = position.toArray() as spec.vec3; + composition.camera.position = position; composition.items?.forEach(item => { if (item.type === VFX_ITEM_TYPE_3D) { const item3D = item as ModelVFXItem; if (item3D.content instanceof PCamera) { // @ts-expect-error - const worldMatrix = Matrix4.fromArray(item3D.transform.parentTransform.getWorldMatrix()); - const invWorldMatrix = worldMatrix.inverse(); - const newPosition = invWorldMatrix.multiplyByPoint3(position); + const worldMatrix = item3D.transform.parentTransform.getWorldMatrix(); + const invWorldMatrix = worldMatrix.invert(); + const newPosition = invWorldMatrix.transformPoint(position); - item3D.updateTransformPosition(newPosition.x, newPosition.y, newPosition.z); + item3D.setTransform(newPosition); // 正式版本不会走到这个流程,只在测试时使用 console.info(`Scene AABB [${sceneAABB.min.toArray()}], [${sceneAABB.max.toArray()}]`); diff --git a/plugin-packages/model/src/plugin/model-tree-plugin.ts b/plugin-packages/model/src/plugin/model-tree-plugin.ts index 54d9b043b..bd136e6fa 100644 --- a/plugin-packages/model/src/plugin/model-tree-plugin.ts +++ b/plugin-packages/model/src/plugin/model-tree-plugin.ts @@ -1,13 +1,5 @@ -import type { - Scene, - Composition, - RenderFrame, - VFXItem, -} from '@galacean/effects'; -import { - AbstractPlugin, - VFX_ITEM_TYPE_TREE, -} from '@galacean/effects'; +import type { Scene, Composition, RenderFrame, VFXItem } from '@galacean/effects'; +import { AbstractPlugin, spec } from '@galacean/effects'; import { PAnimationSystem } from '../runtime/animation'; import type { ModelTreeItem } from './model-tree-item'; @@ -42,7 +34,7 @@ export class ModelTreePlugin extends AbstractPlugin { } override onCompositionItemLifeBegin (composition: Composition, item: VFXItem) { - if (item.type === VFX_ITEM_TYPE_TREE) { + if (item.type === spec.ItemType.tree) { const animSystem = this.getAnimationSystem(composition); const treeItem = item.content; @@ -51,7 +43,7 @@ export class ModelTreePlugin extends AbstractPlugin { } override onCompositionItemRemoved (composition: Composition, item: VFXItem) { - if (item.type === VFX_ITEM_TYPE_TREE) { + if (item.type === spec.ItemType.tree) { const animSystem = this.getAnimationSystem(composition); const treeItem = item.content; diff --git a/plugin-packages/model/src/plugin/model-tree-vfx-item.ts b/plugin-packages/model/src/plugin/model-tree-vfx-item.ts index 69f1e97fe..14afd69be 100644 --- a/plugin-packages/model/src/plugin/model-tree-vfx-item.ts +++ b/plugin-packages/model/src/plugin/model-tree-vfx-item.ts @@ -1,5 +1,5 @@ -import type { spec, Transform, Composition } from '@galacean/effects'; -import { VFX_ITEM_TYPE_TREE, VFXItem, TimelineComponent } from '@galacean/effects'; +import type { Transform, Composition } from '@galacean/effects'; +import { spec, VFXItem, TimelineComponent } from '@galacean/effects'; import { ModelTreeItem } from './model-tree-item'; import type { ModelItemTree, ModelTreeOptions } from '../index'; @@ -8,7 +8,7 @@ export class ModelTreeVFXItem extends VFXItem { timeline?: TimelineComponent; override get type (): spec.ItemType { - return VFX_ITEM_TYPE_TREE; + return spec.ItemType.tree; } override onConstructed (props: ModelItemTree) { diff --git a/plugin-packages/model/src/plugin/model-vfx-item.ts b/plugin-packages/model/src/plugin/model-vfx-item.ts index f750b46b1..d5e75c7a5 100644 --- a/plugin-packages/model/src/plugin/model-vfx-item.ts +++ b/plugin-packages/model/src/plugin/model-vfx-item.ts @@ -3,10 +3,10 @@ import type { HitTestCustomParams, HitTestSphereParams, Composition, - Ray, Engine, + math, } from '@galacean/effects'; -import { HitTestType, VFXItem, spec, TimelineComponent } from '@galacean/effects'; +import { HitTestType, VFXItem, spec, TimelineComponent, Item } from '@galacean/effects'; import type { ModelItemBounding, ModelItemCamera, @@ -17,12 +17,16 @@ import type { } from '../index'; import { PCamera, PLight, PSkybox } from '../runtime'; import { PMesh } from '../runtime'; -import { Matrix4, Vector3 } from '../math'; +import { Vector3 } from '../runtime/math'; import type { CompositionCache } from '../runtime/cache'; import { VFX_ITEM_TYPE_3D } from './const'; import { CheckerHelper, RayIntersectsBoxWithRotation } from '../utility'; import { ModelTreeVFXItem } from './model-tree-vfx-item'; +type Vector2 = math.Vector2; +type Euler = math.Euler; +type Ray = math.Ray; + export type ModelItem = PMesh | PCamera | PLight | PSkybox; export type ModelItemOptions = ModelItemMesh | ModelItemCamera | ModelItemLight | ModelItemSkybox; @@ -47,13 +51,15 @@ export class ModelVFXItem extends VFXItem { this.bounding = bounding && JSON.parse(JSON.stringify(bounding)); - if (options.type === spec.ItemType.camera || options.type === spec.ItemType.light) { - //@ts-expect-error + if ( + Item.is(options, spec.ItemType.camera) || + Item.is(options, spec.ItemType.light) + ) { this.timeline = new TimelineComponent(options.content, this); this.timeline.getRenderData(0, true); } - if (options.type === spec.ItemType.skybox) { + if (Item.is>(options, spec.ItemType.skybox)) { // 从cache中创建天空盒 this.overwriteSkyboxFromCache(options.content.options as spec.SkyboxOptions<'studio'>); } @@ -67,7 +73,7 @@ export class ModelVFXItem extends VFXItem { override doCreateContent (composition: Composition) { switch (this.options?.type) { - case 'mesh':{ + case 'mesh': { const meshOptions = this.options.content.options; CheckerHelper.assertModelMeshOptions(meshOptions as spec.ModelMeshOptions); @@ -83,7 +89,7 @@ export class ModelVFXItem extends VFXItem { return new PCamera(this.options as spec.ModelCameraItem, width, height, this); } - case 'light':{ + case 'light': { const lightOptions = this.options.content.options; CheckerHelper.assertModelLightOptions(lightOptions as spec.ModelLightOptions); @@ -130,7 +136,7 @@ export class ModelVFXItem extends VFXItem { computeBoundingBox (): ModelItemBounding | undefined { if (this._content === undefined) { return; } if (this._content instanceof PMesh) { - const worldMatrix = Matrix4.fromArray(this.transform.getWorldMatrix()); + const worldMatrix = this.transform.getWorldMatrix(); const bbox = this._content.computeBoundingBox(worldMatrix); const center = bbox.getCenter(new Vector3()); const size = bbox.getSize(new Vector3()); @@ -152,12 +158,11 @@ export class ModelVFXItem extends VFXItem { const itemContent = this._content; if (itemContent !== undefined) { - const parentMat4 = new Matrix4(); + const parentMat4 = this.transform.getWorldMatrix(); - parentMat4.setData(this.transform.getWorldMatrix() as unknown as Float32Array); itemContent.matrix = parentMat4; if (itemContent instanceof PCamera && this.composition) { - this.composition.camera.position = this.transform.position; + this.composition.camera.position = this.transform.position.clone(); // FIXME: 可能存在相机朝向相反的问题 this.composition.camera.setQuat(this.transform.quat); this.composition.camera.near = itemContent.nearPlane; @@ -167,9 +172,16 @@ export class ModelVFXItem extends VFXItem { } } - updateTransformPosition (x: number, y: number, z: number): void { - this.timeline?.updatePosition(x, y, z); - this.transform.setPosition(x, y, z); + setTransform (position?: Vector3, rotation?: Euler): void { + if (position !== undefined) { + this.transform.setPosition(position.x, position.y, position.z); + this.timeline?.updatePosition(position.x, position.y, position.z); + } + if (rotation !== undefined) { + this.transform.setRotation(rotation.x, rotation.y, rotation.z); + this.timeline?.updateRotation(rotation.x, rotation.y, rotation.z); + } + this.updateTransform(); } override onLifetimeBegin (composition: Composition, content: ModelItem) { @@ -212,10 +224,8 @@ export class ModelVFXItem extends VFXItem { const customHitTest: HitTestCustomParams = { behavior: bounding.behavior as number, type: HitTestType.custom, - collect: function (ray: Ray, pointInCanvas: spec.vec2) { - const origin = Vector3.fromArray(ray.center); - const direction = Vector3.fromArray(ray.direction); - const result = mesh.hitTesting(origin, direction); + collect: function (ray: Ray, pointInCanvas: Vector2) { + const result = mesh.hitTesting(ray.origin, ray.direction); return result; }, @@ -227,8 +237,8 @@ export class ModelVFXItem extends VFXItem { const customHitTest: HitTestCustomParams = { behavior: bounding.behavior as number, type: HitTestType.custom, - collect: function (ray: Ray, pointInCanvas: spec.vec2) { - const result = RayIntersectsBoxWithRotation(ray, worldMatrixData, bounding) as spec.vec3[]; + collect: function (ray: Ray, pointInCanvas: Vector2) { + const result = RayIntersectsBoxWithRotation(ray, worldMatrixData, bounding); return result; }, @@ -237,14 +247,16 @@ export class ModelVFXItem extends VFXItem { return customHitTest; } } else if (type === spec.ModelBoundingType.sphere) { - const pos = [0, 0, 0]; + const pos = new Vector3(); this.transform.assignWorldTRS(pos); - const center: spec.vec3 = bounding.center ? bounding.center.slice() as spec.vec3 : [0, 0, 0]; + const center = new Vector3(); + + if (bounding.center) { + center.setFromArray(bounding.center); + } - center[0] += pos[0]; - center[1] += pos[1]; - center[2] += pos[2]; + center.add(pos); return { type: type as unknown as HitTestType.sphere, diff --git a/plugin-packages/model/src/runtime/animation.ts b/plugin-packages/model/src/runtime/animation.ts index b39043f62..b6be47c94 100644 --- a/plugin-packages/model/src/runtime/animation.ts +++ b/plugin-packages/model/src/runtime/animation.ts @@ -6,7 +6,7 @@ import type { ModelAnimationOptions, ModelTreeOptions, } from '../index'; -import { Matrix4 } from '../math'; +import { Matrix4 } from './math'; import { PObjectType } from './common'; import { PObject } from './object'; import type { InterpolationSampler } from './anim-sampler'; @@ -48,15 +48,14 @@ export class PSkin extends PObject { if (matList !== undefined && matList.length > 0) { if (matList.length % 16 !== 0 || matList.length !== this.jointList.length * 16) { - throw new Error(`Invalid array length, inverse bind matrices ${matList.length}, joint array ${this.jointList.length}`); + throw new Error(`Invalid array length, invert bind matrices ${matList.length}, joint array ${this.jointList.length}`); } const matrixCount = matList.length / 16; for (let i = 0; i < matrixCount; i++) { - const mat = new Matrix4(); + const mat = Matrix4.fromArray(matList, i * 16); - Matrix4.unpack(matList, i * 16, mat); this.inverseBindMatrices.push(mat); } } @@ -83,9 +82,8 @@ export class PSkin extends PObject { break; } const mat4 = node.transform.getWorldMatrix(); - const newMat4 = Matrix4.fromArray(mat4); - this.animationMatrices.push(newMat4); + this.animationMatrices.push(mat4.clone()); } } @@ -95,22 +93,22 @@ export class PSkin extends PObject { }); } else { this.animationMatrices = this.inverseBindMatrices; - console.error('Some error occured, replace skin animation matrices by inverse bind matrices'); + console.error('Some error occured, replace skin animation matrices by invert bind matrices'); } } computeMeshAnimMatrices (worldMatrix: Matrix4, matrixList: Float32Array, normalMatList: Float32Array) { - const inverseWorldMatrix = worldMatrix.clone().inverse(); + const inverseWorldMatrix = worldMatrix.clone().invert(); const tempMatrix = new Matrix4(); this.animationMatrices.forEach((mat, i) => { - const localMatrix = Matrix4.multiply(inverseWorldMatrix, mat, tempMatrix); + const localMatrix = tempMatrix.multiplyMatrices(inverseWorldMatrix, mat); - localMatrix.data.forEach((x, j) => matrixList[i * 16 + j] = x); + localMatrix.elements.forEach((x, j) => matrixList[i * 16 + j] = x); // - const normalMat = localMatrix.clone().inverse().transpose(); + const normalMat = localMatrix.clone().invert().transpose(); - normalMat.data.forEach((x, j) => normalMatList[i * 16 + j] = x); + normalMat.elements.forEach((x, j) => normalMatList[i * 16 + j] = x); }); } diff --git a/plugin-packages/model/src/runtime/camera.ts b/plugin-packages/model/src/runtime/camera.ts index 47d7da1c6..4aae61f45 100644 --- a/plugin-packages/model/src/runtime/camera.ts +++ b/plugin-packages/model/src/runtime/camera.ts @@ -1,11 +1,14 @@ +import type { math } from '@galacean/effects'; import { spec } from '@galacean/effects'; import type { ModelVFXItem } from '../plugin/model-vfx-item'; import type { ModelItemCamera } from '../index'; -import type { Quaternion, Box3 } from '../math'; -import { Vector2, Vector3, Matrix4 } from '../math'; +import { Vector2, Vector3, Matrix4 } from './math'; import { PObjectType } from './common'; import { PEntity } from './object'; +type Box3 = math.Box3; +type Quaternion = math.Quaternion; + const deg2rad = Math.PI / 180; export class PCamera extends PEntity { @@ -16,8 +19,8 @@ export class PCamera extends PEntity { fovy = 45; aspect = 1.0; clipMode = spec.CameraClipMode.landscape; - projectionMatrix = new Matrix4(); - viewMatrix = new Matrix4(); + projectionMatrix: Matrix4 = new Matrix4(); + viewMatrix: Matrix4 = new Matrix4(); constructor (camera: ModelItemCamera, width: number, height: number, ownerItem?: ModelVFXItem) { super(); @@ -43,12 +46,16 @@ export class PCamera extends PEntity { this.transform.fromEffectsTransform(this.ownerItem.transform); } - this.projectionMatrix.perspective(this.fovy * deg2rad, this.aspect, this.nearPlane, this.farPlane, this.isReversed()); - this.viewMatrix = this.matrix.inverse(); + const reverse = this.clipMode === spec.CameraClipMode.portrait; + + this.projectionMatrix.perspective(this.fovy * deg2rad, this.aspect, this.nearPlane, this.farPlane, reverse); + this.viewMatrix = this.matrix.invert(); } getNewProjectionMatrix (fov: number): Matrix4 { - return new Matrix4().perspective(Math.min(fov * 1.25, 140) * deg2rad, this.aspect, this.nearPlane, this.farPlane, this.isReversed()); + const reverse = this.clipMode === spec.CameraClipMode.portrait; + + return new Matrix4().perspective(Math.min(fov * 1.25, 140) * deg2rad, this.aspect, this.nearPlane, this.farPlane, reverse); } computeViewAABB (box: Box3): Box3 { @@ -74,15 +81,15 @@ export class PCamera extends PEntity { box.makeEmpty(); const matrix = this.matrix; - box.expandByPoint(matrix.multiplyByPoint3(new Vector3(xFarCoord, yFarCoord, -this.farPlane))); - box.expandByPoint(matrix.multiplyByPoint3(new Vector3(xFarCoord, -yFarCoord, -this.farPlane))); - box.expandByPoint(matrix.multiplyByPoint3(new Vector3(-xFarCoord, yFarCoord, -this.farPlane))); - box.expandByPoint(matrix.multiplyByPoint3(new Vector3(-xFarCoord, -yFarCoord, -this.farPlane))); + box.expandByPoint(matrix.transformPoint(new Vector3(xFarCoord, yFarCoord, -this.farPlane))); + box.expandByPoint(matrix.transformPoint(new Vector3(xFarCoord, -yFarCoord, -this.farPlane))); + box.expandByPoint(matrix.transformPoint(new Vector3(-xFarCoord, yFarCoord, -this.farPlane))); + box.expandByPoint(matrix.transformPoint(new Vector3(-xFarCoord, -yFarCoord, -this.farPlane))); // - box.expandByPoint(matrix.multiplyByPoint3(new Vector3(xNearCoord, yNearCoord, -this.nearPlane))); - box.expandByPoint(matrix.multiplyByPoint3(new Vector3(xNearCoord, -yNearCoord, -this.nearPlane))); - box.expandByPoint(matrix.multiplyByPoint3(new Vector3(-xNearCoord, yNearCoord, -this.nearPlane))); - box.expandByPoint(matrix.multiplyByPoint3(new Vector3(-xNearCoord, -yNearCoord, -this.nearPlane))); + box.expandByPoint(matrix.transformPoint(new Vector3(xNearCoord, yNearCoord, -this.nearPlane))); + box.expandByPoint(matrix.transformPoint(new Vector3(xNearCoord, -yNearCoord, -this.nearPlane))); + box.expandByPoint(matrix.transformPoint(new Vector3(-xNearCoord, yNearCoord, -this.nearPlane))); + box.expandByPoint(matrix.transformPoint(new Vector3(-xNearCoord, -yNearCoord, -this.nearPlane))); return box; } @@ -181,7 +188,7 @@ export class PCameraManager { fovy: number, nearPlane: number, farPlane: number, - position: spec.vec3, + position: Vector3, rotation: Quaternion, clipMode: number, ) { diff --git a/plugin-packages/model/src/runtime/common.ts b/plugin-packages/model/src/runtime/common.ts index e8bb78fc4..d11af69b5 100644 --- a/plugin-packages/model/src/runtime/common.ts +++ b/plugin-packages/model/src/runtime/common.ts @@ -1,3 +1,4 @@ +import type { math } from '@galacean/effects'; import { Transform as EffectsTransform, PLAYER_OPTIONS_ENV_EDITOR, spec } from '@galacean/effects'; import { Quaternion, @@ -5,9 +6,11 @@ import { Vector3, Matrix4, EulerOrder, -} from '../math'; +} from './math'; import type { BaseTransform } from '../index'; +type Euler = math.Euler; + export enum PObjectType { none = 0, mesh, @@ -102,7 +105,7 @@ export class PTransform { toEffectsTransform (transform: EffectsTransform) { const mat = this.getMatrix(); - transform.cloneFromMatrix(mat.toArray() as spec.mat4); + transform.cloneFromMatrix(mat); return transform; } @@ -189,13 +192,13 @@ export class PTransform { setMatrix (mat: Matrix4 | spec.mat4) { if (mat instanceof Matrix4) { - const res = mat.decompose(); + const res = mat.getTransform(); this.setTranslation(res.translation); this.setRotation(res.rotation); this.setScale(res.scale); } else { - const res = Matrix4.fromArray(mat).decompose(); + const res = Matrix4.fromArray(mat).getTransform(); this.setTranslation(res.translation); this.setRotation(res.rotation); @@ -217,12 +220,12 @@ export class PCoordinate { this.zAxis = new Vector3(0, 0, 1); } - fromPTransform (trans: PTransform, inverse = false) { + fromPTransform (trans: PTransform, invert = false) { this.origin.copyFrom(trans.getPosition()); const rotationMatrix = trans.getRotation().toMatrix4(new Matrix4()); - if (inverse) { - rotationMatrix.inverse(); + if (invert) { + rotationMatrix.invert(); } this.fromRotationMatrix(rotationMatrix); @@ -230,11 +233,11 @@ export class PCoordinate { } fromRotationMatrix (matrix: Matrix4) { - const matrixData = matrix.data; + const me = matrix.elements; - this.xAxis.set(matrixData[0], matrixData[1], matrixData[2]); - this.yAxis.set(matrixData[4], matrixData[5], matrixData[6]); - this.zAxis.set(matrixData[8], matrixData[9], matrixData[10]); + this.xAxis.set(me[0], me[1], me[2]); + this.yAxis.set(me[4], me[5], me[6]); + this.zAxis.set(me[8], me[9], me[10]); } } diff --git a/plugin-packages/model/src/runtime/light.ts b/plugin-packages/model/src/runtime/light.ts index a9bc8b62f..16fdbf052 100644 --- a/plugin-packages/model/src/runtime/light.ts +++ b/plugin-packages/model/src/runtime/light.ts @@ -1,19 +1,19 @@ import type { ModelItemLight } from '../index'; -import { Vector2, Vector3, Matrix4 } from '../math'; +import { Vector2, Vector3 } from './math'; import { PObjectType, PLightType } from './common'; import { PEntity } from './object'; import { PluginHelper } from '../utility/plugin-helper'; import type { ModelVFXItem } from '../plugin/model-vfx-item'; export class PLight extends PEntity { - direction = new Vector3(0, 0, 1); + direction: Vector3 = new Vector3(0, 0, 1); range = 0; - color = new Vector3(1, 1, 1); + color: Vector3 = new Vector3(1, 1, 1); intensity = 0; outerConeAngle = 0; innerConeAngle = 0; lightType = PLightType.ambient; - padding = new Vector2(0, 0); + padding: Vector2 = new Vector2(0, 0); constructor (light: ModelItemLight, ownerItem?: ModelVFXItem) { super(); @@ -85,9 +85,7 @@ export class PLight extends PEntity { } getWorldDirection (): Vector3 { - const matrix = this.matrix; - - return Matrix4.multiplyByPointAsVector(matrix, this.direction, new Vector3()); + return this.matrix.transformNormal(this.direction, new Vector3()); } } diff --git a/plugin-packages/model/src/runtime/material.ts b/plugin-packages/model/src/runtime/material.ts index 87d474fb9..3a65d2d19 100644 --- a/plugin-packages/model/src/runtime/material.ts +++ b/plugin-packages/model/src/runtime/material.ts @@ -5,7 +5,7 @@ import type { ModelMaterialUnlitOptions, ModelMaterialPBROptions, } from '../index'; -import { Vector3, Vector4, Matrix3 } from '../math'; +import { Vector3, Vector4, Matrix3 } from './math'; import { PObjectType, PMaterialType, @@ -253,13 +253,13 @@ export class PMaterialUnlit extends PMaterialBase { override updateUniforms (material: Material) { super.updateUniforms(material); // - const uvTransform = Matrix3.IDENTITY.clone(); + const uvTransform = new Matrix3().identity(); - material.setVector4('u_BaseColorFactor', this.baseColorFactor.toArray() as spec.vec4); + material.setVector4('u_BaseColorFactor', this.baseColorFactor); if (this.hasBaseColorTexture()) { material.setTexture('u_BaseColorSampler', this.getBaseColorTexture()); material.setInt('u_BaseColorUVSet', 0); - material.setMatrix('u_BaseColorUVTransform', uvTransform.toArray() as spec.mat4); + material.setMatrix3('u_BaseColorUVTransform', uvTransform); } material.setFloat('u_MetallicFactor', 0.0); material.setFloat('u_RoughnessFactor', 0.0); @@ -296,7 +296,7 @@ export class PMaterialUnlit extends PMaterialBase { export class PMaterialPBR extends PMaterialBase { baseColorTexture?: Texture; baseColorTextureTrans?: Matrix3; - baseColorFactor = new Vector4(1, 1, 1, 1); + baseColorFactor: Vector4 = new Vector4(1, 1, 1, 1); // metallicRoughnessTexture?: Texture; metallicRoughnessTextureTrans?: Matrix3; @@ -314,7 +314,7 @@ export class PMaterialPBR extends PMaterialBase { // emissiveTexture?: Texture; emissiveTextureTrans?: Matrix3; - emissiveFactor = new Vector3(0, 0, 0); + emissiveFactor: Vector3 = new Vector3(0, 0, 0); emissiveIntensity = 1; // enableShadow = false; @@ -428,17 +428,17 @@ export class PMaterialPBR extends PMaterialBase { override updateUniforms (material: Material) { super.updateUniforms(material); // - const uvTransform = Matrix3.IDENTITY.clone(); + const uvTransform = new Matrix3().identity(); - material.setVector4('u_BaseColorFactor', this.baseColorFactor.toArray() as spec.vec4); + material.setVector4('u_BaseColorFactor', this.baseColorFactor); if (this.baseColorTexture !== undefined) { material.setTexture('u_BaseColorSampler', this.baseColorTexture); material.setInt('u_BaseColorUVSet', 0); if (this.baseColorTextureTrans !== undefined) { - material.setMatrix3('u_BaseColorUVTransform', this.baseColorTextureTrans.toArray() as spec.mat3); + material.setMatrix3('u_BaseColorUVTransform', this.baseColorTextureTrans); } else { // fill other data - material.setMatrix3('u_BaseColorUVTransform', uvTransform.toArray() as spec.mat3); + material.setMatrix3('u_BaseColorUVTransform', uvTransform); } } // @@ -448,10 +448,10 @@ export class PMaterialPBR extends PMaterialBase { material.setTexture('u_MetallicRoughnessSampler', this.metallicRoughnessTexture); material.setInt('u_MetallicRoughnessUVSet', 0); if (this.metallicRoughnessTextureTrans !== undefined) { - material.setMatrix3('u_MetallicRoughnessUVTransform', this.metallicRoughnessTextureTrans.toArray() as spec.mat3); + material.setMatrix3('u_MetallicRoughnessUVTransform', this.metallicRoughnessTextureTrans); } else { // fill other data - material.setMatrix3('u_MetallicRoughnessUVTransform', uvTransform.toArray() as spec.mat3); + material.setMatrix3('u_MetallicRoughnessUVTransform', uvTransform); } } // @@ -460,10 +460,10 @@ export class PMaterialPBR extends PMaterialBase { material.setFloat('u_NormalScale', this.normalTextureScale); material.setInt('u_NormalUVSet', 0); if (this.normalTextureTrans !== undefined) { - material.setMatrix3('u_NormalUVTransform', this.normalTextureTrans.toArray() as spec.mat3); + material.setMatrix3('u_NormalUVTransform', this.normalTextureTrans); } else { // fill other data - material.setMatrix3('u_NormalUVTransform', uvTransform.toArray() as spec.mat3); + material.setMatrix3('u_NormalUVTransform', uvTransform); } } // @@ -472,10 +472,10 @@ export class PMaterialPBR extends PMaterialBase { material.setFloat('u_OcclusionStrength', this.occlusionTextureStrength); material.setInt('u_OcclusionUVSet', 0); if (this.occlusionTextureTrans !== undefined) { - material.setMatrix3('u_OcclusionUVTransform', this.occlusionTextureTrans.toArray() as spec.mat3); + material.setMatrix3('u_OcclusionUVTransform', this.occlusionTextureTrans); } else { // fill other data - material.setMatrix3('u_OcclusionUVTransform', uvTransform.toArray() as spec.mat3); + material.setMatrix3('u_OcclusionUVTransform', uvTransform); } } // @@ -483,18 +483,18 @@ export class PMaterialPBR extends PMaterialBase { const emissiveFactor = this.getEmissiveFactor(); material.setTexture('u_EmissiveSampler', this.emissiveTexture); - material.setVector3('u_EmissiveFactor', emissiveFactor.toArray() as spec.vec3); + material.setVector3('u_EmissiveFactor', emissiveFactor); material.setInt('u_EmissiveUVSet', 0); if (this.emissiveTextureTrans !== undefined) { - material.setMatrix3('u_EmissiveUVTransform', this.emissiveTextureTrans.toArray() as spec.mat3); + material.setMatrix3('u_EmissiveUVTransform', this.emissiveTextureTrans); } else { // fill other data - material.setMatrix3('u_EmissiveUVTransform', uvTransform.toArray() as spec.mat3); + material.setMatrix3('u_EmissiveUVTransform', uvTransform); } } else if (this.hasEmissiveFactor()) { const emissiveFactor = this.getEmissiveFactor(); - material.setVector3('u_EmissiveFactor', emissiveFactor.toArray() as spec.vec3); + material.setVector3('u_EmissiveFactor', emissiveFactor); } material.setFloat('u_Exposure', 3.0); @@ -595,7 +595,7 @@ export class PMaterialPBR extends PMaterialBase { } getEmissiveFactor (): Vector3 { - return this.emissiveFactor.clone().multiplyScalar(this.emissiveIntensity); + return this.emissiveFactor.clone().multiply(this.emissiveIntensity); } setEmissiveFactor (val: Vector3 | spec.vec3) { diff --git a/plugin-packages/model/src/runtime/math.ts b/plugin-packages/model/src/runtime/math.ts new file mode 100644 index 000000000..ba6f6e562 --- /dev/null +++ b/plugin-packages/model/src/runtime/math.ts @@ -0,0 +1,14 @@ +import { math } from '@galacean/effects'; + +export const { + Vector2, Vector3, Vector4, Matrix3, Matrix4, + Euler, EulerOrder, Quaternion, + Box3, Sphere, Ray, DEG2RAD, +} = math; + +export type Vector2 = math.Vector2; +export type Vector3 = math.Vector3; +export type Vector4 = math.Vector4; +export type Matrix3 = math.Matrix3; +export type Matrix4 = math.Matrix4; +export type Quaternion = math.Quaternion; diff --git a/plugin-packages/model/src/runtime/mesh.ts b/plugin-packages/model/src/runtime/mesh.ts index 877c60295..caa346fed 100644 --- a/plugin-packages/model/src/runtime/mesh.ts +++ b/plugin-packages/model/src/runtime/mesh.ts @@ -1,4 +1,4 @@ -import type { Texture, Geometry, Engine } from '@galacean/effects'; +import type { Texture, Geometry, Engine, math } from '@galacean/effects'; import { spec, Mesh, @@ -26,7 +26,7 @@ import { PMaterialUnlit, createPluginMaterial, } from './material'; -import { Matrix4, Vector3, Box3 } from '../math'; +import { Matrix4, Vector3, Box3, Vector2 } from './math'; import { PSkin, PAnimTexture, PMorph, TextureDataMode } from './animation'; import type { PSceneStates } from './scene'; import type { PSkybox } from './skybox'; @@ -38,6 +38,8 @@ import { BoxMesh } from '../utility/ri-helper'; import { RayBoxTesting } from '../utility/hit-test-helper'; import type { ModelTreeVFXItem, ModelTreeNode } from '../plugin'; +type Box3 = math.Box3; + export class PMesh extends PEntity { /** * 3D 元素父节点 @@ -59,7 +61,7 @@ export class PMesh extends PEntity { primitives: PPrimitive[] = []; hide = true; priority = 0; - boundingBox = new Box3(); + boundingBox: Box3 = new Box3(); visBoundingBox = false; boundingBoxMesh?: BoxMesh; isBuilt = false; @@ -193,7 +195,7 @@ export class PMesh extends PEntity { override updateUniformsForScene (sceneStates: PSceneStates) { const worldMatrix = this.matrix; - const normalMatrix = worldMatrix.clone().inverse().transpose(); + const normalMatrix = worldMatrix.clone().invert().transpose(); this.primitives.forEach(prim => { prim.updateUniformsForScene(worldMatrix, normalMatrix, sceneStates); @@ -226,11 +228,11 @@ export class PMesh extends PEntity { }); } - hitTesting (rayOrigin: Vector3, rayDirection: Vector3): spec.vec3[] { + hitTesting (rayOrigin: Vector3, rayDirection: Vector3): Vector3[] { const worldMatrix = this.matrix; - const invWorldMatrix = worldMatrix.clone().inverse(); - const newOrigin = Matrix4.multiplyByPoint(invWorldMatrix, rayOrigin, new Vector3()); - const newDirection = Matrix4.multiplyByPointAsVector(invWorldMatrix, rayDirection, new Vector3()); + const invWorldMatrix = worldMatrix.clone().invert(); + const newOrigin = invWorldMatrix.transformPoint(rayOrigin, new Vector3()); + const newDirection = invWorldMatrix.transformNormal(rayDirection, new Vector3()); const bounding = this.boundingBox; const boxt = RayBoxTesting(newOrigin, newDirection, bounding.min, bounding.max); @@ -259,16 +261,16 @@ export class PMesh extends PEntity { return []; } - newDirection.multiplyScalar(mint); - newOrigin.addVector(newDirection); - worldMatrix.multiplyByPoint3(newOrigin); + newDirection.multiply(mint); + newOrigin.add(newDirection); + worldMatrix.transformPoint(newOrigin); - return [[newOrigin.x, newOrigin.y, newOrigin.z]]; + return [newOrigin]; } computeBoundingBox (worldMatrix: Matrix4): Box3 { const box = this.boundingBox.makeEmpty(); - const inverseWorldMatrix = worldMatrix.clone().inverse(); + const inverseWorldMatrix = worldMatrix.clone().invert(); this.primitives.forEach(prim => { const subbox = prim.computeBoundingBox(inverseWorldMatrix); @@ -288,17 +290,17 @@ export class PMesh extends PEntity { const center = inBounding.center ?? [0, 0, 0]; const size = inBounding.size ?? [0, 0, 0]; const c = Vector3.fromArray(center); - const hs = Vector3.fromArray(size).multiplyScalar(0.5); - const minVector = c.clone().subVector(hs); - const maxVector = c.clone().addVector(hs); + const hs = Vector3.fromArray(size).multiply(0.5); + const minVector = c.clone().subtract(hs); + const maxVector = c.clone().add(hs); return new Box3(minVector, maxVector); } else { const center = inBounding.center ?? [0, 0, 0]; const halfRadius = (inBounding.radius ?? 0) * 0.5; const c = Vector3.fromArray(center); - const minVector = c.clone().subScalar(halfRadius); - const maxVector = c.clone().addScalar(halfRadius); + const minVector = c.clone().subtract(halfRadius); + const maxVector = c.clone().add(halfRadius); return new Box3(minVector, maxVector); } @@ -449,7 +451,8 @@ export class PPrimitive { const shadowVertexShader = this.shadowMaterial.vertexShaderCode; const shaodwFragmentShader = this.shadowMaterial.fragmentShaderCode; const shadowMaterial = Material.create( - this.engine, { + this.engine, + { shader: { vertex: shadowVertexShader, fragment: shaodwFragmentShader, @@ -618,7 +621,7 @@ export class PPrimitive { const shadowMriMaterial = this.getShadowModelMaterial(); if (shadowMriMaterial !== undefined) { - shadowMriMaterial.setMatrix('u_ViewProjectionMatrix', shadowOpts.viewProjectionMatrix.toArray() as spec.mat4); + shadowMriMaterial.setMatrix('u_ViewProjectionMatrix', shadowOpts.viewProjectionMatrix); if (this.shadowMaterial !== undefined) { this.shadowMaterial.updateUniforms(shadowMriMaterial); } @@ -638,7 +641,7 @@ export class PPrimitive { const animMatrices = this.skin.animationMatrices; animMatrices.forEach(mat => { - bindMatrices.push(Matrix4.multiply(invWorldMatrix, mat, new Matrix4())); + bindMatrices.push(new Matrix4().multiplyMatrices(invWorldMatrix, mat)); }); } @@ -663,7 +666,7 @@ export class PPrimitive { const animMatrices = this.skin.animationMatrices; animMatrices.forEach(mat => { - bindMatrices.push(Matrix4.multiply(inverseWorldMatrix, mat, new Matrix4())); + bindMatrices.push(new Matrix4().multiplyMatrices(inverseWorldMatrix, mat)); }); } @@ -705,11 +708,11 @@ export class PPrimitive { const material = this.getModelMaterial(); const shadowMaterial = this.getShadowModelMaterial(); - material.setMatrix('u_ModelMatrix', worldMatrix.toArray() as spec.mat4); - material.setMatrix('u_NormalMatrix', normalMatrix.toArray() as spec.mat4); + material.setMatrix('u_ModelMatrix', worldMatrix); + material.setMatrix('u_NormalMatrix', normalMatrix); if (shadowMaterial !== undefined) { - shadowMaterial.setMatrix('u_ModelMatrix', worldMatrix.toArray() as spec.mat4); - shadowMaterial.setMatrix('u_NormalMatrix', normalMatrix.toArray() as spec.mat4); + shadowMaterial.setMatrix('u_ModelMatrix', worldMatrix); + shadowMaterial.setMatrix('u_NormalMatrix', normalMatrix); } // const skin = this.skin; @@ -760,8 +763,8 @@ export class PPrimitive { private updateUniformsByScene (sceneStates: PSceneStates) { const material = this.getModelMaterial(); - material.setMatrix('u_ViewProjectionMatrix', sceneStates.viewProjectionMatrix.toArray() as spec.mat4); - material.setVector3('u_Camera', sceneStates.cameraPosition.toArray() as spec.vec3); + material.setMatrix('u_ViewProjectionMatrix', sceneStates.viewProjectionMatrix); + material.setVector3('u_Camera', sceneStates.cameraPosition); if (this.isEnableShadow()) { // shadow map 先不支持 // const matrix = sceneStates.lightViewProjectionMatrix?.data ?? [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; @@ -784,32 +787,32 @@ export class PPrimitive { const light = lightList[i]; const intensity = light.visible ? light.intensity : 0; - material.setVector3(`u_Lights[${i}].direction`, light.getWorldDirection().toArray() as spec.vec3); + material.setVector3(`u_Lights[${i}].direction`, light.getWorldDirection()); material.setFloat(`u_Lights[${i}].range`, light.range); - material.setVector3(`u_Lights[${i}].color`, light.color.toArray() as spec.vec3); + material.setVector3(`u_Lights[${i}].color`, light.color); material.setFloat(`u_Lights[${i}].intensity`, intensity); - material.setVector3(`u_Lights[${i}].position`, light.getWorldPosition().toArray() as spec.vec3); + material.setVector3(`u_Lights[${i}].position`, light.getWorldPosition()); material.setFloat(`u_Lights[${i}].innerConeCos`, Math.cos(light.innerConeAngle)); material.setFloat(`u_Lights[${i}].outerConeCos`, Math.cos(light.outerConeAngle)); material.setInt(`u_Lights[${i}].type`, light.lightType); - material.setVector2(`u_Lights[${i}].padding`, light.padding.toArray() as spec.vec2); + material.setVector2(`u_Lights[${i}].padding`, light.padding); } else { - material.setVector3(`u_Lights[${i}].direction`, [0, 0, 0]); + material.setVector3(`u_Lights[${i}].direction`, Vector3.ZERO); material.setFloat(`u_Lights[${i}].range`, 0); - material.setVector3(`u_Lights[${i}].color`, [0, 0, 0]); + material.setVector3(`u_Lights[${i}].color`, Vector3.ZERO); material.setFloat(`u_Lights[${i}].intensity`, 0); - material.setVector3(`u_Lights[${i}].position`, [0, 0, 0]); + material.setVector3(`u_Lights[${i}].position`, Vector3.ZERO); material.setFloat(`u_Lights[${i}].innerConeCos`, 0); material.setFloat(`u_Lights[${i}].outerConeCos`, 0); material.setInt(`u_Lights[${i}].type`, 99999); - material.setVector2(`u_Lights[${i}].padding`, [0, 0]); + material.setVector2(`u_Lights[${i}].padding`, Vector2.ZERO); } } const skybox = sceneStates.skybox; if (skybox !== undefined && skybox.available) { - material.setVector2('u_IblIntensity', [skybox.currentIntensity, skybox.currentReflectionsIntensity]); + material.setVector2('u_IblIntensity', new Vector2(skybox.currentIntensity, skybox.currentReflectionsIntensity)); material.setTexture('u_brdfLUT', skybox.brdfLUT as Texture); if (skybox.diffuseImage !== undefined) { material.setTexture('u_DiffuseEnvSampler', skybox.diffuseImage); @@ -818,7 +821,7 @@ export class PPrimitive { const aliasName = ['l00', 'l1m1', 'l10', 'l11', 'l2m2', 'l2m1', 'l20', 'l21', 'l22']; aliasName.forEach((n, i) => { - material.setVector3(`u_shCoefficients.${n}`, coeffs[i] as spec.vec3); + material.setVector3(`u_shCoefficients.${n}`, Vector3.fromArray(coeffs[i] as spec.vec3)); }); } material.setInt('u_MipCount', skybox.specularMipCount ?? 1); @@ -886,7 +889,7 @@ export class PPrimitive { getWorldBoundingBox (): Box3 { if (this.parent === undefined) { if (this.boundingBox.isEmpty()) { - this.computeBoundingBox(Matrix4.IDENTITY.clone()); + this.computeBoundingBox(Matrix4.fromIdentity()); } return this.boundingBox; @@ -894,10 +897,10 @@ export class PPrimitive { const matrix = this.parent.matrix; if (this.boundingBox.isEmpty()) { - this.computeBoundingBox(matrix.clone().inverse()); + this.computeBoundingBox(matrix.clone().invert()); } - return this.boundingBox.clone().transform(matrix); + return this.boundingBox.clone().applyMatrix4(matrix); } } } diff --git a/plugin-packages/model/src/runtime/object.ts b/plugin-packages/model/src/runtime/object.ts index 137002dde..bab8924b1 100644 --- a/plugin-packages/model/src/runtime/object.ts +++ b/plugin-packages/model/src/runtime/object.ts @@ -1,10 +1,12 @@ -import type { spec, Mesh } from '@galacean/effects'; +import type { spec, Mesh, math } from '@galacean/effects'; import type { ModelVFXItem } from '../plugin/model-vfx-item'; import type { BaseTransform } from '../index'; -import type { Quaternion, Euler, Vector3, Matrix4 } from '../math'; +import type { Quaternion, Euler, Vector3, Matrix4 } from './math'; import { PObjectType, PTransform, PCoordinate } from './common'; import type { PSceneStates } from './scene'; +type Euler = math.Euler; + let objectIndex = 1; export abstract class PObject { diff --git a/plugin-packages/model/src/runtime/scene.ts b/plugin-packages/model/src/runtime/scene.ts index dc8f23f18..f38064acf 100644 --- a/plugin-packages/model/src/runtime/scene.ts +++ b/plugin-packages/model/src/runtime/scene.ts @@ -1,12 +1,11 @@ -import type { CameraOptions, RenderFrame, Mesh, Renderer, Texture, Engine } from '@galacean/effects'; +import type { RenderFrame, Mesh, Renderer, Texture, Engine, math, CameraOptionsEx } from '@galacean/effects'; import { Transform, spec, addItem, removeItem, PLAYER_OPTIONS_ENV_EDITOR } from '@galacean/effects'; -import type { ModelVFXItem, ModelItem } from '../plugin/model-vfx-item'; +import type { ModelItem, ModelVFXItem } from '../plugin/model-vfx-item'; import { PMesh } from './mesh'; import type { PCamera } from './camera'; import { PCameraManager } from './camera'; import { PLight, PLightManager } from './light'; -import type { Vector2 } from '../math'; -import { Vector3, Matrix4, Box3 } from '../math'; +import { Vector3, Matrix4, Box3 } from './math'; import { PSkybox } from './skybox'; import { PTransform, PGlobalState, PObjectType } from './common'; import type { CompositionCache } from './cache'; @@ -15,6 +14,9 @@ import type { PEntity } from './object'; import { WebGLHelper } from '../utility/plugin-helper'; import { TwoStatesSet } from '../utility/ts-helper'; +type Box3 = math.Box3; +type Vector2 = math.Vector2; + export interface PSceneOptions { componentName: string, renderer: Renderer, @@ -238,7 +240,7 @@ export class PSceneManager { removeItem(this.itemList, entity); } - updateDefaultCamera (camera: CameraOptions) { + updateDefaultCamera (camera: CameraOptionsEx) { const effectsTransfrom = new Transform({ ...camera, valid: true, @@ -250,7 +252,7 @@ export class PSceneManager { camera.fov, camera.near, camera.far, - camera.position, + newTransform.getPosition(), newTransform.getRotation(), camera.clipMode as number, ); @@ -378,10 +380,10 @@ export class PSceneManager { const btransparent = WebGLHelper.isTransparentMesh(b); if (atransparent && btransparent) { - const aposition = Vector3.unpack(a.worldMatrix, 12, new Vector3()); - const bposition = Vector3.unpack(b.worldMatrix, 12, new Vector3()); - const anewPos = viewMatrix.multiplyByPoint3(aposition); - const bnewPos = viewMatrix.multiplyByPoint3(bposition); + const aposition = Vector3.fromArray(a.worldMatrix.elements, 12); + const bposition = Vector3.fromArray(b.worldMatrix.elements, 12); + const anewPos = viewMatrix.transformPoint(aposition); + const bnewPos = viewMatrix.transformPoint(bposition); return anewPos.z - bnewPos.z; } else if (atransparent) { @@ -441,13 +443,13 @@ export class PSceneManager { if (mesh.ownerItem) { const transform = mesh.ownerItem.getWorldTransform(); - const worldMatrix = Matrix4.fromArray(transform.getWorldMatrix()); + const worldMatrix = transform.getWorldMatrix(); const meshBox = mesh.computeBoundingBox(worldMatrix); - meshBox.transform(worldMatrix); + meshBox.applyMatrix4(worldMatrix); sceneBox.union(meshBox); } else { - sceneBox.union(mesh.computeBoundingBox(Matrix4.IDENTITY.clone())); + sceneBox.union(mesh.computeBoundingBox(Matrix4.fromIdentity())); } } }); diff --git a/plugin-packages/model/src/runtime/shader-libs/standard/primitive.vert.glsl b/plugin-packages/model/src/runtime/shader-libs/standard/primitive.vert.glsl index 35b96eb06..d0f0fdf24 100644 --- a/plugin-packages/model/src/runtime/shader-libs/standard/primitive.vert.glsl +++ b/plugin-packages/model/src/runtime/shader-libs/standard/primitive.vert.glsl @@ -33,7 +33,10 @@ vsIn vec2 a_UV2; #endif vsOut vec2 v_UVCoord1; + +#ifdef HAS_UV_SET2 vsOut vec2 v_UVCoord2; +#endif #ifdef HAS_VERTEX_COLOR_VEC3 vsIn vec3 a_Color; @@ -127,7 +130,6 @@ void main() #endif // !HAS_NORMALS v_UVCoord1 = vec2(0.0, 0.0); - v_UVCoord2 = vec2(0.0, 0.0); #ifdef HAS_UV_SET1 v_UVCoord1 = a_UV1; diff --git a/plugin-packages/model/src/runtime/shader-libs/standard/textures.vert.glsl b/plugin-packages/model/src/runtime/shader-libs/standard/textures.vert.glsl index 3ffaef6c3..7c3e44f22 100644 --- a/plugin-packages/model/src/runtime/shader-libs/standard/textures.vert.glsl +++ b/plugin-packages/model/src/runtime/shader-libs/standard/textures.vert.glsl @@ -1,5 +1,8 @@ fsIn vec2 v_UVCoord1; + +#ifdef HAS_UV_SET2 fsIn vec2 v_UVCoord2; +#endif // General Material #ifdef HAS_NORMAL_MAP @@ -76,7 +79,9 @@ vec2 getNormalUV() { vec3 uv = vec3(v_UVCoord1, 1.0); #ifdef HAS_NORMAL_MAP + #ifdef HAS_UV_SET2 uv.xy = u_NormalUVSet < 1 ? v_UVCoord1 : v_UVCoord2; + #endif #ifdef HAS_NORMAL_UV_TRANSFORM uv *= u_NormalUVTransform; #endif @@ -88,7 +93,9 @@ vec2 getEmissiveUV() { vec3 uv = vec3(v_UVCoord1, 1.0); #ifdef HAS_EMISSIVE_MAP + #ifdef HAS_UV_SET2 uv.xy = u_EmissiveUVSet < 1 ? v_UVCoord1 : v_UVCoord2; + #endif #ifdef HAS_EMISSIVE_UV_TRANSFORM uv *= u_EmissiveUVTransform; #endif @@ -101,7 +108,9 @@ vec2 getOcclusionUV() { vec3 uv = vec3(v_UVCoord1, 1.0); #ifdef HAS_OCCLUSION_MAP + #ifdef HAS_UV_SET2 uv.xy = u_OcclusionUVSet < 1 ? v_UVCoord1 : v_UVCoord2; + #endif #ifdef HAS_OCCLUSION_UV_TRANSFORM uv *= u_OcclusionUVTransform; #endif @@ -113,7 +122,9 @@ vec2 getBaseColorUV() { vec3 uv = vec3(v_UVCoord1, 1.0); #ifdef HAS_BASE_COLOR_MAP + #ifdef HAS_UV_SET2 uv.xy = u_BaseColorUVSet < 1 ? v_UVCoord1 : v_UVCoord2; + #endif #ifdef HAS_BASECOLOR_UV_TRANSFORM uv *= u_BaseColorUVTransform; #endif @@ -125,7 +136,9 @@ vec2 getMetallicRoughnessUV() { vec3 uv = vec3(v_UVCoord1, 1.0); #ifdef HAS_METALLIC_ROUGHNESS_MAP + #ifdef HAS_UV_SET2 uv.xy = u_MetallicRoughnessUVSet < 1 ? v_UVCoord1 : v_UVCoord2; + #endif #ifdef HAS_METALLICROUGHNESS_UV_TRANSFORM uv *= u_MetallicRoughnessUVTransform; #endif @@ -137,7 +150,9 @@ vec2 getSpecularGlossinessUV() { vec3 uv = vec3(v_UVCoord1, 1.0); #ifdef HAS_SPECULAR_GLOSSINESS_MAP + #ifdef HAS_UV_SET2 uv.xy = u_SpecularGlossinessUVSet < 1 ? v_UVCoord1 : v_UVCoord2; + #endif #ifdef HAS_SPECULARGLOSSINESS_UV_TRANSFORM uv *= u_SpecularGlossinessUVTransform; #endif @@ -149,7 +164,9 @@ vec2 getDiffuseUV() { vec3 uv = vec3(v_UVCoord1, 1.0); #ifdef HAS_DIFFUSE_MAP + #ifdef HAS_UV_SET2 uv.xy = u_DiffuseUVSet < 1 ? v_UVCoord1 : v_UVCoord2; + #endif #ifdef HAS_DIFFUSE_UV_TRANSFORM uv *= u_DiffuseUVTransform; #endif diff --git a/plugin-packages/model/src/runtime/shadow.ts b/plugin-packages/model/src/runtime/shadow.ts index a2d9edd64..d9f3a8152 100644 --- a/plugin-packages/model/src/runtime/shadow.ts +++ b/plugin-packages/model/src/runtime/shadow.ts @@ -1,6 +1,5 @@ import type { RenderFrame, - spec, Mesh, Material, RenderPass, @@ -8,8 +7,8 @@ import type { Renderer, Engine, } from '@galacean/effects'; -import { glContext, RenderPassAttachmentStorageType } from '@galacean/effects'; -import { Vector3, Matrix4, Vector2, Box3 } from '../math'; +import { glContext, RenderPassAttachmentStorageType, math } from '@galacean/effects'; +import { Vector3, Matrix4, Vector2, Box3 } from './math'; import type { PShadowType } from './common'; import { PObjectType, PLightType, PMaterialType } from './common'; import type { PMesh, PPrimitive } from './mesh'; @@ -19,7 +18,8 @@ import type { PSceneManager, PSceneStates } from './scene'; import { FBOOptions } from '../utility/ri-helper'; import { WebGLHelper } from '../utility/plugin-helper'; import { TwoStatesSet } from '../utility/ts-helper'; -import { MathUtils } from '../math/utilities'; + +type Box3 = math.Box3; export interface PMaterialShadowBaseOptions { name: string, @@ -92,7 +92,7 @@ export class PMaterialShadowFilter extends PMaterialBase { } override updateUniforms (material: Material) { - material.setVector2('u_BlurScale', this.blurScale.toArray() as spec.vec2); + material.setVector2('u_BlurScale', this.blurScale); } } @@ -379,7 +379,7 @@ export class PShadowManager { private updateDirectionalLightViewProjection (lightObj: PLight) { if (!lightObj.isDirectional()) { - this.lightViewProjection.setIdentity(); + this.lightViewProjection.identity(); return; } @@ -387,19 +387,19 @@ export class PShadowManager { const shadowAABB = this.shadowAABB; const viewPosition = shadowAABB.getCenter(new Vector3()); const lightDirection = lightObj.getWorldDirection(); - const viewTarget = viewPosition.clone().addVector(lightDirection); - const viewUp = Vector3.computeUpVector(lightDirection, new Vector3()); + const viewTarget = viewPosition.clone().add(lightDirection); + const viewUp = computeUpVector(lightDirection, new Vector3()); const viewMatrix = new Matrix4().lookAt(viewPosition, viewTarget, viewUp); - const tempAABB = shadowAABB.clone().transform(viewMatrix); + const tempAABB = shadowAABB.clone().applyMatrix4(viewMatrix); const tempCenter = tempAABB.getCenter(new Vector3()); - const halfSize = tempAABB.getSize(new Vector3()).multiplyScalar(0.5); + const halfSize = tempAABB.getSize(new Vector3()).multiply(0.5); halfSize.x = Math.max(halfSize.x, halfSize.y); halfSize.y = Math.max(halfSize.x, halfSize.y); - const tempMin = halfSize.clone().multiplyScalar(-1.001).addVector(tempCenter); - const tempMax = halfSize.clone().multiplyScalar(1.001).addVector(tempCenter); - const projectMatrix = new Matrix4().orth2d( + const tempMin = halfSize.clone().multiply(-1.001).add(tempCenter); + const tempMax = halfSize.clone().multiply(1.001).add(tempCenter); + const projectMatrix = new Matrix4().orthographic( tempMin.x, tempMax.x, tempMin.y, tempMax.y, tempMin.z, tempMax.z @@ -408,25 +408,25 @@ export class PShadowManager { this.lightView.copyFrom(viewMatrix); this.lightProjection.copyFrom(projectMatrix); - return Matrix4.multiply(projectMatrix, viewMatrix, this.lightViewProjection); + return this.lightViewProjection.multiplyMatrices(projectMatrix, viewMatrix); } private updateSpotLightViewProjection (lightObj: PLight) { if (!lightObj.isSpot()) { - this.lightViewProjection.setIdentity(); + this.lightViewProjection.identity(); return; } // const viewPosition = lightObj.getWorldPosition(); const lightDirection = lightObj.direction.clone(); - const dirOffset = lightDirection.clone().multiplyScalar(10); - const viewTarget = viewPosition.clone().addVector(dirOffset); - const viewUp = Vector3.computeUpVector(lightDirection, new Vector3()); - const viewMatrix = new Matrix4().lookAt(viewPosition, viewTarget, viewUp); + const dirOffset = lightDirection.clone().multiply(10); + const viewTarget = viewPosition.clone().add(dirOffset); + const viewUp = computeUpVector(lightDirection, new Vector3()); + const viewMatrix = Matrix4.fromLookAt(viewPosition, viewTarget, viewUp); // estimate real fovy const shadowAABB = this.shadowAABB; - const tempAABB = shadowAABB.clone().transform(viewMatrix); + const tempAABB = shadowAABB.clone().applyMatrix4(viewMatrix); const tempPoints = [ new Vector3(tempAABB.min.x, tempAABB.min.y, tempAABB.min.z), new Vector3(tempAABB.min.x, tempAABB.min.y, tempAABB.max.z), @@ -453,9 +453,9 @@ export class PShadowManager { }); const minTheta = Math.acos(minCosTheta); const fovy = Math.min(minTheta, lightObj.outerConeAngle) * 2; - const projectMatrix = new Matrix4().perspective(fovy, 1, nearPlane, farPlane, false); + const projectMatrix = Matrix4.fromPerspective(fovy, 1, nearPlane, farPlane, false); - return Matrix4.multiply(projectMatrix, viewMatrix, this.lightViewProjection); + return this.lightViewProjection.multiplyMatrices(projectMatrix, viewMatrix); } private getShadowOptions (): PShadowRuntimeOptions { @@ -694,7 +694,7 @@ export class PShadowManager { } else { console.error(`Invalid softness value from shadow init options, ${softness}`); - return MathUtils.clamp(softness, 0, 2); + return math.clamp(softness, 0, 2); } } @@ -806,3 +806,33 @@ export class PShadowManager { } } +function computeUpVector (lookatDir: Vector3, result: Vector3) { + const dir = lookatDir.clone().normalize(); + const absDir = dir.clone().abs(); + const minIndex = getMinComponentIndex(absDir); + + if (minIndex !== 1) { + result.set(0, 1, 0); + } else { + result.set(dir.z, 0, -dir.x); + result.normalize(); + } + + return result; +} + +function getMinComponentIndex (cartesian: Vector3) { + if (cartesian.x < cartesian.y) { + if (cartesian.x < cartesian.z) { + return 0; + } else { + return 2; + } + } else { + if (cartesian.y < cartesian.z) { + return 1; + } else { + return 2; + } + } +} diff --git a/plugin-packages/model/src/runtime/skybox.ts b/plugin-packages/model/src/runtime/skybox.ts index 147ccb043..80b621551 100644 --- a/plugin-packages/model/src/runtime/skybox.ts +++ b/plugin-packages/model/src/runtime/skybox.ts @@ -8,6 +8,7 @@ import type { CompositionCache } from './cache'; import type { PSceneStates } from './scene'; import type { ModelVFXItem } from '../plugin/model-vfx-item'; import { WebGLHelper } from '../utility/plugin-helper'; +import { Vector2, Vector3 } from './math'; export class PSkybox extends PEntity { renderable = true; @@ -88,11 +89,11 @@ export class PSkybox extends PEntity { if (this.visible && this.renderable && this.skyboxMesh !== undefined && this.skyboxMaterial !== undefined) { const camera = sceneStates.camera; const viewMatrix = sceneStates.viewMatrix; - const newProjViewMatrix = camera.getNewProjectionMatrix(camera.fovy).multiply(viewMatrix).inverse(); + const newProjViewMatrix = camera.getNewProjectionMatrix(camera.fovy).multiply(viewMatrix).invert(); const material = this.skyboxMesh.material; this.skyboxMaterial.updateUniforms(material); - material.setMatrix('u_InvViewProjectionMatrix', newProjViewMatrix.toArray() as spec.mat4); + material.setMatrix('u_InvViewProjectionMatrix', newProjViewMatrix); } } @@ -172,7 +173,7 @@ export class PMaterialSkyboxFilter extends PMaterialBase { throw new Error('Setup brdfLUT for skybox at first.'); } - material.setVector2('u_IblIntensity', [2.0, 2.0]); + material.setVector2('u_IblIntensity', new Vector2(2.0, 2.0)); material.setTexture('u_brdfLUT', this.brdfLUT); if (this.diffuseImage !== undefined) { material.setTexture('u_DiffuseEnvSampler', this.diffuseImage); @@ -184,7 +185,7 @@ export class PMaterialSkyboxFilter extends PMaterialBase { const aliasName = ['l00', 'l1m1', 'l10', 'l11', 'l2m2', 'l2m1', 'l20', 'l21', 'l22']; aliasName.forEach((n, i) => { - material.setVector3(`u_shCoefficients.${n}`, coeffs[i] as spec.vec3); + material.setVector3(`u_shCoefficients.${n}`, Vector3.fromArray(coeffs[i] as spec.vec3)); }); } material.setInt('u_MipCount', this.specularMipCount); diff --git a/plugin-packages/model/src/utility/hit-test-helper.ts b/plugin-packages/model/src/utility/hit-test-helper.ts index 0fc917589..7e9ec4d30 100644 --- a/plugin-packages/model/src/utility/hit-test-helper.ts +++ b/plugin-packages/model/src/utility/hit-test-helper.ts @@ -1,44 +1,45 @@ - -import type { Composition, Ray, Region, spec } from '@galacean/effects'; -import { intersectRayBox } from '@galacean/effects'; -import { Matrix4, Vector3, Vector4 } from '../math'; +import type { Composition, Region, spec, math } from '@galacean/effects'; +import type { Ray, Matrix4 } from '../runtime/math'; +import { Vector3 } from '../runtime/math'; import type { ModelItemBounding, ModelItemBoundingBox } from '../index'; import { VFX_ITEM_TYPE_3D } from '../plugin/const'; import type { ModelVFXItem } from '../plugin/model-vfx-item'; import type { PMesh } from '../runtime'; import { PObjectType } from '../runtime/common'; +type Ray = math.Ray; + // 射线与带旋转的包围盒求交 function transformDirection (m: Matrix4, direction: Vector3) { const x = direction.x; const y = direction.y; const z = direction.z; + const me = m.elements; const result = new Vector3(); - result.x = m.data[0] * x + m.data[4] * y + m.data[8] * z; - result.y = m.data[1] * x + m.data[5] * y + m.data[9] * z; - result.z = m.data[2] * x + m.data[6] * y + m.data[10] * z; + result.x = me[0] * x + me[4] * y + me[8] * z; + result.y = me[1] * x + me[5] * y + me[9] * z; + result.z = me[2] * x + me[6] * y + me[10] * z; return result.normalize(); } -function RayIntersectsBoxWithRotation (ray: Ray, matrixData: spec.mat4, bounding: ModelItemBounding) { - const local2World = Matrix4.fromArray(matrixData); - const world2Local = Matrix4.inverse(local2World, new Matrix4()); - - const origin = new Vector4(ray.center[0], ray.center[1], ray.center[2], 1.0); - const direction = new Vector3(ray.direction[0], ray.direction[1], ray.direction[2]); - - const origin2 = Matrix4.multiplyByVector(world2Local, origin, new Vector4()); - const direction2 = transformDirection(world2Local, direction); +function RayIntersectsBoxWithRotation (ray: Ray, matrixData: Matrix4, bounding: ModelItemBounding) { + const local2World = matrixData; + const world2Local = local2World.clone().invert(); - const insersetPoint: any = intersectRayBox([], [origin2.x, origin2.y, origin2.z], [direction2.x, direction2.y, direction2.z], bounding.center as spec.vec3, (bounding as ModelItemBoundingBox).size as spec.vec3); + const newRay = ray.clone().applyMatrix(world2Local); + const boxCenter = Vector3.fromArray(bounding.center!); + const boxHalfSize = Vector3.fromArray((bounding as ModelItemBoundingBox).size!).multiply(0.5); + const boxMin = boxCenter.clone().subtract(boxHalfSize); + const boxMax = boxCenter.clone().add(boxHalfSize); + const intersetPoint = newRay.intersectBox({ min: boxMin, max: boxMax }, new Vector3()); - if (insersetPoint !== null) { - const insersetPointInWorld = Matrix4.multiplyByVector(local2World, new Vector4(insersetPoint[0], insersetPoint[1], insersetPoint[2], 1.0), new Vector4()); + if (intersetPoint !== undefined) { + const insersetPointInWorld = local2World.transformPoint(intersetPoint); - return [[insersetPointInWorld.x, insersetPointInWorld.y, insersetPointInWorld.z]]; + return [insersetPointInWorld]; } else { return; } @@ -115,9 +116,9 @@ const normal = new Vector3(); function RayTriangleTesting (ro: Vector3, rd: Vector3, a: Vector3, b: Vector3, c: Vector3, backfaceCulling: boolean): number | undefined { // Compute the offset origin, edges, and normal. // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h - Vector3.subtract(b, a, edge1); - Vector3.subtract(c, a, edge2); - Vector3.cross(edge1, edge2, normal); + edge1.subtractVectors(b, a); + edge2.subtractVectors(c, a); + normal.crossVectors(edge1, edge2); // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) @@ -137,16 +138,16 @@ function RayTriangleTesting (ro: Vector3, rd: Vector3, a: Vector3, b: Vector3, c return; } - Vector3.subtract(ro, a, diff); + diff.subtractVectors(ro, a); - Vector3.cross(diff, edge2, edge2); + edge2.crossVectors(diff, edge2); const DdQxE2 = sign * rd.dot(edge2); // b1 < 0, no intersection if (DdQxE2 < 0) { return; } - Vector3.cross(edge1, diff, edge1); + edge1.crossVectors(edge1, diff); const DdE1xQ = sign * rd.dot(edge1); // b2 < 0, no intersection if (DdE1xQ < 0) { @@ -174,7 +175,7 @@ function CompositionHitTest (composition: Composition, x: number, y: number): Re return []; } - const o = ray.center; + const o = ray.origin; const d = ray.direction; const nums = regions.map((region, index) => { const p = region.position; @@ -182,7 +183,7 @@ function CompositionHitTest (composition: Composition, x: number, y: number): Re const t: spec.vec3 = [0, 0, 0]; for (let i = 0; i < 3; i++) { - t[i] = (p[i] - o[i]) / d[i]; + t[i] = (p.getElement(i) - o.getElement(i)) / d.getElement(i); } return [index, Math.max(...t)]; diff --git a/plugin-packages/model/src/utility/plugin-helper.ts b/plugin-packages/model/src/utility/plugin-helper.ts index 073c16e4b..e1eee9ad5 100644 --- a/plugin-packages/model/src/utility/plugin-helper.ts +++ b/plugin-packages/model/src/utility/plugin-helper.ts @@ -9,12 +9,12 @@ import type { Texture2DSourceOptionsImage, TextureCubeSourceOptionsImageMipmaps, Engine, + math, } from '@galacean/effects'; import { Player, spec, Transform, - DEG2RAD, glContext, Material, Mesh, @@ -45,8 +45,7 @@ import type { ModelTreeOptions, ModelAnimTrackOptions, } from '../index'; -import type { Box3 } from '../math'; -import { Matrix3, Matrix4, Vector3, Vector4 } from '../math'; +import { Matrix3, Matrix4, Vector3, Vector4, DEG2RAD } from '../runtime/math'; import type { FBOOptions } from './ri-helper'; import type { PMaterialBase } from '../runtime/material'; import type { PImageBufferData } from '../runtime/skybox'; @@ -55,6 +54,7 @@ import { RayTriangleTesting } from './hit-test-helper'; import { PMorph } from '../runtime/animation'; import type { CompositionCache } from '../runtime/cache'; +type Box3 = math.Box3; type VertexArray = Float32Array | Int32Array | Int16Array | Int8Array | Uint32Array | Uint16Array | Uint8Array; export class WebGLHelper { @@ -379,7 +379,7 @@ export class MeshHelper { engine, { name, - worldMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], + worldMatrix: Matrix4.fromIdentity(), material: effectsMaterial, geometry, } @@ -530,15 +530,15 @@ export class PluginHelper { return; } - const res = Matrix3.IDENTITY.clone(); + const res = Matrix3.fromIdentity(); const temp = new Matrix3(); if (transform.offset !== undefined) { - Matrix3.fromColumnMajorArray([ + temp.setFromArray([ 1, 0, 0, 0, 1, 0, transform.offset[0], transform.offset[1], 1, - ], temp); + ]); res.multiply(temp); } @@ -546,24 +546,24 @@ export class PluginHelper { const cosTheta = Math.cos(transform.rotation); const sinTheta = Math.sin(transform.rotation); - Matrix3.fromColumnMajorArray([ + temp.setFromArray([ cosTheta, sinTheta, 0, -sinTheta, cosTheta, 0, 0, 0, 1, - ], temp); + ]); res.multiply(temp); } if (transform.scale !== undefined) { - Matrix3.fromColumnMajorArray([ + temp.setFromArray([ transform.scale[0], 0, 0, 0, transform.scale[1], 0, 0, 0, 1, - ], temp); + ]); res.multiply(temp); } - Matrix3.transpose(res, res); + res.transpose(); return res; } @@ -587,17 +587,16 @@ export class PluginHelper { static focusOnPoint (cameraPosition: spec.vec3, YRotationAngle: number, targetPoint: spec.vec3) { const camPos = Vector3.fromArray(cameraPosition); const targetPos = Vector3.fromArray(targetPoint); - const deltaPos = new Vector3().copyFrom(camPos).subVector(targetPos); - const rotationMat = Matrix4.IDENTITY.clone().rotate(YRotationAngle * DEG2RAD, new Vector3(0, 1, 0)) as Matrix4; + const deltaPos = new Vector3().copyFrom(camPos).subtract(targetPos); + const rotationMat = Matrix4.fromRotationAxis(new Vector3(0, 1, 0), YRotationAngle * DEG2RAD); - rotationMat.multiplyByPoint3(deltaPos); - const newCamPos = deltaPos.clone().addVector(targetPos); - const viewMatrix = new Matrix4().lookAt(newCamPos, targetPos, new Vector3(0, 1, 0)).inverse(); + rotationMat.transformPoint(deltaPos); + const newCamPos = deltaPos.clone().add(targetPos); + const viewMatrix = new Matrix4().lookAt(newCamPos, targetPos, new Vector3(0, 1, 0)).invert(); const effectsTransform = new Transform(); effectsTransform.setValid(true); - - effectsTransform.cloneFromMatrix(viewMatrix.toArray() as spec.mat4); + effectsTransform.cloneFromMatrix(viewMatrix); return effectsTransform; } @@ -761,16 +760,20 @@ export class PluginHelper { return; } - let compIndex = 0; - const jsonScene = scene.jsonScene; + const { jsonScene } = scene; + const compIndexSet: Set = new Set(); + + if (jsonScene.compositionId === undefined) { + compIndexSet.add(0); + } jsonScene.compositions.forEach((comp, index) => { - if (comp.id === jsonScene.compositionId) { - compIndex = index; + if (comp.id === jsonScene.compositionId || composition.refCompositionProps.has(comp.id)) { + compIndexSet.add(index); } }); - if (compIndex >= 0 && compIndex < jsonScene.compositions.length) { + compIndexSet.forEach(compIndex => { const sceneComp = jsonScene.compositions[compIndex]; sceneComp.items.forEach((item, itemId) => { @@ -864,7 +867,7 @@ export class PluginHelper { } } }); - } + }); } @@ -1255,14 +1258,14 @@ export class GeometryBoxProxy { const bindMat = this.bindMatrices[jointData[i]]; - Matrix4.mulScalerAddMatrix(skinMat, bindMat, weightData[i], skinMat); + skinMat.addScaledMatrix(bindMat, weightData[i]); } if (!findError) { - const resVec = skinMat.multiplyByVector4(new Vector4(posVec.x, posVec.y, posVec.z, 1.0)); + const resVec = skinMat.transformVector4(new Vector4(posVec.x, posVec.y, posVec.z, 1.0)); if (Math.abs(resVec.w) > 1e-4) { - resVec.multiplyScalar(1.0 / resVec.w); + resVec.multiply(1.0 / resVec.w); posVec.set(resVec.x, resVec.y, resVec.z); } } @@ -1381,11 +1384,11 @@ export class HitTestingProxy { const bindMat = this.bindMatrices[jointData[i]]; - Matrix4.mulScalerAddMatrix(skinMat, bindMat, weightData[i], skinMat); + skinMat.addScaledMatrix(bindMat, weightData[i]); } vec4.set(posData[0], posData[1], posData[2], 1.0); - const resVec = skinMat.multiplyByVector4(vec4); + const resVec = skinMat.transformVector4(vec4); const scale = 1.0 / resVec.w; vec3.set(resVec.x * scale, resVec.y * scale, resVec.z * scale); @@ -1675,7 +1678,7 @@ export class CheckerHelper { for (let i = 0; i < v.inverseBindMatrices.length; i += 16) { for (let j = 0; j < 16; j++) { - mat.data[j] = v.inverseBindMatrices[i + j]; + mat.elements[j] = v.inverseBindMatrices[i + j]; } if (Math.abs(mat.determinant()) < 1e-5) { console.error(`Determinant of inverseBindMatrices is too small ${mat.determinant()}, index ${i / 16}, ${this.stringify(v)}`); diff --git a/plugin-packages/model/src/utility/ri-helper.ts b/plugin-packages/model/src/utility/ri-helper.ts index da6a6f974..b64262cbd 100644 --- a/plugin-packages/model/src/utility/ri-helper.ts +++ b/plugin-packages/model/src/utility/ri-helper.ts @@ -1,4 +1,4 @@ -import type { spec, GeometryProps, Engine } from '@galacean/effects'; +import type { GeometryProps, Engine } from '@galacean/effects'; import { glContext, Geometry, @@ -6,8 +6,8 @@ import { Mesh, RenderPassAttachmentStorageType, } from '@galacean/effects'; -import type { Matrix4, Vector3 } from '../math'; -import { Vector2 } from '../math'; +import type { Matrix4 } from '../runtime/math'; +import { Vector2, Vector3 } from '../runtime/math'; export class FBOOptions { resolution: Vector2; @@ -87,12 +87,12 @@ export class BoxMesh { update (modelMatrix: Matrix4, viewProjMatrix: Matrix4, positions: Float32Array, lineColor: Vector3) { const material = this.mesh.material; - material.setMatrix('u_ModelMatrix', modelMatrix.toArray() as spec.mat4); - material.setMatrix('u_ViewProjectionMatrix', viewProjMatrix.toArray() as spec.mat4); + material.setMatrix('u_ModelMatrix', modelMatrix); + material.setMatrix('u_ViewProjectionMatrix', viewProjMatrix); for (let i = 0; i < positions.length; i += 3) { - material.setVector3(`u_PositionList[${i / 3}]`, [positions[i], positions[i + 1], positions[i + 2]]); + material.setVector3(`u_PositionList[${i / 3}]`, Vector3.fromArray(positions, i)); } - material.setVector3('u_LineColor', lineColor.toArray() as spec.vec3); + material.setVector3('u_LineColor', lineColor); } dispose () { diff --git a/plugin-packages/model/test/src/gesture.spec.ts b/plugin-packages/model/test/src/gesture.spec.ts index fb6a27cf5..af1ed6725 100644 --- a/plugin-packages/model/test/src/gesture.spec.ts +++ b/plugin-packages/model/test/src/gesture.spec.ts @@ -27,19 +27,19 @@ describe('测试CameraGestureHandler对象接口', function () { expect(comp.camera.far).to.eql(5000); expect(comp.camera.near).to.eql(0.1); expect(comp.camera.fov).to.eql(90); - comp.camera.getQuat().forEach((v, i) => { + comp.camera.getQuat().toArray().forEach((v, i) => { expect([0, 0, 0, 1][i]).closeTo(v, 1e-5); }); - expect(comp.camera.position).to.eql([0, 0.3, 1.2]); + expect(comp.camera.position.toArray()).to.eql([0, 0.3, 1.2]); handler.onKeyEvent({ cameraID: 'extra-camera', xAxis: -1, yAxis: 1, }); - comp.camera.getQuat().forEach((v, i) => { + comp.camera.getQuat().toArray().forEach((v, i) => { expect([0, 0, 0, 1][i]).closeTo(v, 1e-5); }); - comp.camera.position.forEach((v, i) => { + comp.camera.position.toArray().forEach((v, i) => { expect([-0.0707106739282608, 0.37071067094802856, 1.2000000476837158][i]).closeTo(v, 1e-5); }); handler.onKeyEvent({ @@ -48,10 +48,10 @@ describe('测试CameraGestureHandler对象接口', function () { zAxis: -1, speed: 10, }); - comp.camera.getQuat().forEach((v, i) => { + comp.camera.getQuat().toArray().forEach((v, i) => { expect([0, 0, 0, 1][i]).closeTo(v, 1e-5); }); - comp.camera.position.forEach((v, i) => { + comp.camera.position.toArray().forEach((v, i) => { expect([-7.141778469085693, 0.37071067094802856, -5.871068000793457][i]).closeTo(v, 1e-5); }); handler.onRotateBegin(100, 100, 512, 512, 'extra-camera'); @@ -59,10 +59,10 @@ describe('测试CameraGestureHandler对象接口', function () { handler.onRotating(150, 60); handler.onRotating(260, 160); handler.onRotateEnd(); - comp.camera.getQuat().forEach((v, i) => { + comp.camera.getQuat().toArray().forEach((v, i) => { expect([-0.1283985823392868, -0.35647067427635193, -0.04952879995107651, 0.9241154789924622][i]).closeTo(v, 1e-5); }); - comp.camera.position.forEach((v, i) => { + comp.camera.position.toArray().forEach((v, i) => { expect([-7.141778469085693, 0.37071067094802856, -5.871068000793457][i]).closeTo(v, 1e-5); }); handler.onKeyEvent({ @@ -77,10 +77,10 @@ describe('测试CameraGestureHandler对象接口', function () { yAxis: -1, speed: 12, }); - comp.camera.getQuat().forEach((v, i) => { + comp.camera.getQuat().toArray().forEach((v, i) => { expect([-0.1283985823392868, -0.35647067427635193, -0.04952879995107651, 0.9241154789924622][i]).closeTo(v, 1e-5); }); - comp.camera.position.forEach((v, i) => { + comp.camera.position.toArray().forEach((v, i) => { expect([-5.027445316314697, -0.8084001541137695, 4.431324005126953][i]).closeTo(v, 1e-5); }); }); @@ -97,10 +97,10 @@ describe('测试CameraGestureHandler对象接口', function () { expect(comp.camera.far).to.eql(5000); expect(comp.camera.near).to.eql(0.1); expect(comp.camera.fov).to.eql(90); - comp.camera.getQuat().forEach((v, i) => { + comp.camera.getQuat().toArray().forEach((v, i) => { expect([0, 0, 0, 1][i]).closeTo(v, 1e-5); }); - expect(comp.camera.position).to.eql([0, 0.3, 1.2]); + expect(comp.camera.position.toArray()).to.eql([0, 0.3, 1.2]); handler.onRotatePointBegin(200, 180, 512, 512, [12, 34, 56], 'extra-camera'); handler.onRotatingPoint(260, 230); handler.onRotatingPoint(360, 430); @@ -112,10 +112,10 @@ describe('测试CameraGestureHandler对象接口', function () { yAxis: 1, speed: 15, }); - comp.camera.getQuat().forEach((v, i) => { + comp.camera.getQuat().toArray().forEach((v, i) => { expect([-0.41165038943, -0.5583808422088, 0.6160783171653, -0.3730981945991][i]).closeTo(v, 1e-5); }); - comp.camera.position.forEach((v, i) => { + comp.camera.position.toArray().forEach((v, i) => { expect([4.380855560302, 90.79966735839, 83.62515258789][i]).closeTo(v, 1e-5); }); handler.onKeyEvent({ @@ -130,10 +130,10 @@ describe('测试CameraGestureHandler对象接口', function () { zAxis: 1, speed: 7, }); - comp.camera.getQuat().forEach((v, i) => { + comp.camera.getQuat().toArray().forEach((v, i) => { expect([-0.4116503894329071, -0.5583808422088623, 0.6160783171653748, -0.3730981945991516][i]).closeTo(v, 1e-5); }); - comp.camera.position.forEach((v, i) => { + comp.camera.position.toArray().forEach((v, i) => { expect([14.007980346679688, 84.97274780273438, 84.22966766357422][i]).closeTo(v, 1e-5); }); handler.onKeyEvent({ @@ -154,13 +154,247 @@ describe('测试CameraGestureHandler对象接口', function () { handler.onRotatingPoint(370, 420); handler.onRotatingPoint(420, 370); handler.onRotatePointEnd(); - comp.camera.getQuat().forEach((v, i) => { + comp.camera.getQuat().toArray().forEach((v, i) => { expect([0.115898996591568, 0.8160297870635986, -0.5383279919624329, 0.17568668723106384][i]).closeTo(v, 1e-5); }); - comp.camera.position.forEach((v, i) => { + comp.camera.position.toArray().forEach((v, i) => { expect([50.17472457885742, 65.73796081542969, 79.31233215332031][i]).closeTo(v, 1e-5); }); }); + + it('测试其他功能', async function () { + const comp = await createComposition({ pauseOnFirstFrame: true }); + const handler = new CameraGestureHandlerImp(comp); + + handler.onFocusPoint('extra-camera', [10, 20, 30]); + + comp.camera.position.toArray().forEach((v, i) => { + expect([10, 20, 35][i]).closeTo(v, 1e-5); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([0, 0, 0, 1][i]).closeTo(v, 1e-5); + }); + + handler.onRotateBegin(12, 60, 1200, 1300, 'extra-camera'); + handler.onRotating(15, 62, 1.5); + + comp.camera.position.toArray().forEach((v, i) => { + expect([10, 20, 35][i]).closeTo(v, 1e-5); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([-0.0018124483758583665, -0.002945234067738056, -0.000005338116807251936, 0.9999939799308777][i]).closeTo(v, 1e-5); + }); + + handler.onRotating(40, 200, 2.5); + handler.onRotating(150, 350); + + comp.camera.position.toArray().forEach((v, i) => { + expect([10, 20, 35][i]).closeTo(v, 1e-5); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([-0.2574109137058258, -0.13042953610420227, -0.03508928790688515, 0.9568158388137817][i]).closeTo(v, 1e-5); + }); + + handler.onRotating(130, 223, 1.0); + handler.onRotateEnd(); + + comp.camera.position.toArray().forEach((v, i) => { + expect([10, 20, 35][i]).closeTo(v, 1e-5); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([-0.09802468866109848, -0.07678026705980301, -0.007585614919662476, 0.9921886920928955][i]).closeTo(v, 1e-5); + }); + + handler.onFocusPoint('extra-camera', [-5.5, 62, 70]); + + comp.camera.position.toArray().forEach((v, i) => { + expect([-6.254369258880615, 62.97841262817383, 74.84495544433594][i]).closeTo(v, 1e-5); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([-0.09802468866109848, -0.07678026705980301, -0.007585614919662476, 0.9921886920928955][i]).closeTo(v, 1e-5); + }); + + handler.onXYMoveBegin(356, 666, 1234, 877, 'extra-camera'); + handler.onXYMoving(300, 777, 1.2); + handler.onXYMoving(220, 677); + + comp.camera.position.toArray().forEach((v, i) => { + expect([-4.233689308166504, 63.14022445678711, 75.12689971923828][i]).closeTo(v, 1e-4); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([-0.09802468866109848, -0.07678026705980301, -0.007585614919662476, 0.9921886920928955][i]).closeTo(v, 1e-5); + }); + + handler.onXYMoving(556, 423, 10.0); + + comp.camera.position.toArray().forEach((v, i) => { + expect([-2055.599609375, -2320.04296875, 236.99668884277344][i]).closeTo(v, 1e-4); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([-0.09802468866109848, -0.07678026705980301, -0.007585614919662476, 0.9921886920928955][i]).closeTo(v, 1e-5); + }); + + handler.onXYMoving(897, 999, 0.1); + handler.onXYMoveEnd(); + + comp.camera.position.toArray().forEach((v, i) => { + expect([-58.70777130126953, 95.63462829589844, 60.083106994628906][i]).closeTo(v, 1e-4); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([-0.09802468866109848, -0.07678026705980301, -0.007585614919662476, 0.9921886920928955][i]).closeTo(v, 1e-5); + }); + + handler.onFocusPoint('extra-camera', [-30, -90, -40]); + + comp.camera.position.toArray().forEach((v, i) => { + expect([-30.754369735717773, -89.0215835571289, -35.1550407409668][i]).closeTo(v, 1e-5); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([-0.09802468866109848, -0.07678026705980301, -0.007585614919662476, 0.9921886920928955][i]).closeTo(v, 1e-5); + }); + + handler.onZMoveBegin(532, 877, 2354, 999, 'extra-camera'); + handler.onZMoving(423, 754, 1.0); + handler.onZMoving(377, 855, 5.0); + + comp.camera.position.toArray().forEach((v, i) => { + expect([-14.158243179321289, -110.54669189453125, -141.744140625][i]).closeTo(v, 1e-5); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([-0.09802468866109848, -0.07678026705980301, -0.007585614919662476, 0.9921886920928955][i]).closeTo(v, 1e-5); + }); + + handler.onZMoving(777, 666); + handler.onZMoving(1542, 200, 3.0); + handler.onZMoveEnd(); + + comp.camera.position.toArray().forEach((v, i) => { + expect([275.67047119140625, -486.4533996582031, -2003.177490234375][i]).closeTo(v, 5e-4); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([-0.09802468866109848, -0.07678026705980301, -0.007585614919662476, 0.9921886920928955][i]).closeTo(v, 1e-5); + }); + + handler.onRotatePointBegin(765, 254, 1358, 1245, [34, 156, 256], 'extra-camera'); + handler.onRotatingPoint(687, 333); + handler.onRotatingPoint(555, 333); + comp.camera.position.toArray().forEach((v, i) => { + expect([-263.5001525878906, -2097.41015625, -383.27386474609375][i]).closeTo(v, 5e-4); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([0.22716321051120758, 0.5978137254714966, 0.74696946144104, -0.18180298805236816][i]).closeTo(v, 1e-5); + }); + handler.onRotatingPoint(999, 1001); + handler.onRotatePointEnd(); + + comp.camera.position.toArray().forEach((v, i) => { + expect([2215.251708984375, -658.2001953125, -136.75177001953125][i]).closeTo(v, 5e-4); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([0.607955276966095, -0.16751287877559662, -0.7645838260650635, -0.13319708406925201][i]).closeTo(v, 1e-5); + }); + + handler.onFocusPoint('extra-camera', [28.5, -70.6, -60], 3); + + comp.camera.position.toArray().forEach((v, i) => { + expect([25.844877243041992, -69.34566497802734, -59.38602066040039][i]).closeTo(v, 1e-5); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([0.607955276966095, -0.16751287877559662, -0.7645838260650635, -0.13319708406925201][i]).closeTo(v, 1e-5); + }); + + handler.onFocusPoint('extra-camera', [-18.5, 130.6, 60]); + + comp.camera.position.toArray().forEach((v, i) => { + expect([-22.92520523071289, 132.69056701660156, 61.02329635620117][i]).closeTo(v, 1e-4); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([0.607955276966095, -0.16751287877559662, -0.7645838260650635, -0.13319708406925201][i]).closeTo(v, 1e-5); + }); + + handler.onKeyEvent({ + cameraID: 'extra-camera', + xAxis: 5.6, + speed: 3.0, + }); + + comp.camera.position.toArray().forEach((v, i) => { + expect([-23.601099014282227, 132.69056701660156, 58.100425720214844][i]).closeTo(v, 1e-4); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([0.607955276966095, -0.16751287877559662, -0.7645838260650635, -0.13319708406925201][i]).closeTo(v, 1e-5); + }); + + handler.onKeyEvent({ + cameraID: 'extra-camera', + yAxis: -66.3, + speed: 5.5, + }); + + comp.camera.position.toArray().forEach((v, i) => { + expect([-21.360610961914, 137.68673706054, 57.58232879638][i]).closeTo(v, 1e-5); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([0.607955276966095, -0.16751287877559662, -0.7645838260650635, -0.13319708406925201][i]).closeTo(v, 1e-5); + }); + + handler.onKeyEvent({ + cameraID: 'extra-camera', + zAxis: -36.36, + speed: 0.589, + }); + + comp.camera.position.toArray().forEach((v, i) => { + expect([-20.83932113647461, 137.4404754638672, 57.46178436279297][i]).closeTo(v, 1e-4); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([0.607955276966095, -0.16751287877559662, -0.7645838260650635, -0.13319708406925201][i]).closeTo(v, 1e-5); + }); + + handler.onKeyEvent({ + cameraID: 'extra-camera', + xAxis: -5.6, + yAxis: 35.2, + speed: 8.8, + }); + + comp.camera.position.toArray().forEach((v, i) => { + expect([-24.068078994750977, 129.54586791992188, 59.62751007080078][i]).closeTo(v, 1e-5); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([0.607955276966095, -0.16751287877559662, -0.7645838260650635, -0.13319708406925201][i]).closeTo(v, 1e-5); + }); + + handler.onKeyEvent({ + cameraID: 'extra-camera', + yAxis: -66.3, + zAxis: 89.2, + speed: 22.5, + }); + + comp.camera.position.toArray().forEach((v, i) => { + expect([-34.582611083984375, 149.28878784179688, 62.058921813964844][i]).closeTo(v, 1e-5); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([0.607955276966095, -0.16751287877559662, -0.7645838260650635, -0.13319708406925201][i]).closeTo(v, 1e-5); + }); + + handler.onKeyEvent({ + cameraID: 'extra-camera', + xAxis: 5.6, + yAxis: -9.65, + zAxis: 36.36, + speed: 12.0, + }); + + comp.camera.position.toArray().forEach((v, i) => { + expect([-43.89362716674805, 156.85116577148438, 62.39853286743164][i]).closeTo(v, 1e-5); + }); + comp.camera.getQuat().toArray().forEach((v, i) => { + expect([0.607955276966095, -0.16751287877559662, -0.7645838260650635, -0.13319708406925201][i]).closeTo(v, 1e-5); + }); + + }); }); async function generateCurrentScene () { diff --git a/plugin-packages/model/test/src/index.ts b/plugin-packages/model/test/src/index.ts index 46d52f614..99eba6dcc 100644 --- a/plugin-packages/model/test/src/index.ts +++ b/plugin-packages/model/test/src/index.ts @@ -3,7 +3,6 @@ export * from './model-plugin.spec'; export * from './plugin-unit.spec'; export * from './gesture.spec'; export * from './material.spec'; -export * from './matrix.spec'; export * from './runtime-unit.spec'; export * from './tree.spec'; export * from './memory-leak.spec'; \ No newline at end of file diff --git a/plugin-packages/model/test/src/material.spec.ts b/plugin-packages/model/test/src/material.spec.ts index bdd98b2df..5cc1cd256 100644 --- a/plugin-packages/model/test/src/material.spec.ts +++ b/plugin-packages/model/test/src/material.spec.ts @@ -20,8 +20,8 @@ describe('验证 gltf 与 glb 几何、材质和相机是否解析正确', funct }, }); - result.sceneAABB.max.forEach((v, i) => { expect([371.6921691894531, 115.25850677490234, 135.25839233398438][i]).closeTo(v, 1e-5); }); - result.sceneAABB.min.forEach((v, i) => { expect([-373.2934875488281, -188.28282165527344, -140][i]).closeTo(v, 1e-5); }); + result.sceneAABB.max.forEach((v, i) => { expect([371.6921691894531, 115.25850677490234, 135.25839233398438][i]).closeTo(v, 1e-4); }); + result.sceneAABB.min.forEach((v, i) => { expect([-373.2934875488281, -188.28282165527344, -140][i]).closeTo(v, 4e-5); }); const items = result.items; expect(items.length).to.eql(68); @@ -65,36 +65,56 @@ describe('验证 gltf 与 glb 几何、材质和相机是否解析正确', funct expect(node3.children).to.eql([]); expect(node3.id).to.eql('3'); expect(node3.transform.position).to.eql([-45.315460205078125, -24.617263793945312, -26.320369720458984]); - expect(node3.transform.quat).to.eql([0, 0, 0.014748196117579937, 0.9998912215232849]); - expect(node3.transform.scale).to.eql([1, 1, 1]); + [0, 0, 0.014748196117579937, 0.9998912215232849].forEach((v, i) => { + expect(node3.transform.quat[i]).closeTo(v, 1e-5); + }); + [1, 1, 1].forEach((v, i) => { + expect(node3.transform.scale[i]).closeTo(v, 1e-5); + }); const node18 = treeNodes[18]; expect(node18.children).to.eql([]); expect(node18.id).to.eql('18'); expect(node18.transform.position).to.eql([122.53109741210938, 86.64814758300781, -312.3133850097656]); - expect(node18.transform.quat).to.eql([0.23851503431797028, -0.8046342730522156, 0.45807889103889465, 0.29298120737075806]); - expect(node18.transform.scale).to.eql([1, 1, 1]); + [0.23851503431797028, -0.8046342730522156, 0.45807889103889465, 0.29298120737075806].forEach((v, i) => { + expect(node18.transform.quat[i]).closeTo(v, 1e-5); + }); + [1, 1, 1].forEach((v, i) => { + expect(node18.transform.scale[i]).closeTo(v, 1e-5); + }); const node33 = treeNodes[33]; expect(node33.children).to.eql([]); expect(node33.id).to.eql('33'); expect(node33.transform.position).to.eql([-71.11894989013672, 74.98487091064453, -164.83367919921875]); - expect(node33.transform.quat).to.eql([-0.48719531297683716, 0.23058265447616577, 0.12568430602550507, 0.8328720331192017]); - expect(node33.transform.scale).to.eql([1, 1, 1]); + [-0.48719531297683716, 0.23058265447616577, 0.12568430602550507, 0.8328720331192017].forEach((v, i) => { + expect(node33.transform.quat[i]).closeTo(v, 1e-5); + }); + [1, 1, 1].forEach((v, i) => { + expect(node33.transform.scale[i]).closeTo(v, 1e-5); + }); const node46 = treeNodes[46]; expect(node46.children).to.eql([]); expect(node46.id).to.eql('46'); expect(node46.transform.position).to.eql([49.69811248779297, 257.12298583984375, 94.59911346435547]); - expect(node46.transform.quat).to.eql([-0.9999993443489075, -0, -0.0011350003769621253, -4.4896589918019814e-11]); - expect(node46.transform.scale).to.eql([1, 1, 1]); + [-0.9999993443489075, -0, -0.0011350003769621253, -4.4896589918019814e-11].forEach((v, i) => { + expect(node46.transform.quat[i]).closeTo(v, 1e-5); + }); + [1, 1, 1].forEach((v, i) => { + expect(node46.transform.scale[i]).closeTo(v, 1e-5); + }); const node80 = treeNodes[80]; expect(node80.children).to.eql([]); expect(node80.id).to.eql('80'); expect(node80.transform.position).to.eql([-295, 132.1395263671875, -3.9833459854125977]); - expect(node80.transform.quat).to.eql([-0.7071062922477722, -0.0008329991833306849, 0.7071062922477722, 0.0008333029691129923]); - expect(node80.transform.scale).to.eql([1, 1, 1]); + [-0.7071062922477722, -0.0008329991833306849, 0.7071062922477722, 0.0008333029691129923].forEach((v, i) => { + expect(node80.transform.quat[i]).closeTo(v, 1e-5); + }); + [1, 1, 1].forEach((v, i) => { + expect(node80.transform.scale[i]).closeTo(v, 1e-5); + }); // { const item1 = items[1]; @@ -227,7 +247,9 @@ describe('验证 gltf 与 glb 几何、材质和相机是否解析正确', funct const interaction1 = item.content.interaction; expect(interaction1.type).to.eql(2); expect(interaction1.size).to.eql([21.999984741210938, 21.983718872070312, 85]); - expect(interaction1.center).to.eql([-253.4925537109375, 96.58930206298828, 42.5]); + [-253.4925537109375, 96.58930206298828, 42.5].forEach((v, i) => { + expect(interaction1.center[i]).closeTo(v, 1e-5); + }); expect(item.type).to.eql('mesh'); expect(item.parentId).to.eql('sceneTree^6'); expect(item.id).to.eql('mesh_ni6_mi1'); @@ -307,7 +329,9 @@ describe('验证 gltf 与 glb 几何、材质和相机是否解析正确', funct const interaction1 = item.content.interaction; expect(interaction1.type).to.eql(2); expect(interaction1.size).to.eql([4, 29.99703598022461, 14.998519897460938]); - expect(interaction1.center).to.eql([130.30111694335938, -49.399810791015625, 7.499259948730469]); + [130.30111694335938, -49.399810791015625, 7.499259948730469].forEach((v, i) => { + expect(interaction1.center[i]).closeTo(v, 1e-5); + }); expect(item.type).to.eql('mesh'); expect(item.parentId).to.eql('sceneTree^36'); expect(item.id).to.eql('mesh_ni36_mi6'); @@ -488,7 +512,9 @@ describe('验证 gltf 与 glb 几何、材质和相机是否解析正确', funct expect(node0.children).to.eql([1]); expect(node0.id).to.eql('0'); expect(node0.transform.position).to.eql([0, 0, 0]); - expect(node0.transform.quat).to.eql([-0.7071067690849304, 0, 0, 0.7071067690849304]); + [-0.7071067690849304, 0, 0, 0.7071067690849304].forEach((v, i) => { + expect(node0.transform.quat[i]).closeTo(v, 1e-5); + }); expect(node0.transform.scale).to.eql([1, 1, 1]); } { @@ -496,24 +522,36 @@ describe('验证 gltf 与 glb 几何、材质和相机是否解析正确', funct expect(node0.children).to.eql([3, 2]); expect(node0.id).to.eql('1'); expect(node0.transform.position).to.eql([0, 0, 0]); - expect(node0.transform.quat).to.eql([0, 0, -0.7071067690849304, 0.7071067690849304]); - expect(node0.transform.scale).to.eql([1, 1, 1]); + [0, 0, -0.7071067690849304, 0.7071067690849304].forEach((v, i) => { + expect(node0.transform.quat[i]).closeTo(v, 1e-5); + }); + [1, 1, 1].forEach((v, i) => { + expect(node0.transform.scale[i]).closeTo(v, 1e-5); + }); } { const node0 = treeNodes[7]; expect(node0.children).to.eql([]); expect(node0.id).to.eql('7'); expect(node0.transform.position).to.eql([-0.06681963056325912, -0.001072264974936843, 0.026351310312747955]); - expect(node0.transform.quat).to.eql([0, -0.3269147574901581, 0, 0.9450538158416748]); - expect(node0.transform.scale).to.eql([1, 1, 1]); + [0, -0.3269147574901581, 0, 0.9450538158416748].forEach((v, i) => { + expect(node0.transform.quat[i]).closeTo(v, 1e-5); + }); + [1, 1, 1].forEach((v, i) => { + expect(node0.transform.scale[i]).closeTo(v, 1e-5); + }); } { const node0 = treeNodes[18]; expect(node0.children).to.eql([19]); expect(node0.id).to.eql('18'); expect(node0.transform.position).to.eql([0.01322161965072155, 0.21549950540065765, 0.10933209955692291]); - expect(node0.transform.quat).to.eql([-0, 0.0711694285273552, -0, 0.9974642395973206]); - expect(node0.transform.scale).to.eql([1, 1, 1]); + [-0, 0.0711694285273552, -0, 0.9974642395973206].forEach((v, i) => { + expect(node0.transform.quat[i]).closeTo(v, 1e-5); + }); + [1, 1, 1].forEach((v, i) => { + expect(node0.transform.scale[i]).closeTo(v, 1e-5); + }); } expect(treeOptions.animation).to.eql(0); @@ -652,8 +690,12 @@ describe('验证 gltf 与 glb 几何、材质和相机是否解析正确', funct const item = items[1]; const interaction1 = item.content.interaction; expect(interaction1.type).to.eql(2); - expect(interaction1.size).to.eql([0.3119540214538574, 1.1382739543914795, 1.5065499544143677]); - expect(interaction1.center).to.eql([0.02497699111700058, -8.940696716308594e-8, 0.7532749772071838]); + [0.3119540214538574, 1.1382739543914795, 1.5065499544143677].forEach((v, i) => { + expect(interaction1.size[i]).closeTo(v, 1e-5); + }); + [0.02497699111700058, -8.940696716308594e-8, 0.7532749772071838].forEach((v, i) => { + expect(interaction1.center[i]).closeTo(v, 1e-5); + }); expect(item.type).to.eql('mesh'); expect(item.parentId).to.eql('sceneTree^2'); expect(item.id).to.eql('mesh_ni2_mi0'); @@ -834,15 +876,23 @@ describe('验证 gltf 与 glb 几何、材质和相机是否解析正确', funct expect(node0.children).to.eql([]); expect(node0.id).to.eql('0'); expect(node0.transform.position).to.eql([0, 0, 0]); - expect(node0.transform.quat).to.eql([0.7071068286895752, 0, 0, 0.7071067094802856]); - expect(node0.transform.scale).to.eql([1, 1.0000001192092896, 1.0000001192092896]); + [0.7071068286895752, 0, 0, 0.7071067094802856].forEach((v, i) => { + expect(node0.transform.quat[i]).closeTo(v, 1e-5); + }); + [1, 1.0000001192092896, 1.0000001192092896].forEach((v, i) => { + expect(node0.transform.scale[i]).closeTo(v, 1e-5); + }); } { const item = items[1]; const interaction1 = item.content.interaction; expect(interaction1.type).to.eql(2); - expect(interaction1.size).to.eql([1.8899539709091187, 2, 1.8019688129425049]); - expect(interaction1.center).to.eql([-0.0024815797805786133, -0.18715494871139526, -0.000010520219802856445]); + [1.8899539709091187, 2, 1.8019688129425049].forEach((v, i) => { + expect(interaction1.size[i]).closeTo(v, 1e-5); + }); + [-0.0024815797805786133, -0.18715494871139526, -0.000010520219802856445].forEach((v, i) => { + expect(interaction1.center[i]).closeTo(v, 1e-5); + }); expect(item.type).to.eql('mesh'); expect(item.parentId).to.eql('sceneTree^0'); expect(item.id).to.eql('mesh_ni0_mi0'); diff --git a/plugin-packages/model/test/src/matrix.spec.ts b/plugin-packages/model/test/src/matrix.spec.ts deleted file mode 100644 index 5df6b7363..000000000 --- a/plugin-packages/model/test/src/matrix.spec.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Matrix3, Matrix4, Quaternion, Vector3, PTransform } from '@galacean/effects-plugin-model'; - -const { expect } = chai; - -describe('矩阵测试', () => { - it('测试带镜像加缩放矩阵分解(todo:镜像矩阵如何分解出旋转四元数?)', () => { - const trans0 = new PTransform(); - const expectMatrix = [ - -2.1, 0, 0, 0, - 0, 0.3, 0, 0, - 0, 0, 4.5, 0, - 0, 0, 0, 1, - ]; - - trans0.setScale(new Vector3(-2.1, 0.3, 4.5)); - trans0.getMatrix().toArray().forEach((v, index) => { - expect(v).closeTo(expectMatrix[index], 1e-5); - }); - const decomp = trans0.getMatrix().decompose(); - const scale = Matrix4.getScale(trans0.getMatrix(), new Vector3()); - const rotate = Matrix4.getRotationQuaternion(trans0.getMatrix(), new Quaternion()); - const mat3 = Matrix3.fromQuaternion(rotate, new Matrix3()); - const trans = Matrix4.getTranslation(trans0.getMatrix(), new Vector3()); - - // expect(decomp.translation).to.eql(new Vector3(0, 0, 0)); - // 镜像矩阵无法计算出旋转 - // expect(decomp.rotation).to.eql(new Quaternion(0, 0, 0, 1)); - // 缩放是否应该包含负数? - // expect(decomp.scale).to.eql(new Vector3(-2.1, 0.3, 4.5)); - - }); - - it('测试矩阵分解与合成(与Unity对齐)', function () { - const trans0 = new PTransform(); - - trans0.setPosition(new Vector3(13.5, 2.34, 5.678)); - trans0.setScale(new Vector3(3.78, 2.56, 5.12)); - trans0.setRotation(Quaternion.fromAxisAngle(new Vector3(-8.45, 13.0, 43.2), -Math.PI * 7.43, new Quaternion())); - const expectMatrix = [ - -0.66851407289505, 3.232001543045044, -1.8427306413650513, 0, - -2.5140888690948486, -0.3082773983478546, 0.3713786005973816, 0, - 0.33451002836227417, 2.5825717449188232, 4.408267974853516, 0, - 13.5, 2.34, 5.678, 1, - ]; - - trans0.getMatrix().toArray().forEach((v, index) => { - expect(v).closeTo(expectMatrix[index], 1e-5); - }); - - const decomp0 = trans0.getMatrix().decompose(); - - expect(decomp0.translation).to.eql(new Vector3(13.5, 2.3399999141693115, 5.677999973297119)); - expect(decomp0.rotation).to.eql(new Quaternion(-0.14367972314357758, 0.22104571759700775, 0.7345519661903381, 0.6252426505088806)); - expect(decomp0.scale).to.eql(new Vector3(3.7800002098083496, 2.559999942779541, 5.119999885559082)); - - const trans1 = new PTransform(); - - const res0 = Matrix4.fromArray(expectMatrix); - - //console.log(expectMatrix); - //console.log(res0.toArray()); - //trans1.matrix = expectMatrix; - //console.log(trans1.matrix.toArray()); - - res0.toArray().forEach((v, index) => { - expect(v).closeTo(expectMatrix[index], 1e-5); - }); - - // trans1.matrix.toArray().forEach((v, index) => { - // expect(v).closeTo(expectMatrix[index], 1e-5); - // }); - - }); -}); diff --git a/plugin-packages/model/test/src/model-plugin.spec.ts b/plugin-packages/model/test/src/model-plugin.spec.ts index 194505119..13d00a7f0 100644 --- a/plugin-packages/model/test/src/model-plugin.spec.ts +++ b/plugin-packages/model/test/src/model-plugin.spec.ts @@ -1,8 +1,10 @@ /* eslint-disable @typescript-eslint/quotes */ // @ts-nocheck -import { Player, HitTestType, spec } from '@galacean/effects'; +import { Player, HitTestType, spec, math } from '@galacean/effects'; import { generateComposition } from './utilities'; +const { Vector3 } = math; + const { expect } = chai; const player = new Player({ @@ -63,18 +65,19 @@ describe('mode plugin test', function () { "plugins": ["model"], "_imgs": { "1": [] }, }; - const comp = await generateComposition(player, scn, {}, { pauseOnFirstFrame: true }); + const comp = await generateComposition(player, scn, {}, { autoplay: false }); const cameraItem = comp.getItemByName('camera'); - let pos = cameraItem.content.transform.getPosition().toArray(); - expect(pos).to.deep.equal([0, 0, 8]); + let pos = cameraItem.content.transform.getPosition(); + + expect(pos.toArray()).to.deep.equal([0, 0, 8]); expect(cameraItem.duration).to.eql(5); - comp.gotoAndPlay(1.9); - pos = cameraItem.content.transform.getPosition().toArray(); - const cp = [0, 0, 0]; + comp.gotoAndStop(1.9); + pos = cameraItem.content.transform.getPosition(); + const cp = new Vector3(); cameraItem.transform.assignWorldTRS(cp); - expect(cp).to.deep.equal([3, 0, 8]); + expect(cp.toArray()).to.deep.equal([3, 0, 8]); expect(pos).to.deep.equal(cp); comp.dispose(); }); @@ -135,6 +138,7 @@ describe('mode plugin test', function () { }; const comp = await generateComposition(player, scn, {}, { pauseOnFirstFrame: true }); const cameraItem = comp.getItemByName('camera'); + let p = cameraItem.getHitTestParams(true); if (p !== undefined) { @@ -147,7 +151,7 @@ describe('mode plugin test', function () { } else if (p.type === HitTestType.sphere) { - expect(p.center).to.deep.equal([0, 0, 0]); + expect(p.center.toArray()).to.deep.equal([0, 0, 0]); expect(p.radius).to.eql(0.2); const ret = comp.hitTest(0, 0, true); @@ -156,7 +160,7 @@ describe('mode plugin test', function () { comp.gotoAndPlay(1.9); p = cameraItem.getHitTestParams(true); - expect(p.center).to.deep.equal([3, 0, 0]); + expect(p.center.toArray()).to.deep.equal([3, 0, 0]); expect(p.radius).to.eql(0.2); expect(comp.hitTest(0, 0, true)).to.be.an('array').length(0); diff --git a/plugin-packages/model/test/src/plugin-unit.spec.ts b/plugin-packages/model/test/src/plugin-unit.spec.ts index 7369d331e..5a329dc69 100644 --- a/plugin-packages/model/test/src/plugin-unit.spec.ts +++ b/plugin-packages/model/test/src/plugin-unit.spec.ts @@ -1,9 +1,11 @@ /* eslint-disable padding-line-between-statements */ // @ts-nocheck -import { Player, VFX_ITEM_TYPE_TREE, Texture, spec, Downloader } from '@galacean/effects'; +import { Player, Texture, spec, Downloader, math } from '@galacean/effects'; +const { Matrix4, Quaternion, Vector3, RAD2DEG } = math; + import type { ModelVFXItem } from '@galacean/effects-plugin-model'; import { - Matrix4, Quaternion, Vector3, PEntity, PObject, PAnimationManager, PMorph, + PEntity, PObject, PAnimationManager, PMorph, PBlendMode, PLightType, PMaterialType, PObjectType, PShadowType, PTransform, PCamera, PLight, PMesh, PSkybox, PCameraManager, PLightManager, PMaterialPBR, PMaterialUnlit, VFX_ITEM_TYPE_3D, RayBoxTesting, RayTriangleTesting, @@ -41,7 +43,7 @@ describe('渲染插件单测', function () { 0, 0, 1, 0, 123, 456, 789, 1, ]); - const decomp0 = trans0.getMatrix().decompose(); + const decomp0 = trans0.getMatrix().getTransform(); expect(decomp0.translation).to.eql(new Vector3(123, 456, 789)); expect(decomp0.rotation).to.eql(new Quaternion(0, 0, 0, 1)); @@ -56,14 +58,14 @@ describe('渲染插件单测', function () { 0, 0, 1, 0, 987, 654, 321, 1, ]); - const decomp1 = trans1.getMatrix().decompose(); + const decomp1 = trans1.getMatrix().getTransform(); expect(decomp1.translation).to.eql(new Vector3(987, 654, 321)); expect(decomp1.rotation).to.eql(new Quaternion(0, 0, 0, 1)); expect(decomp1.scale).to.eql(new Vector3(1, 1, 1)); } { const trans0 = new PTransform(); - trans0.setRotation(new Vector3(-2.1, 0.3, 4.5)); + trans0.setRotation(new Vector3(-2.1, 0.3, 4.5).multiply(RAD2DEG)); trans0.getMatrix().toArray().forEach((v, i) => { expect(v).closeTo([ -0.20138093829154968, -0.9338701963424683, -0.29552021622657776, 0, @@ -74,16 +76,22 @@ describe('渲染插件单测', function () { }); const trans1 = new PTransform(); trans1.setRotation(Quaternion.fromAxisAngle(new Vector3(3, 7, 5), Math.PI * 0.15, new Quaternion())); - expect(trans1.getMatrix().toArray()).to.eql([ - 0.9028250575065613, 0.2767362892627716, -0.32912591099739075, 0, - -0.22158297896385193, 0.9553520679473877, 0.19545689225196838, 0, - 0.368521124124527, -0.10353469103574753, 0.9238358736038208, 0, - 0, 0, 0, 1, - ]); - const decomp1 = trans1.getMatrix().decompose(); + trans1.getMatrix().toArray().forEach((v, i) => { + expect([ + 0.9028250575065613, 0.2767362892627716, -0.32912591099739075, 0, + -0.22158297896385193, 0.9553520679473877, 0.19545689225196838, 0, + 0.368521124124527, -0.10353469103574753, 0.9238358736038208, 0, + 0, 0, 0, 1, + ][i]).closeTo(v, 1e-5); + }); + const decomp1 = trans1.getMatrix().getTransform(); expect(decomp1.translation).to.eql(new Vector3(0, 0, 0)); - expect(decomp1.rotation).to.eql(new Quaternion(0.07687187194824219, 0.17936770617961884, 0.12811978161334991, 0.972369909286499)); - expect(decomp1.scale).to.eql(new Vector3(0.9999999403953552, 1, 1)); + decomp1.rotation.toArray().forEach((v, i) => { + expect([0.07687187194824219, 0.17936770617961884, 0.12811978161334991, 0.972369909286499][i]).closeTo(v, 1e-5); + }); + decomp1.scale.toArray().forEach((v, i) => { + expect([0.9999999403953552, 1, 1][i]).closeTo(v, 1e-5); + }); } { const trans0 = new PTransform(); @@ -97,10 +105,12 @@ describe('渲染插件单测', function () { trans0.getMatrix().toArray().forEach((v, index) => { expect(v).closeTo(expectMatrix[index], 1e-5); }); - const decomp = trans0.getMatrix().decompose(); + const decomp = trans0.getMatrix().getTransform(); expect(decomp.translation).to.eql(new Vector3(0, 0, 0)); expect(decomp.rotation).to.eql(new Quaternion(0, 0, 0, 1)); - expect(decomp.scale).to.eql(new Vector3(2.1, 0.3, 4.5)); + decomp.scale.toArray().forEach((v, i) => { + expect([2.1, 0.3, 4.5][i]).closeTo(v, 1e-5); + }); } { const trans0 = new PTransform(); @@ -116,23 +126,31 @@ describe('渲染插件单测', function () { trans0.getMatrix().toArray().forEach((v, index) => { expect(v).closeTo(expectMatrix[index], 1e-5); }); - const decomp0 = trans0.getMatrix().decompose(); - expect(decomp0.translation).to.eql(new Vector3(13.5, 2.3399999141693115, 5.677999973297119)); + const decomp0 = trans0.getMatrix().getTransform(); + decomp0.translation.toArray().forEach((v, index) => { + expect(v).closeTo([13.5, 2.3399999141693115, 5.677999973297119][index], 1e-6); + }); decomp0.rotation.toArray().forEach((v, index) => { expect(v).closeTo([-0.14367972314357758, 0.22104573249816895, 0.7345519661903381, 0.6252426505088806][index], 1e-6); }); - expect(decomp0.scale).to.eql(new Vector3(3.7800002098083496, 2.559999942779541, 5.119999885559082)); + decomp0.scale.toArray().forEach((v, index) => { + expect(v).closeTo([3.7800002098083496, 2.559999942779541, 5.119999885559082][index], 1e-6); + }); const trans1 = new PTransform(); trans1.setMatrix(Matrix4.fromArray(expectMatrix)); trans1.getMatrix().toArray().forEach((v, index) => { expect(v).closeTo(expectMatrix[index], 1e-5); }); - expect(trans1.getTranslation()).to.eql(new Vector3(13.5, 2.3399999141693115, 5.677999973297119)); + trans1.getTranslation().toArray().forEach((v, i) => { + expect([13.5, 2.3399999141693115, 5.677999973297119][i]).closeTo(v, 1e-5); + }); trans1.getRotation().toArray().forEach((v, index) => { expect(v).closeTo([-0.14367972314357758, 0.22104573249816895, 0.7345519661903381, 0.6252426505088806][index], 1e-6); }); - expect(trans1.getScale()).to.eql(new Vector3(3.7800002098083496, 2.559999942779541, 5.119999885559082)); + trans1.getScale().toArray().forEach((v, index) => { + expect(v).closeTo([3.7800002098083496, 2.559999942779541, 5.119999885559082][index], 1e-6); + }); } }); it('Object和Entity测试', function () { @@ -184,7 +202,9 @@ describe('渲染插件单测', function () { expect(light1.name).to.eql('light1'); expect(light1.visible).to.eql(false); expect(light1.transform.getPosition().toArray()).to.eql([123, 456, 789]); - expect(light1.transform.getRotation()).to.eql(new Quaternion(-0.0833304226398468, -0.06886117160320282, -0.02214544080197811, 0.9938932657241821)); + light1.transform.getRotation().toArray().forEach((v, index) => { + expect(v).closeTo([-0.0833304226398468, -0.06886117160320282, -0.02214544080197811, 0.9938932657241821][index], 1e-6); + }); light1.transform.getScale().toArray().forEach((v, index) => { expect(v).closeTo([3.4, 5.7, 6.9][index], 1e-6); }); @@ -237,7 +257,7 @@ describe('渲染插件单测', function () { expect(light2.innerConeAngle).to.eql(35.8); expect(light2.outerConeAngle).to.eql(49.6); light2.getWorldDirection().toArray().forEach((v, index) => { - expect(v).closeTo([0, 0, -10.199999809265137][index], 1e-5); + expect(v).closeTo([0, 0, -1][index], 1e-5); }); // // @@ -270,7 +290,9 @@ describe('渲染插件单测', function () { light3.transform.getRotation().toArray().forEach((v, index) => { expect(v).closeTo([0.16855104267597198, -0.3602624833583832, -0.5503277778625488, 0.7341259121894836][index], 1e-6); }); - expect(light3.transform.getScale().toArray()).to.eql([1, 1, 1]); + light3.transform.getScale().toArray().forEach((v, index) => { + expect(v).closeTo([1, 1, 1][index], 1e-6); + }); light3.color.toArray().forEach((v, index) => { expect(v).closeTo([128, 0, 177, 33][index] / 255.0, 1e-6); }); @@ -327,7 +349,9 @@ describe('渲染插件单测', function () { camera1.position.toArray().forEach((v, i) => { expect([3.21, 6.54, 9.87][i]).closeTo(v, 1e-6); }); - expect(camera1.scale.toArray()).to.eql([1, 1, 1]); + camera1.scale.toArray().forEach((v, i) => { + expect([1, 1, 1][i]).closeTo(v, 1e-6); + }); camera1.rotation.toArray().forEach((v, i) => { expect([0.3490997552871704, -0.46741336584091187, -0.36707738041877747, 0.7245055437088013][i]).closeTo(v, 1e-6); }); @@ -383,7 +407,9 @@ describe('渲染插件单测', function () { camera2.position.toArray().forEach((v, i) => { expect([13.5, 33.6, 56.2][i]).closeTo(v, 1e-5); }); - expect(camera2.scale.toArray()).to.eql([1, 1, 1]); + camera2.scale.toArray().forEach((v, i) => { + expect([1, 1, 1][i]).closeTo(v, 1e-6); + }); camera2.rotation.toArray().forEach((v, i) => { expect([0.0990457609295845, -0.36964380741119385, -0.23911762237548828, 0.8923990726470947][i]).closeTo(v, 1e-6); }); @@ -393,7 +419,7 @@ describe('渲染插件单测', function () { -0.5000000596046448, 0.866025447845459, 0, 0, 0.6123724579811096, 0.35355344414711, 0.7071068286895752, 0, -25.882360458374023, -53.741127014160156, -30.193462371826172, 1, - ][i]).closeTo(v, 1e-6); + ][i]).closeTo(v, 1e-5); }); camera2.projectionMatrix.toArray().forEach((v, i) => { expect([ @@ -438,7 +464,7 @@ describe('渲染插件单测', function () { 0.254606157541275, 0.962443470954895, -0.09422452002763748, 0, 0.1283072978258133, 0.06295374035835266, 0.9897343516349792, -0, -64.57437896728516, -93.03727722167969, -63.509559631347656, 1, - ][i]).closeTo(v, 1e-6); + ][i]).closeTo(v, 1e-5); }); acam.projectionMatrix.toArray().forEach((v, i) => { expect([ @@ -667,8 +693,12 @@ describe('渲染插件单测', function () { }, }); const sceneAABB = loadResult.sceneAABB; - expect(sceneAABB.max).to.eql([0.569136917591095, 1.5065498352050781, 0.18095403909683228]); - expect(sceneAABB.min).to.eql([-0.5691370964050293, -6.193791257658177e-9, -0.13100001215934753]); + [0.569136917591095, 1.5065498352050781, 0.18095403909683228].forEach((v, i) => { + expect(sceneAABB.max[i]).closeTo(v, 1e-5); + }); + [-0.5691370964050293, -6.193791257658177e-9, -0.13100001215934753].forEach((v, i) => { + expect(sceneAABB.min[i]).closeTo(v, 1e-5); + }); // const itemList = loadResult.items; expect(itemList.length).to.eql(2); @@ -717,7 +747,9 @@ describe('渲染插件单测', function () { expect(mesh.primitives.length).to.eql(1); expect(mesh.hide).to.eql(false); expect(mesh.priority).to.eql(priority); - expect(mesh.boundingBox.max.toArray()).to.eql([0.1809540092945099, 0.5691368579864502, 1.5065499544143677]); + mesh.boundingBox.max.toArray().forEach((v, i) => { + expect(v).closeTo([0.1809540092945099, 0.5691368579864502, 1.5065499544143677][i], 1e-5); + }); expect(mesh.boundingBox.min.toArray()).to.eql([-0.13100001215934753, -0.5691370964050293, 0]); expect(mesh.mriMeshs.length).to.eql(1); const skin = mesh.skin; @@ -967,11 +999,11 @@ describe('渲染插件单测', function () { const items = comp.items; expect(items.length).to.eql(2); const treeItem = items[0]; - expect(treeItem.type).to.eql(VFX_ITEM_TYPE_TREE); + expect(treeItem.type).to.eql(spec.ItemType.tree); expect(treeItem.id).to.eql('tree0'); expect(treeItem.name).to.eql('tree0'); const treeWorldMatrix = treeItem.transform.getWorldMatrix(); - treeWorldMatrix.forEach((v, i) => { + treeWorldMatrix.elements.forEach((v, i) => { expect([ 1, 0, 0, 0, 0, 1, 0, 0, @@ -1119,7 +1151,7 @@ describe('渲染插件单测', function () { 0, 0.9999999403953552, 3.422854177870249e-8, 0, 0, 0, 0, 1, ].forEach((val, idx) => { - expect(val).closeTo(worldMatrix[idx], 1e-5); + expect(val).closeTo(worldMatrix.elements[idx], 1e-5); }); const skin = modelItem.options.content.options.skin; expect(skin.inverseBindMatrices.length).to.eql(304); @@ -1305,20 +1337,20 @@ describe('渲染插件单测', function () { const t3 = RayTriangleTesting(new Vector3(0, 0, 0), new Vector3(1, 1, 1).normalize(), a, b, c, false); expect(t3).closeTo(1.1547005591040844, 1e-5); - b.multiplyScalar(-1); + b.multiply(-1); const t4 = RayTriangleTesting(new Vector3(0, 0, 0), new Vector3(1, 1, 1).normalize(), a, b, c, false); expect(t4).to.eql(undefined); - a.multiplyScalar(-1); + a.multiply(-1); const t5 = RayTriangleTesting(new Vector3(0, 0, 0), new Vector3(1, 1, 1).normalize(), a, b, c, false); expect(t5).to.eql(undefined); - b.multiplyScalar(-1); + b.multiply(-1); const t6 = RayTriangleTesting(new Vector3(0, 0, 0), new Vector3(1, 1, 1).normalize(), a, b, c, false); expect(t6).to.eql(undefined); - a.multiplyScalar(-1); - b.multiplyScalar(-1); + a.multiply(-1); + b.multiply(-1); const t7 = RayTriangleTesting(new Vector3(0, 0, 0), new Vector3(-1, -1, -1).normalize(), a, b, c, false); expect(t7).to.eql(undefined); }); diff --git a/plugin-packages/model/test/src/tree.spec.ts b/plugin-packages/model/test/src/tree.spec.ts index 1603d25b0..d0968a27c 100644 --- a/plugin-packages/model/test/src/tree.spec.ts +++ b/plugin-packages/model/test/src/tree.spec.ts @@ -1,8 +1,10 @@ // @ts-nocheck -import { Player, spec } from '@galacean/effects'; +import { Player, math, spec } from '@galacean/effects'; import { ModelTreeVFXItem } from '@galacean/effects-plugin-model'; import { generateComposition } from './utilities'; +const { Vector3 } = math; + const { expect } = chai; describe('tree item', () => { @@ -51,11 +53,11 @@ describe('tree item', () => { const grandChild = child.children[0]; expect(grandChild.id).to.eql('^r2-2'); - const worldPos = []; + const worldPos = new Vector3(); grandChild.transform.assignWorldTRS(worldPos); - expect(grandChild.transform.position).to.deep.equals([1, 0, 1]); - expect(worldPos).to.deep.equals([2, 0, 1]); + expect(grandChild.transform.position.toArray()).to.deep.equals([1, 0, 1]); + expect(worldPos.toArray()).to.deep.equals([2, 0, 1]); }); it('set node as parent', async () => { @@ -76,7 +78,7 @@ describe('tree item', () => { ], player, ); - const pos = []; + const pos = new Vector3(); const treeItem = comp.items[0]; const root = treeItem.content.nodes[0]; const treeItem1 = comp.items[1]; @@ -91,10 +93,10 @@ describe('tree item', () => { expect(comp.items[2].transform.parentTransform).to.eql(treeItem.content.getNodeById('2').transform, '^2'); comp.items[2].transform.assignWorldTRS(pos); - expect(comp.items[2].transform.position).to.eql([0, 1, 0]); - expect(comp.items[2].transform.parentTransform.position).to.eql([1, 0, 1]); - expect(comp.items[2].transform.parentTransform.parentTransform.position).to.deep.equals([1, 0, 0]); - expect(pos).to.deep.equals([2, 1, 1]); + expect(comp.items[2].transform.position.toArray()).to.eql([0, 1, 0]); + expect(comp.items[2].transform.parentTransform.position.toArray()).to.eql([1, 0, 1]); + expect(comp.items[2].transform.parentTransform.parentTransform.position.toArray()).to.deep.equals([1, 0, 0]); + expect(pos.toArray()).to.deep.equals([2, 1, 1]); }); it('grand children tree', async () => { @@ -129,7 +131,7 @@ describe('tree item', () => { expect(treeItem1.transform.parentTransform).to.eql(treeItem.content.nodes[0].transform); expect(treeItem2.parent).to.eql(treeItem1); expect(treeItem2.transform.parentTransform).to.eql(treeItem1.content.nodes[0].transform); - expect(treeItem2.getCurrentPosition()).to.eql([1, 1, 1]); + expect(treeItem2.getCurrentPosition().toArray()).to.eql([1, 1, 1]); }); it('wrong children index', async () => { diff --git a/plugin-packages/model/vite.config.js b/plugin-packages/model/vite.config.js index ce2c1cb29..34c3b56a0 100644 --- a/plugin-packages/model/vite.config.js +++ b/plugin-packages/model/vite.config.js @@ -35,6 +35,7 @@ export default defineConfig(({ mode }) => { plugins: [ legacy({ targets: ['iOS >= 9'], + modernPolyfills: ['es/global-this'], }), glslInner(), tsconfigPaths(), diff --git a/plugin-packages/orientation-transformer/package.json b/plugin-packages/orientation-transformer/package.json index 6f5fad114..7431bf1ec 100644 --- a/plugin-packages/orientation-transformer/package.json +++ b/plugin-packages/orientation-transformer/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/effects-plugin-orientation-transformer", - "version": "1.0.1", + "version": "1.1.0", "description": "Galacean Effects player orientation transformer plugin", "module": "./dist/index.mjs", "main": "./dist/index.js", diff --git a/plugin-packages/orientation-transformer/src/composition-transformer-acceler.ts b/plugin-packages/orientation-transformer/src/composition-transformer-acceler.ts index 41ee20237..13a95f76f 100644 --- a/plugin-packages/orientation-transformer/src/composition-transformer-acceler.ts +++ b/plugin-packages/orientation-transformer/src/composition-transformer-acceler.ts @@ -48,7 +48,7 @@ export class CompositionTransformerAcceler { const target = this.targets[item.name]; if (target) { - const position = item.transform.position.slice(); + const position = item.transform.position.toArray(); const currentPosition = this.current[item.name]; this.records[item.name] = { diff --git a/plugin-packages/orientation-transformer/vite.config.js b/plugin-packages/orientation-transformer/vite.config.js index 5b3ea19d5..7364adae0 100644 --- a/plugin-packages/orientation-transformer/vite.config.js +++ b/plugin-packages/orientation-transformer/vite.config.js @@ -35,6 +35,7 @@ export default defineConfig(({ mode }) => { plugins: [ legacy({ targets: ['iOS >= 9'], + modernPolyfills: ['es/global-this'], }), glslInner(), tsconfigPaths(), diff --git a/plugin-packages/spine/demo/src/api-test.ts b/plugin-packages/spine/demo/src/api-test.ts index c0940e5dd..03578cb63 100644 --- a/plugin-packages/spine/demo/src/api-test.ts +++ b/plugin-packages/spine/demo/src/api-test.ts @@ -1,3 +1,4 @@ +// @ts-nocheck import type { Camera, JSONValue } from '@galacean/effects'; import { Player } from '@galacean/effects'; import type { FileFormat } from './files'; diff --git a/plugin-packages/spine/package.json b/plugin-packages/spine/package.json index c7e385c23..a5a14c6d4 100644 --- a/plugin-packages/spine/package.json +++ b/plugin-packages/spine/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/effects-plugin-spine", - "version": "1.0.1", + "version": "1.1.0", "description": "Galacean Effects player spine plugin", "module": "./dist/index.mjs", "main": "./dist/index.js", diff --git a/plugin-packages/spine/src/core/Bone.ts b/plugin-packages/spine/src/core/Bone.ts index 6b76cae90..999f68ef6 100644 --- a/plugin-packages/spine/src/core/Bone.ts +++ b/plugin-packages/spine/src/core/Bone.ts @@ -2,7 +2,7 @@ import type { BoneData } from './BoneData'; import { TransformMode } from './BoneData'; import type { Skeleton } from './Skeleton'; import type { Updatable } from './Updatable'; -import type { Vector2 } from '../math/vector2'; +import type { math } from '@galacean/effects'; import { MathUtils } from '../math/math'; /** Stores a bone's current pose. @@ -329,7 +329,7 @@ export class Bone implements Updatable { } /** Transforms a point from world coordinates to the bone's local coordinates. */ - worldToLocal (world: Vector2) { + worldToLocal (world: math.Vector2) { const invDet = 1 / (this.a * this.d - this.b * this.c); const x = world.x - this.worldX, y = world.y - this.worldY; @@ -340,7 +340,7 @@ export class Bone implements Updatable { } /** Transforms a point from the bone's local coordinates to world coordinates. */ - localToWorld (local: Vector2) { + localToWorld (local: math.Vector2) { const x = local.x, y = local.y; local.x = x * this.a + y * this.b + this.worldX; diff --git a/plugin-packages/spine/src/core/Skeleton.ts b/plugin-packages/spine/src/core/Skeleton.ts index 9095e78ff..4d5d598e9 100644 --- a/plugin-packages/spine/src/core/Skeleton.ts +++ b/plugin-packages/spine/src/core/Skeleton.ts @@ -1,3 +1,4 @@ +import { math } from '@galacean/effects'; import type { Attachment } from './attachments'; import { MeshAttachment } from './attachments'; import { PathAttachment } from './attachments'; @@ -12,7 +13,6 @@ import { TransformConstraint } from './TransformConstraint'; import type { Updatable } from './Updatable'; import { Color, ArrayUtils } from '../utils'; import { MathUtils } from '../math/math'; -import { Vector2 } from '../math/vector2'; /** Stores the current pose for a skeleton. * @@ -612,8 +612,8 @@ export class Skeleton { /** Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose as `{ x: number, y: number, width: number, height: number }`. * Note that this method will create temporary objects which can add to garbage collection pressure. Use `getBounds()` if garbage collection is a concern. */ getBoundsRect () { - const offset = new Vector2(); - const size = new Vector2(); + const offset = new math.Vector2(); + const size = new math.Vector2(); this.getBounds(offset, size); @@ -624,7 +624,7 @@ export class Skeleton { * @param offset An output value, the distance from the skeleton origin to the bottom left corner of the AABB. * @param size An output value, the width and height of the AABB. * @param temp Working memory to temporarily store attachments' computed world vertices. */ - getBounds (offset: Vector2, size: Vector2, temp: Array = new Array(2)) { + getBounds (offset: math.Vector2, size: math.Vector2, temp: Array = new Array(2)) { if (!offset) {throw new Error('offset cannot be null.');} if (!size) {throw new Error('size cannot be null.');} const drawOrder = this.drawOrder; diff --git a/plugin-packages/spine/src/core/TransformConstraint.ts b/plugin-packages/spine/src/core/TransformConstraint.ts index ba1c5d4a1..c5308c7e1 100644 --- a/plugin-packages/spine/src/core/TransformConstraint.ts +++ b/plugin-packages/spine/src/core/TransformConstraint.ts @@ -1,9 +1,9 @@ +import { math } from '@galacean/effects'; import type { Bone } from './Bone'; import type { Skeleton } from './Skeleton'; import type { TransformConstraintData } from './TransformConstraintData'; import type { Updatable } from './Updatable'; import { MathUtils } from '../math/math'; -import { Vector2 } from '../math/vector2'; /** Stores the current pose for a transform constraint. A transform constraint adjusts the world transform of the constrained * bones to match that of the target bone. @@ -22,7 +22,7 @@ export class TransformConstraint implements Updatable { mixRotate = 0; mixX = 0; mixY = 0; mixScaleX = 0; mixScaleY = 0; mixShearY = 0; - temp = new Vector2(); + temp = new math.Vector2(); active = false; constructor (data: TransformConstraintData, skeleton: Skeleton) { diff --git a/plugin-packages/spine/src/core/attachments/PointAttachment.ts b/plugin-packages/spine/src/core/attachments/PointAttachment.ts index 336e062da..f27099646 100644 --- a/plugin-packages/spine/src/core/attachments/PointAttachment.ts +++ b/plugin-packages/spine/src/core/attachments/PointAttachment.ts @@ -1,5 +1,5 @@ +import type { math } from '@galacean/effects'; import type { Bone } from '../Bone'; -import type { Vector2 } from '../../math/vector2'; import { MathUtils } from '../../math/math'; import { Color } from '../../utils'; import type { Attachment } from './Attachment'; @@ -23,7 +23,7 @@ export class PointAttachment extends VertexAttachment { super(name); } - computeWorldPosition (bone: Bone, point: Vector2) { + computeWorldPosition (bone: Bone, point: math.Vector2) { point.x = this.x * bone.a + this.y * bone.b + bone.worldX; point.y = this.x * bone.c + this.y * bone.d + bone.worldY; diff --git a/plugin-packages/spine/src/math/vector2.ts b/plugin-packages/spine/src/math/vector2.ts deleted file mode 100644 index 9f6e9a1fa..000000000 --- a/plugin-packages/spine/src/math/vector2.ts +++ /dev/null @@ -1,30 +0,0 @@ -export class Vector2 { - constructor (public x = 0, public y = 0) { - } - - set (x: number, y: number): Vector2 { - this.x = x; - this.y = y; - - return this; - } - - length () { - const x = this.x; - const y = this.y; - - return Math.sqrt(x * x + y * y); - } - - normalize () { - const len = this.length(); - - if (len != 0) { - this.x /= len; - this.y /= len; - } - - return this; - } -} - diff --git a/plugin-packages/spine/src/slot-group.ts b/plugin-packages/spine/src/slot-group.ts index 081b31c58..4268e129e 100644 --- a/plugin-packages/spine/src/slot-group.ts +++ b/plugin-packages/spine/src/slot-group.ts @@ -1,5 +1,5 @@ -import { mat4create } from '@galacean/effects'; -import type { Transform, Texture, mat4, Mesh, Engine } from '@galacean/effects'; +import { math } from '@galacean/effects'; +import type { Transform, Texture, Mesh, Engine } from '@galacean/effects'; import type { Slot, BlendMode } from './core'; import { ClippingAttachment, MeshAttachment, RegionAttachment, SkeletonClipping } from './core'; import type { NumberArrayLike } from './utils'; @@ -46,7 +46,7 @@ export class SlotGroup { /** * 世界变换矩阵 */ - private wm: mat4 = mat4create(); + private wm = math.Matrix4.fromIdentity(); /** * 当前环境,用于 editor */ diff --git a/plugin-packages/spine/src/spine-loader.ts b/plugin-packages/spine/src/spine-loader.ts index 150621cac..7217aa02e 100644 --- a/plugin-packages/spine/src/spine-loader.ts +++ b/plugin-packages/spine/src/spine-loader.ts @@ -189,7 +189,7 @@ export class SpineLoader extends AbstractPlugin { this.slotGroups.length && this.slotGroups.map(slotGroup => { if (slotGroup) { slotGroup.meshToAdd.forEach(mesh => { - renderFrame.addMeshToDefaultRenderPass(mesh); + mesh.getVisible() && renderFrame.addMeshToDefaultRenderPass(mesh); }); slotGroup.resetMeshes(); } diff --git a/plugin-packages/spine/src/spine-mesh.ts b/plugin-packages/spine/src/spine-mesh.ts index 0f5083a61..75fc5ccaa 100644 --- a/plugin-packages/spine/src/spine-mesh.ts +++ b/plugin-packages/spine/src/spine-mesh.ts @@ -1,7 +1,7 @@ -import type { mat4, Texture, Attribute, SharedShaderWithSource, Engine } from '@galacean/effects'; +import type { Texture, Attribute, SharedShaderWithSource, Engine } from '@galacean/effects'; import { - PLAYER_OPTIONS_ENV_EDITOR, Mesh, Geometry, Material, setMaskMode, mat4create, - GLSLVersion, glContext, createShaderWithMarcos, ShaderType, + PLAYER_OPTIONS_ENV_EDITOR, Mesh, Geometry, Material, setMaskMode, + GLSLVersion, glContext, createShaderWithMarcos, ShaderType, math, } from '@galacean/effects'; import type { BlendMode } from './core'; import { SlotGroup } from './slot-group'; @@ -105,7 +105,7 @@ export class SpineMesh { }); material.setTexture('uTexture', this.lastTexture); - material.setMatrix('uModel', mat4create()); + material.setMatrix('uModel', math.Matrix4.fromIdentity()); material.blending = true; material.culling = false; material.depthTest = false; @@ -132,7 +132,7 @@ export class SpineMesh { this.indicesLength += indices.length; } - endUpdate (worldMatrix: mat4) { + endUpdate (worldMatrix: math.Matrix4) { for (let i = this.verticesLength; i < this.vertices.length; i++) { this.vertices[i] = 0; } diff --git a/plugin-packages/spine/src/spine-vfx-item.ts b/plugin-packages/spine/src/spine-vfx-item.ts index b117663ae..b8f906412 100644 --- a/plugin-packages/spine/src/spine-vfx-item.ts +++ b/plugin-packages/spine/src/spine-vfx-item.ts @@ -1,20 +1,14 @@ -import { - HitTestType, - VFXItem, - spec, - vec3MulMat4, - PLAYER_OPTIONS_ENV_EDITOR, - assertExist, -} from '@galacean/effects'; +import { HitTestType, VFXItem, spec, PLAYER_OPTIONS_ENV_EDITOR, assertExist, math } from '@galacean/effects'; import type { HitTestTriangleParams, Engine, Composition, VFXItemProps, BoundingBoxTriangle } from '@galacean/effects'; import type { AnimationStateListener, SkeletonData } from './core'; import { AnimationState, AnimationStateData, Skeleton } from './core'; -import { Vector2 } from './math/vector2'; import { SlotGroup } from './slot-group'; import type { SpineResource } from './spine-loader'; import { createSkeletonData, getAnimationDuration } from './utils'; import type { SpineMesh } from './spine-mesh'; +const { Vector2, Vector3 } = math; + export interface BoundsData { x: number, y: number, @@ -141,24 +135,25 @@ export class SpineVFXItem extends VFXItem { } this.state.apply(this.skeleton); this.resize(); - this._contentVisible = true; this.updateState(0); } override onItemUpdate (dt: number, lifetime: number) { - if (lifetime < 0) { - // 还未开始 隐藏一下 - this.setVisible(false); + if (!this.content || !this.content.meshGroups.length) { + return ; + } + const visible = this.contentVisible; - return; + this.content.meshGroups.map((mesh: SpineMesh) => { + mesh.mesh.setVisible(visible); + }); + if (visible) { + this.updateState(dt / 1000); } - this.setVisible(true); - this.updateState(dt / 1000); } override onEnd () { - if (this.endBehavior === spec.END_BEHAVIOR_DESTROY) { - this.setVisible(false); + if (this.endBehavior === spec.END_BEHAVIOR_DESTROY && this.state) { this.state.clearListeners(); this.state.clearTracks(); } @@ -191,19 +186,19 @@ export class SpineVFXItem extends VFXItem { const wm = this.transform.getWorldMatrix(); const centerX = x + width / 2; const centerY = y + height / 2; - const p0: spec.vec3 = [centerX - width / 2, centerY - height / 2, 0]; - const p1: spec.vec3 = [centerX + width / 2, centerY - height / 2, 0]; - const p2: spec.vec3 = [centerX + width / 2, centerY + height / 2, 0]; - const p3: spec.vec3 = [centerX - width / 2, centerY + height / 2, 0]; + const p0 = new Vector3(centerX - width / 2, centerY - height / 2, 0); + const p1 = new Vector3(centerX + width / 2, centerY - height / 2, 0); + const p2 = new Vector3(centerX + width / 2, centerY + height / 2, 0); + const p3 = new Vector3(centerX - width / 2, centerY + height / 2, 0); - vec3MulMat4(p0, p0, wm); - vec3MulMat4(p1, p1, wm); - vec3MulMat4(p2, p2, wm); - vec3MulMat4(p3, p3, wm); + wm.projectPoint(p0); + wm.projectPoint(p1); + wm.projectPoint(p2); + wm.projectPoint(p3); res.area = [ - [p0, p1, p2], - [p0, p2, p3], + { p0, p1, p2 }, + { p0: p0, p1: p2, p2: p3 }, ]; return res; @@ -379,11 +374,17 @@ export class SpineVFXItem extends VFXItem { const scaleFactor = 1 / width; this.scaleFactor = scaleFactor; - this.transform.setScale(this.startSize * scaleFactor, this.startSize * scaleFactor, scale[2]); + this.transform.setScale(this.startSize * scaleFactor, this.startSize * scaleFactor, scale.z); + } + + override setScale (sx: number, sy: number, sz: number) { + const { x, y, z } = this.transform.scale; + + this.transform.setScale(x * sx, y * sy, z * sz); } getBounds (): BoundsData | undefined { - if (!(this.state && this.skeleton)) { + if (!(this.state && this.skeleton && this.contentVisible)) { return; } this.skeleton.updateWorldTransform(); diff --git a/plugin-packages/spine/vite.config.js b/plugin-packages/spine/vite.config.js index 09f9dfb5e..464e12422 100644 --- a/plugin-packages/spine/vite.config.js +++ b/plugin-packages/spine/vite.config.js @@ -36,6 +36,7 @@ export default defineConfig(({ mode }) => { plugins: [ legacy({ targets: ['iOS >= 9'], + modernPolyfills: ['es/global-this'], }), glslInner(), tsconfigPaths(), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0ece7c739..c2a04015f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -83,6 +83,9 @@ importers: js-yaml: specifier: ^4.1.0 version: 4.1.0 + jsdom: + specifier: ^22.1.0 + version: 22.1.0 lint-staged: specifier: ^11.2.6 version: 11.2.6 @@ -137,6 +140,9 @@ importers: packages/effects-core: dependencies: + '@galacean/effects-math': + specifier: 1.0.0 + version: 1.0.0 '@galacean/effects-specification': specifier: 1.0.0 version: 1.0.0 @@ -1835,6 +1841,11 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true + /@galacean/effects-math@1.0.0: + resolution: {integrity: sha512-fHkFk/UdJ0B/MXRKoXsytYQFmAphtsEWYj7NS2HFixkFMSoKFKu9donBKWnxyeYStcPKxroYWBOWpfMJQzNjlg==} + engines: {node: '>=10.0.0'} + dev: false + /@galacean/effects-specification@1.0.0: resolution: {integrity: sha512-o9EnyCaf2lV2FqeNAS7fPTa7IenqbX6LCPiJpi2PH2ub5yDdhERqTHKClIxS+obAKXX1KwkLRHC64QAZ51K6Gg==} dev: false @@ -2075,6 +2086,11 @@ packages: rollup: 2.79.1 dev: true + /@tootallnate/once@2.0.0: + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + dev: true + /@types/chai-spies@1.0.0: resolution: {integrity: sha1-FELaqSTSqMPyAlPbXl6P3R/U8ik=, tarball: https://registry.npm.alibaba-inc.com/@types/chai-spies/download/@types/chai-spies-1.0.0.tgz} dependencies: @@ -2337,6 +2353,10 @@ packages: through: 2.3.8 dev: true + /abab@2.0.6: + resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} + dev: true + /acorn-jsx@5.3.2(acorn@8.10.0): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -2357,6 +2377,15 @@ packages: hasBin: true dev: true + /agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + dependencies: + debug: 4.3.4(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + dev: true + /aggregate-error@3.1.0: resolution: {integrity: sha1-kmcP9Q9TWb23o+DUDQ7DDFc3aHo=, tarball: https://registry.npm.alibaba-inc.com/aggregate-error/download/aggregate-error-3.1.0.tgz} engines: {node: '>=8'} @@ -2811,6 +2840,13 @@ packages: which: 2.0.2 dev: true + /cssstyle@3.0.0: + resolution: {integrity: sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==} + engines: {node: '>=14'} + dependencies: + rrweb-cssom: 0.6.0 + dev: true + /cwise-compiler@1.1.3: resolution: {integrity: sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==} dependencies: @@ -2838,6 +2874,15 @@ packages: engines: {node: '>= 12'} dev: true + /data-urls@4.0.0: + resolution: {integrity: sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==} + engines: {node: '>=14'} + dependencies: + abab: 2.0.6 + whatwg-mimetype: 3.0.0 + whatwg-url: 12.0.1 + dev: true + /date-fns@2.30.0: resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} engines: {node: '>=0.11'} @@ -2871,6 +2916,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /decimal.js@10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + dev: true + /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true @@ -2904,6 +2953,14 @@ packages: esutils: 2.0.3 dev: true + /domexception@4.0.0: + resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} + engines: {node: '>=12'} + deprecated: Use your platform's native DOMException instead + dependencies: + webidl-conversions: 7.0.0 + dev: true + /dot-prop@5.3.0: resolution: {integrity: sha1-kMzOcIzZzYLMTcjD3dmr3VWyDog=, tarball: https://registry.npm.alibaba-inc.com/dot-prop/download/dot-prop-5.3.0.tgz} engines: {node: '>=8'} @@ -2938,6 +2995,11 @@ packages: strip-ansi: 6.0.1 dev: true + /entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + dev: true + /error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: @@ -3250,6 +3312,15 @@ packages: mime-types: 2.1.35 dev: true + /form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: true + /formdata-polyfill@4.0.10: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} @@ -3488,6 +3559,24 @@ packages: lru-cache: 6.0.0 dev: true + /html-encoding-sniffer@3.0.0: + resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} + engines: {node: '>=12'} + dependencies: + whatwg-encoding: 2.0.0 + dev: true + + /http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + dependencies: + '@tootallnate/once': 2.0.0 + agent-base: 6.0.2 + debug: 4.3.4(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + dev: true + /http-signature@1.2.0: resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==} engines: {node: '>=0.8', npm: '>=1.3.7'} @@ -3497,6 +3586,16 @@ packages: sshpk: 1.17.0 dev: true + /https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + dependencies: + agent-base: 6.0.2 + debug: 4.3.4(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + dev: true + /human-signals@2.1.0: resolution: {integrity: sha1-3JH8ukLk0G5Kuu0zs+ejwC9RTqA=, tarball: https://registry.npm.alibaba-inc.com/human-signals/download/human-signals-2.1.0.tgz} engines: {node: '>=10.17.0'} @@ -3508,6 +3607,13 @@ packages: hasBin: true dev: true + /iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: true + /ignore@5.2.4: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} engines: {node: '>= 4'} @@ -3628,6 +3734,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + dev: true + /is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} dependencies: @@ -3698,6 +3808,44 @@ packages: resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} dev: true + /jsdom@22.1.0: + resolution: {integrity: sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==} + engines: {node: '>=16'} + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + dependencies: + abab: 2.0.6 + cssstyle: 3.0.0 + data-urls: 4.0.0 + decimal.js: 10.4.3 + domexception: 4.0.0 + form-data: 4.0.0 + html-encoding-sniffer: 3.0.0 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.7 + parse5: 7.1.2 + rrweb-cssom: 0.6.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 4.1.3 + w3c-xmlserializer: 4.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + whatwg-url: 12.0.1 + ws: 8.14.2 + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + /jsesc@0.5.0: resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} hasBin: true @@ -4155,6 +4303,10 @@ packages: path-key: 3.1.1 dev: true + /nwsapi@2.2.7: + resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==} + dev: true + /oauth-sign@0.9.0: resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} dev: true @@ -4260,6 +4412,12 @@ packages: lines-and-columns: 1.2.4 dev: true + /parse5@7.1.2: + resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + dependencies: + entities: 4.5.0 + dev: true + /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -4364,6 +4522,10 @@ packages: engines: {node: '>=0.6'} dev: true + /querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + dev: true + /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true @@ -4522,6 +4684,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + dev: true + /resolve-from@4.0.0: resolution: {integrity: sha1-SrzYUq0y3Xuqv+m0DgCjbbXzkuY=, tarball: https://registry.npm.alibaba-inc.com/resolve-from/download/resolve-from-4.0.0.tgz} engines: {node: '>=4'} @@ -4620,6 +4786,10 @@ packages: fsevents: 2.3.2 dev: true + /rrweb-cssom@0.6.0: + resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} + dev: true + /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: @@ -4652,6 +4822,13 @@ packages: through: 2.3.8 dev: true + /saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + dependencies: + xmlchars: 2.2.0 + dev: true + /semver-compare@1.0.0: resolution: {integrity: sha1-De4hahyUGrN+nvsXiPavxf9VN/w=, tarball: https://registry.npm.alibaba-inc.com/semver-compare/download/semver-compare-1.0.0.tgz} dev: true @@ -4928,6 +5105,10 @@ packages: engines: {node: '>= 0.4'} dev: true + /symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + dev: true + /systemjs@6.14.1: resolution: {integrity: sha512-8ftwWd+XnQtZ/aGbatrN4QFNGrKJzmbtixW+ODpci7pyoTajg4sonPP8aFLESAcuVxaC1FyDESt+SpfFCH9rZQ==} dev: true @@ -4985,6 +5166,23 @@ packages: punycode: 2.3.0 dev: true + /tough-cookie@4.1.3: + resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==} + engines: {node: '>=6'} + dependencies: + psl: 1.9.0 + punycode: 2.3.0 + universalify: 0.2.0 + url-parse: 1.5.10 + dev: true + + /tr46@4.1.1: + resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} + engines: {node: '>=14'} + dependencies: + punycode: 2.3.0 + dev: true + /tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -5142,6 +5340,11 @@ packages: engines: {node: '>= 4.0.0'} dev: true + /universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + dev: true + /universalify@2.0.0: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} @@ -5164,6 +5367,13 @@ packages: punycode: 2.3.0 dev: true + /url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + dev: true + /util-deprecate@1.0.2: resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=, tarball: https://registry.npm.alibaba-inc.com/util-deprecate/download/util-deprecate-1.0.2.tgz} dev: true @@ -5264,11 +5474,43 @@ packages: resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==} dev: true + /w3c-xmlserializer@4.0.0: + resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} + engines: {node: '>=14'} + dependencies: + xml-name-validator: 4.0.0 + dev: true + /web-streams-polyfill@3.2.1: resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==} engines: {node: '>= 8'} dev: true + /webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + dev: true + + /whatwg-encoding@2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + dependencies: + iconv-lite: 0.6.3 + dev: true + + /whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + dev: true + + /whatwg-url@12.0.1: + resolution: {integrity: sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==} + engines: {node: '>=14'} + dependencies: + tr46: 4.1.1 + webidl-conversions: 7.0.0 + dev: true + /which@2.0.2: resolution: {integrity: sha1-fGqN0KY2oDJ+ELWckobu6T8/UbE=, tarball: https://registry.npm.alibaba-inc.com/which/download/which-2.0.2.tgz} engines: {node: '>= 8'} @@ -5312,6 +5554,28 @@ packages: optional: true dev: true + /ws@8.14.2: + resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: true + + /xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + dev: true + + /xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + dev: true + /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} diff --git a/scripts/print-changelog/index.js b/scripts/print-changelog/index.js new file mode 100644 index 000000000..345683b79 --- /dev/null +++ b/scripts/print-changelog/index.js @@ -0,0 +1,52 @@ +const chalk = require('chalk'); +const simpleGit = require('simple-git'); +const { Select } = require('enquirer'); +const { fetchPullRequest, queryWithJSDOM, printPR } = require('./utils'); + +const cwd = process.cwd(); +const git = simpleGit(cwd); + +async function main() { + const tags = await git.tags(); + const prompt = new Select({ + name: 'fromVersion', + message: '🏷 Please choose tag to compare with current branch:', + choices: tags.all + .filter((item) => !item.includes('experimental')) + .filter((item) => !item.includes('alpha')) + .filter((item) => !item.includes('resource')) + .reverse() + .slice(0, 50), + }); + const fromVersion = await prompt.run(); + const logs = await git.log({ from: fromVersion, to: 'main' }); + let prList = []; + + for (let i = 0; i < logs.all.length; i += 1) { + const { message, body, hash, author_name: author } = logs.all[i]; + const text = `${message} ${body}`; + const match = text.match(/#\d+/g); + const prs = (match || []).map(pr => pr.slice(1)); + + console.log( + `[${i + 1}/${logs.all.length}]`, + hash.slice(0, 6), + '-', + prs.length ? prs.map(pr => `#${pr}`).join(',') : '?', + ); + + for (let j = 0; j < prs.length; j += 1) { + const pr = prs[j]; + const { response, html } = await fetchPullRequest(pr); + const res = queryWithJSDOM(html); + + prList.push({ ...res, pr, url: response.url }); + } + } + + console.log('\n', chalk.green('Done. Here is the log:')); + console.log('\n'); + printPR(prList); +} + +main(); diff --git a/scripts/print-changelog/utils.js b/scripts/print-changelog/utils.js new file mode 100644 index 000000000..98e5c0046 --- /dev/null +++ b/scripts/print-changelog/utils.js @@ -0,0 +1,86 @@ +const chalk = require('chalk'); +const jsdom = require('jsdom'); +const configConventional = require('@commitlint/config-conventional'); +const commitTypes = configConventional.rules['type-enum'][2]; + +const { JSDOM } = jsdom; + +function printPR(prList) { + prList.forEach(entity => { + const { pr, url, author, title, descriptions = [] } = entity; + const validatePR = commitTypes.some(type => new RegExp(`^${type}(?:\\(\\S+\\))?:\\s.+`, 'ig').test(title.toLocaleLowerCase())); + + // 过滤非 commit types 的 PR + if (!validatePR) { return; } + // skip document + if (title.includes('docs: ')) { return; } + + const titleText = title.replace(title[0], title[0].toLocaleUpperCase()); + const authorText = `@${author}`; + const urlText = `[#${pr}](${url})`; + + console.log(`- ${titleText}. ${urlText} ${authorText}`); + + if (descriptions.length !== 0) { + console.log(descriptions.map(desc => ` - ${desc}`).join('\n')); + } + }); +} + +function queryWithJSDOM(txt) { + const QUERY_TITLE = '.gh-header-title .js-issue-title'; + const QUERY_DESCRIPTION_LINES = '.comment-body ol li'; + const QUERY_AUTHOR = '.pull-discussion-timeline>.js-discussion>.TimelineItem .author'; + const dom = new JSDOM(txt); + const { document } = dom.window; + const prTitle = document.querySelector(QUERY_TITLE).textContent.trim(); + const prAuthor = document.querySelector(QUERY_AUTHOR).textContent.trim(); + const prLines = [...document.querySelectorAll(QUERY_DESCRIPTION_LINES)].map(li => li.textContent.trim()); + + return { + title: prTitle, + author: prAuthor, + descriptions: prLines, + }; +} + +async function fetchPullRequest(pr) { + const timeout = 30000; + let tryTimes = 0; + let response; + let html; + + try { + response = await new Promise((resolve, reject) => { + const timer = setTimeout(() => { + reject(new Error(`Fetch timeout of ${timeout}ms exceeded`)); + }, timeout); + + fetch(`https://github.com/galacean/effects-runtime/pull/${pr}`) + .then(res => { + res.text() + .then(txt => { + html = txt; + clearTimeout(timer); + resolve(res); + }); + }) + .catch(reject); + }); + } catch (e) { + tryTimes++; + if (tryTimes < 3) { + console.log(chalk.red(`❌ Fetch error, reason: ${e}`)); + console.log(chalk.red(`⌛️ Retrying...(Retry times: ${tryTimes})`)); + await fetchPullRequest(pr); + } + } + + return { response, html }; +} + +module.exports = { + fetchPullRequest, + queryWithJSDOM, + printPR, +} diff --git a/web-packages/demo/src/assets/inspire-list.ts b/web-packages/demo/src/assets/inspire-list.ts index 595e6ac7f..bcc062287 100644 --- a/web-packages/demo/src/assets/inspire-list.ts +++ b/web-packages/demo/src/assets/inspire-list.ts @@ -722,5 +722,10 @@ export default { name: '多级父节点', pass: true, }, + preComp: { + url: 'https://gw.alipayobjects.com/os/gltf-asset/97436498253707/pre.json', + name: '预合成', + pass: true, + }, }; diff --git a/web-packages/demo/src/common/three-display-object.ts b/web-packages/demo/src/common/three-display-object.ts index c970bce88..973509694 100644 --- a/web-packages/demo/src/common/three-display-object.ts +++ b/web-packages/demo/src/common/three-display-object.ts @@ -50,7 +50,7 @@ export async function renderbyThreeDisplayObject (player, json) { // 兼容父节点的结束行为销毁时表现为冻结 displayObject.currentComposition.items.forEach(item => { if (item.type === spec.ItemType.null && item.endBehavior === spec.ItemEndBehavior.destroy) { - item.endBehavior = spec.ItemEndBehavior.forward; + item.endBehavior = spec.ItemEndBehavior.freeze; } }); scene.add(displayObject); diff --git a/web-packages/demo/src/common/utils.ts b/web-packages/demo/src/common/utils.ts index fbf7fdc64..c932fa77b 100644 --- a/web-packages/demo/src/common/utils.ts +++ b/web-packages/demo/src/common/utils.ts @@ -1,13 +1,12 @@ import type { Composition } from '@galacean/effects'; -import { spec } from '@galacean/effects'; +import { VFXItem, spec } from '@galacean/effects'; // TODO 不需要和 player 做效果对比时可以移除 export function compatibleCalculateItem (composition: Composition) { // 测试用的兼容 加载好后修改空节点结束行为,保持和player一致,在runtime上空节点结束为销毁改为冻结的效果 composition.items.forEach(item => { - if (item.type === spec.ItemType.null && item.endBehavior === spec.ItemEndBehavior.destroy) { + if (VFXItem.isNull(item) && item.endBehavior === spec.ItemEndBehavior.destroy) { item.endBehavior = spec.ItemEndBehavior.freeze; } }); - } diff --git a/web-packages/demo/src/single.ts b/web-packages/demo/src/single.ts index 158855478..316bb3eff 100644 --- a/web-packages/demo/src/single.ts +++ b/web-packages/demo/src/single.ts @@ -3,7 +3,7 @@ import '@galacean/effects-plugin-spine'; import '@galacean/effects-plugin-model'; import inspireList from './assets/inspire-list'; -const json = inspireList.mask.url; +const json = inspireList.preComp.url; const container = document.getElementById('J-container'); (async () => { @@ -11,8 +11,6 @@ const container = document.getElementById('J-container'); const player = createPlayer(); const comp = await player.loadScene(json); - const item = comp.getItemByName('mask'); - } catch (e) { console.error('biz', e); } @@ -28,8 +26,8 @@ function createPlayer () { onPausedByItem: data => { console.info('onPausedByItem', data); }, - onItemClicked: () => { - + onItemClicked: data => { + console.info(`item ${data.name} has been clicked`); }, // reportGPUTime: console.debug, }); diff --git a/web-packages/demo/vite.config.js b/web-packages/demo/vite.config.js index 093e7a8fd..13db6c94b 100644 --- a/web-packages/demo/vite.config.js +++ b/web-packages/demo/vite.config.js @@ -27,6 +27,7 @@ export default defineConfig(({ mode }) => { 'three-sprite': resolve(__dirname, 'html/three-sprite.html'), 'threejs-large-scene': resolve(__dirname, 'html/threejs-large-scene.html'), }, + context: 'undefined', }, minify: false, // iOS 9 等低版本加载压缩代码报脚本异常 }, diff --git a/web-packages/test/case/2d/index.html b/web-packages/test/case/2d/index.html index 8611d56cf..61f06ef7c 100644 --- a/web-packages/test/case/2d/index.html +++ b/web-packages/test/case/2d/index.html @@ -25,6 +25,7 @@ '__MarsConfig__', 'MarsModel', 'MarsSpine', + 'mars', ] }); mocha.checkLeaks(); diff --git a/web-packages/test/case/2d/src/common/utilities.ts b/web-packages/test/case/2d/src/common/utilities.ts index aef208a04..f756c6478 100644 --- a/web-packages/test/case/2d/src/common/utilities.ts +++ b/web-packages/test/case/2d/src/common/utilities.ts @@ -5,15 +5,17 @@ import { registerPlugin, AbstractPlugin, VFXItem, - vec3MulMat4, spec, - getDefaultTemplateCanvasPool, AssetManager, + math, + AssetManager, + getDefaultTemplateCanvasPool, } from '@galacean/effects'; -const sleepTime = 50; +const { Vector3, Matrix4 } = math; + +const sleepTime = 20; const params = new URLSearchParams(location.search); -const playerVersion = params.get('version') || '1.3.4'; // 旧 Player 版本 -const oldSpineVersion = params.get('version') || '2.0.1'; // 旧 spine 插件版本 +const oldVersion = params.get('version') || '2.3.6'; // 旧版Player版本 const playerOptions: PlayerConfig = { //env: 'editor', //pixelRatio: 2, @@ -25,7 +27,7 @@ const playerOptions: PlayerConfig = { }; export class TestPlayer { - constructor (width, height, playerClass, playerOptions, renderFramework, registerFunc, MarsPlugin, VFXItem, prefetchFunc) { + constructor (width, height, playerClass, playerOptions, renderFramework, registerFunc, Plugin, VFXItem, prefetchFunc, oldVersion) { this.width = width; this.height = height; // @@ -40,11 +42,12 @@ export class TestPlayer { renderFramework, }); this.prefetchFunc = prefetchFunc; + this.oldVersion = oldVersion; this.scene = undefined; this.composition = undefined; this.lastTime = 0; - registerFunc('orientation-transformer', MarsPlugin, VFXItem, true); + registerFunc('orientation-transformer', Plugin, VFXItem, true); } async initialize (url, loadOptions = undefined, playerOptions = undefined) { @@ -53,41 +56,40 @@ export class TestPlayer { } Math.seedrandom('mars-runtime'); - if (this.player.loadScene) { + if (this.oldVersion) { + this.scene = await this.player.loadSceneAsync(url, { ...loadOptions, timeout: 100 }); + Math.seedrandom('mars-runtime'); + this.composition = await this.player.play(this.scene, playerOptions ?? { pauseOnFirstFrame: true }); + } else { getDefaultTemplateCanvasPool().dispose(); - const assetManager = new AssetManager(); + const assetManager = new AssetManager({ ...loadOptions, timeout: 100, autoplay: false }); const json = await assetManager.loadScene(url); compatibleCalculateItem(json.jsonScene.compositions[0]); this.player.destroyCurrentCompositions(); - Math.seedrandom('mars-runtime'); this.composition = this.scene = await this.player.loadScene(json, { ...loadOptions, timeout: 100, autoplay: false }); - this.player.gotoAndStop(0); - } else { - // 旧版Mars调用 - this.scene = await this.player.loadSceneAsync(url, { ...loadOptions, timeout: 100 }); Math.seedrandom('mars-runtime'); - this.composition = await this.player.play(this.scene, playerOptions ?? { pauseOnFirstFrame: true }); + this.player.gotoAndStop(0); } - } - gotoTime (time) { + gotoTime (newtime) { + + let time = newtime; + + // 兼容旧 Player 设置结束行为为重播时在第duration秒会回到第0帧 + if (this.composition.content.endBehavior === 5 && newtime === this.composition.content.duration) { + time -= 0.01; + } const deltaTime = time - this.lastTime; - this.lastTime = time; - // + this.lastTime = newtime; Math.seedrandom(`mars-runtime${time}`); if (this.player.gotoAndStop) { this.player.gotoAndStop(time); - const comp = this.player.getCompositions()[0]; - - if (comp.time === comp.duration && comp.content.endBehavior === 5) { - this.player.gotoAndStop(0); - } } else { this.composition.forwardTime(deltaTime); - this.player.tick(0); + this.player.doTick(0, true); } } @@ -103,7 +105,7 @@ export class TestPlayer { } loadSceneTime () { - return this.scene.totalTime; + return this.composition.statistic.loadTime; } firstFrameTime () { @@ -119,7 +121,7 @@ export class TestPlayer { } duration () { - return this.composition.duration; + return this.composition.content.duration; } isLoop () { @@ -129,12 +131,15 @@ export class TestPlayer { getRandomPointInParticle () { const itemList = []; let viewProjection; - const inPosition = [0, 0, 0]; + const inPosition = new Vector3(0, 0, 0); if (this.composition._camera) { - viewProjection = this.composition._camera.viewProjection; + viewProjection = Matrix4.fromArray(this.composition._camera.viewProjection); } else { viewProjection = this.composition.camera.getViewProjectionMatrix(); + if (ArrayBuffer.isView(viewProjection)) { + viewProjection = Matrix4.fromArray(viewProjection); + } } this.composition.items.forEach(item => { @@ -163,14 +168,14 @@ export class TestPlayer { if (typeof itemList[index].getParticleBoxes === 'function') { const pos = item.getParticleBoxes().reverse()[subIndex].center; - vec3MulMat4(inPosition, pos, viewProjection); + viewProjection.projectPoint(pos, inPosition); } else { const pos = mesh.getPointPosition(subIndex); - vec3MulMat4(inPosition, pos, viewProjection); + viewProjection.projectPoint(pos, inPosition); } - return [inPosition[0], inPosition[1]]; + return [inPosition.x, inPosition.y]; } } @@ -212,29 +217,27 @@ export class TestPlayer { export class TestController { constructor () { this.renderFramework = 'webgl'; - this.marsPlayer = undefined; - this.runtimePlayer = undefined; + this.oldPlayer = undefined; + this.newPlayer = undefined; } async createPlayers (width, height, renderFramework, isEditor = true) { const playerScript = await this.loadOldPlayer(); - const filterScript = await this.loadOldFilters(); const modelPlugin = await this.loadOldModelPlugin(); const spinePlugin = await this.loadOldSpinePlugin(); playerOptions.env = isEditor ? 'editor' : ''; this.renderFramework = renderFramework; - if (window.Mars.MarsPlayer) { - window.Mars.registerFilters(window.MarsFilters.filters); - this.marsPlayer = new TestPlayer( - width, height, window.Mars.MarsPlayer, playerOptions, renderFramework, - window.Mars.registerPlugin, window.Mars.MarsPlugin, window.Mars.VFXItem, - window.Mars.loadSceneAsync + if (window.mars.MarsPlayer) { + this.oldPlayer = new TestPlayer( + width, height, window.mars.MarsPlayer, playerOptions, renderFramework, + window.mars.registerPlugin, window.mars.AbstractPlugin, window.mars.VFXItem, + null, true ); - this.runtimePlayer = new TestPlayer( + this.newPlayer = new TestPlayer( width, height, Player, playerOptions, renderFramework, - registerPlugin, AbstractPlugin, VFXItem, null + registerPlugin, AbstractPlugin, VFXItem, null, false ); console.info('Create all players'); } else { @@ -243,40 +246,41 @@ export class TestController { } disposePlayers () { - this.marsPlayer.dispose(); - this.runtimePlayer.dispose(); + this.oldPlayer.dispose(); + this.newPlayer.dispose(); // - this.marsPlayer = undefined; - this.runtimePlayer = undefined; + this.oldPlayer = undefined; + this.newPlayer = undefined; } async loadOldPlayer () { - const playerAddress = `https://gw.alipayobjects.com/os/lib/alipay/mars-player/${playerVersion}/dist/index.js`; + const playerAddress = `https://gw.alipayobjects.com/os/lib/galacean/mars-player/${oldVersion}/dist/index.min.js`; return this.loadScript(playerAddress); } - async loadOldFilters () { - const filterAddress = `https://gw.alipayobjects.com/os/lib/alipay/mars-player/${playerVersion}/dist/filters.js`; - - return this.loadScript(filterAddress); - } - async loadOldModelPlugin () { - const pluginAddress = 'https://gw.alipayobjects.com/render/p/yuyan_npm/@alipay_mars-plugin-model/1.1.0/dist/index.min.js'; + const pluginAddress = `https://gw.alipayobjects.com/os/lib/galacean/mars-plugin-model/${oldVersion}/dist/index.min.js`; return this.loadScript(pluginAddress); } async loadOldSpinePlugin () { - const spineAddress = `https://gw.alipayobjects.com/render/p/yuyan_npm/@alipay_mars-plugin-spine/${oldSpineVersion}/dist/index.min.js`; + const spineAddress = `https://gw.alipayobjects.com/os/lib/galacean/mars-plugin-spine/${oldVersion}/dist/index.min.js`; return this.loadScript(spineAddress); } async loadScript (src) { + const element = document.getElementById(src); + + if (element !== null) { + return; + } + const script = document.createElement('script'); + script.id = src; script.src = src; document.head.appendChild(script); @@ -436,28 +440,28 @@ export class PlayerCost { export class ComparatorStats { constructor (renderFramework) { this.renderFramework = renderFramework; - this.marsCost = new PlayerCost(); - this.runtimeCost = new PlayerCost(); + this.oldCost = new PlayerCost(); + this.newCost = new PlayerCost(); } - addSceneInfo (name, marsLoadCost, marsCreateCost, runtimeLoadCost, runtimeCreateCost, pixelDiffCount) { - this.marsCost.add( - name, marsLoadCost, marsCreateCost, - marsLoadCost, marsCreateCost, pixelDiffCount + addSceneInfo (name, oldLoadCost, oldCreateCost, newLoadCost, newCreateCost, pixelDiffCount) { + this.oldCost.add( + name, oldLoadCost, oldCreateCost, + oldLoadCost, oldCreateCost, pixelDiffCount ); - this.runtimeCost.add( - name, runtimeLoadCost, runtimeCreateCost, - marsLoadCost, marsCreateCost, pixelDiffCount + this.newCost.add( + name, newLoadCost, newCreateCost, + oldLoadCost, oldCreateCost, pixelDiffCount ); } getStatsInfo () { - const diffSceneCount = this.runtimeCost.getDiffSceneCount(); - const equalSceneCount = this.runtimeCost.getEqualSceneCount(); + const diffSceneCount = this.newCost.getDiffSceneCount(); + const equalSceneCount = this.newCost.getEqualSceneCount(); const totalCount = equalSceneCount + diffSceneCount; const diffSceneRatio = equalSceneCount / totalCount; - const maxPixelDiffCount = this.runtimeCost.maxPixelDiffCount; - const totalPixelDiffCount = this.runtimeCost.getTotalPixelDiffCount(); + const maxPixelDiffCount = this.newCost.maxPixelDiffCount; + const totalPixelDiffCount = this.newCost.getTotalPixelDiffCount(); const averPixelDiffCount = totalPixelDiffCount / diffSceneCount; const msgList = []; @@ -465,17 +469,17 @@ export class ComparatorStats { `DiffStats: ${this.renderFramework}, total ${totalCount}, equal ${equalSceneCount}, ratio ${diffSceneRatio.toFixed(2)}, ` + `diff ${diffSceneCount}, max ${maxPixelDiffCount}, aver ${averPixelDiffCount.toFixed(2)}` ); - const marsAverLoadCost = this.marsCost.getAverLoadCost(); - const marsAverCreateCost = this.marsCost.getAverCreateCost(); - const marsAverTotalCost = marsAverLoadCost + marsAverCreateCost; - const runtimeAverLoadCost = this.runtimeCost.getAverLoadCost(); - const runtimeAverCreateCost = this.runtimeCost.getAverCreateCost(); - const runtimeAverTotalCost = runtimeAverLoadCost + runtimeAverCreateCost; - const maxSceneList = this.runtimeCost.getMaxDiffCostScenes(5); - const marsCostInfo = `mars(${marsAverLoadCost.toFixed(2)}, ${marsAverCreateCost.toFixed(2)}, ${marsAverTotalCost.toFixed(2)})`; - const runtimeCostInfo = `runtime(${runtimeAverLoadCost.toFixed(2)}, ${runtimeAverCreateCost.toFixed(2)}, ${runtimeAverTotalCost.toFixed(2)})`; - - msgList.push(`CostStats: ${this.renderFramework}, ${marsCostInfo}, ${runtimeCostInfo}`); + const oldAverLoadCost = this.oldCost.getAverLoadCost(); + const oldAverCreateCost = this.oldCost.getAverCreateCost(); + const oldAverTotalCost = oldAverLoadCost + oldAverCreateCost; + const newAverLoadCost = this.newCost.getAverLoadCost(); + const newAverCreateCost = this.newCost.getAverCreateCost(); + const newAverTotalCost = newAverLoadCost + newAverCreateCost; + const maxSceneList = this.newCost.getMaxDiffCostScenes(5); + const oldCostInfo = `Old(${oldAverLoadCost.toFixed(2)}, ${oldAverCreateCost.toFixed(2)}, ${oldAverTotalCost.toFixed(2)})`; + const newCostInfo = `New(${newAverLoadCost.toFixed(2)}, ${newAverCreateCost.toFixed(2)}, ${newAverTotalCost.toFixed(2)})`; + + msgList.push(`CostStats: ${this.renderFramework}, ${oldCostInfo}, ${newCostInfo}`); msgList.push('Top5Scene: ' + maxSceneList.map(scene => scene.name + `(${scene.totalDiffCost.toFixed(1)})`).join(', ')); console.info(msgList.join('\n')); @@ -540,13 +544,12 @@ function compatibleCalculateItem (composition: Composition) { // 测试用的兼容 composition.items.forEach(item => { // 兼容空节点结束行为,保持和player一致,在runtime上空节点结束为销毁改为冻结的效果 - if (item.type === spec.ItemType.null && item.endBehavior === spec.ItemEndBehavior.destroy) { + if (VFXItem.isNull(item) && item.endBehavior === spec.ItemEndBehavior.destroy) { item.endBehavior = spec.ItemEndBehavior.forward; } // 兼容旧版 Player 粒子发射器为直线时形状错误 - if (item.type === spec.ItemType.particle && item.content.shape && item.content.shape.type === spec.ShapeType.EDGE) { + if (VFXItem.isParticle(item) && item.content.shape && item.content.shape.type === spec.ShapeType.EDGE) { item.content.shape.width /= 2; } }); - } diff --git a/web-packages/test/case/2d/src/filter/filter.spec.ts b/web-packages/test/case/2d/src/filter/filter.spec.ts index eb5d1e477..0264adb63 100644 --- a/web-packages/test/case/2d/src/filter/filter.spec.ts +++ b/web-packages/test/case/2d/src/filter/filter.spec.ts @@ -61,10 +61,10 @@ async function checkScene (keyName, sceneData) { it(`${name}`, async () => { console.info(`[Compare]: Begin ${name}, ${url}`); - const { marsPlayer, runtimePlayer, renderFramework } = controller; + const { oldPlayer, newPlayer, renderFramework } = controller; - await marsPlayer.initialize(url); - await runtimePlayer.initialize(url); + await oldPlayer.initialize(url); + await newPlayer.initialize(url); const imageCmp = new ImageComparator(pixelDiffThreshold); const namePrefix = getCurrnetTimeStr(); const timeList = [ @@ -79,18 +79,18 @@ async function checkScene (keyName, sceneData) { for (let i = 0; i < timeList.length; i++) { const time = timeList[i]; - if (!marsPlayer.isLoop() && time > marsPlayer.duration()) { + if (!oldPlayer.isLoop() && time > oldPlayer.duration()) { break; } // - marsPlayer.gotoTime(time); - runtimePlayer.gotoTime(time); - const marsImage = await marsPlayer.readImageBuffer(); - const runtimeImage = await runtimePlayer.readImageBuffer(); + oldPlayer.gotoTime(time); + newPlayer.gotoTime(time); + const oldImage = await oldPlayer.readImageBuffer(); + const newImage = await newPlayer.readImageBuffer(); - expect(marsImage.length).to.eql(runtimeImage.length); + expect(oldImage.length).to.eql(newImage.length); // - const pixelDiffValue = await imageCmp.compareImages(marsImage, runtimeImage); + const pixelDiffValue = await imageCmp.compareImages(oldImage, newImage); const diffCountRatio = pixelDiffValue / (canvasWidth * canvasHeight); if (pixelDiffValue > 0) { @@ -100,11 +100,11 @@ async function checkScene (keyName, sceneData) { if (diffCountRatio > accumRatioThreshold) { console.error('FindDiff:', renderFramework, name, keyName, time, pixelDiffValue, url); if (dumpImageForDebug) { - const marsFileName = `${namePrefix}_${name}_${time}_mars.png`; - const runtimeFileName = `${namePrefix}_${name}_${time}_runtime.png`; + const oldFileName = `${namePrefix}_${name}_${time}_old.png`; + const newFileName = `${namePrefix}_${name}_${time}_new.png`; - await marsPlayer.saveCanvasToFile(marsFileName); - await runtimePlayer.saveCanvasToFile(runtimeFileName); + await oldPlayer.saveCanvasToFile(oldFileName); + await newPlayer.saveCanvasToFile(newFileName); } } diff --git a/web-packages/test/case/2d/src/inspire/inspire.spec.ts b/web-packages/test/case/2d/src/inspire/inspire.spec.ts index 78468fa46..04a83e5dd 100644 --- a/web-packages/test/case/2d/src/inspire/inspire.spec.ts +++ b/web-packages/test/case/2d/src/inspire/inspire.spec.ts @@ -7,7 +7,7 @@ const { expect } = chai; * 万分之一的像素不相等比例,对于512x512大小的图像, * 不能超过26个像素不相等 */ -const accumRatioThreshold = 1.5e-4; +const accumRatioThreshold = 3e-4; const pixelDiffThreshold = 1; const dumpImageForDebug = false; const canvasWidth = 512; @@ -48,16 +48,14 @@ function addDescribe (renderFramework) { }); it(`${renderFramework}检查`, () => { - const { marsPlayer } = controller; - - const instance = marsPlayer.player.renderer.gpu; + const { oldPlayer, newPlayer } = controller; if (renderFramework === 'webgl') { - expect(instance.level).to.eql(1); - expect(marsPlayer.player.renderer.gpu.level).to.eql(1); + expect(oldPlayer.player.gpuCapability.level).to.eql(1); + expect(newPlayer.player.gpuCapability.level).to.eql(1); } else { - expect(instance.level).to.eql(2); - expect(marsPlayer.player.renderer.gpu.level).to.eql(2); + expect(oldPlayer.player.gpuCapability.level).to.eql(2); + expect(newPlayer.player.gpuCapability.level).to.eql(2); } }); @@ -72,11 +70,11 @@ function addDescribe (renderFramework) { async function checkScene (keyName, name, url) { it(`${name}`, async () => { console.info(`[Compare]: Begin ${name}, ${url}`); - const { marsPlayer, runtimePlayer, renderFramework } = controller; + const { oldPlayer, newPlayer, renderFramework } = controller; - runtimePlayer.player.compositions.length = 0; - await marsPlayer.initialize(url); - await runtimePlayer.initialize(url); + newPlayer.player.compositions.length = 0; + await oldPlayer.initialize(url); + await newPlayer.initialize(url); const imageCmp = new ImageComparator(pixelDiffThreshold); const namePrefix = getCurrnetTimeStr(); const timeList = [ @@ -89,17 +87,18 @@ async function checkScene (keyName, name, url) { for (let i = 0; i < timeList.length; i++) { const time = timeList[i]; - if (!marsPlayer.isLoop() && time > marsPlayer.duration()) { + if (!oldPlayer.isLoop() && time > oldPlayer.duration()) { break; } - marsPlayer.gotoTime(time); - runtimePlayer.gotoTime(time); - const marsImage = await marsPlayer.readImageBuffer(); - const runtimeImage = await runtimePlayer.readImageBuffer(); + // + oldPlayer.gotoTime(time); + newPlayer.gotoTime(time); + const oldImage = await oldPlayer.readImageBuffer(); + const newImage = await newPlayer.readImageBuffer(); - expect(marsImage.length).to.eql(runtimeImage.length); + expect(oldImage.length).to.eql(newImage.length); // - const pixelDiffValue = await imageCmp.compareImages(marsImage, runtimeImage); + const pixelDiffValue = await imageCmp.compareImages(oldImage, newImage); const diffCountRatio = pixelDiffValue / (canvasWidth * canvasHeight); if (pixelDiffValue > 0) { @@ -109,28 +108,28 @@ async function checkScene (keyName, name, url) { if (diffCountRatio > accumRatioThreshold) { console.error('FindDiff:', renderFramework, name, keyName, time, pixelDiffValue, url); if (dumpImageForDebug) { - const marsFileName = `${namePrefix}_${name}_${time}_mars.png`; - const runtimeFileName = `${namePrefix}_${name}_${time}_runtime.png`; + const oldFileName = `${namePrefix}_${name}_${time}_old.png`; + const newFileName = `${namePrefix}_${name}_${time}_new.png`; - await marsPlayer.saveCanvasToFile(marsFileName); - await runtimePlayer.saveCanvasToFile(runtimeFileName); + await oldPlayer.saveCanvasToFile(oldFileName); + await newPlayer.saveCanvasToFile(newFileName); } } expect(diffCountRatio).to.lte(accumRatioThreshold); } - const marsLoadCost = marsPlayer.loadSceneTime(); - const marsFirstCost = marsPlayer.firstFrameTime(); - const runtimeLoadCost = runtimePlayer.loadSceneTime(); - const runtimeFirstCost = runtimePlayer.firstFrameTime(); + const oldLoadCost = oldPlayer.loadSceneTime(); + const oldFirstCost = oldPlayer.firstFrameTime(); + const newLoadCost = newPlayer.loadSceneTime(); + const newFirstCost = newPlayer.firstFrameTime(); cmpStats.addSceneInfo( - `${keyName}@${name}`, marsLoadCost, marsFirstCost - marsLoadCost, - runtimeLoadCost, runtimeFirstCost - runtimeLoadCost, maxDiffValue + `${keyName}@${name}`, oldLoadCost, oldFirstCost - oldLoadCost, + newLoadCost, newFirstCost - newLoadCost, maxDiffValue ); console.info(`[Compare]: End ${name}, ${url}`); - runtimePlayer.disposeScene(); + newPlayer.disposeScene(); }); } diff --git a/web-packages/test/case/2d/src/interact/interact.spec.ts b/web-packages/test/case/2d/src/interact/interact.spec.ts index 28322bfeb..676ad72a2 100644 --- a/web-packages/test/case/2d/src/interact/interact.spec.ts +++ b/web-packages/test/case/2d/src/interact/interact.spec.ts @@ -41,16 +41,16 @@ function addDescribe (renderFramework) { async function checkScene (keyName, name, url) { it(`${name}`, async () => { console.info(`[Compare]: Begin ${name}, ${url}`); - const { marsPlayer, runtimePlayer, renderFramework } = controller; + const { oldPlayer, newPlayer, renderFramework } = controller; - await marsPlayer.initialize(url); - await runtimePlayer.initialize(url); + await oldPlayer.initialize(url); + await newPlayer.initialize(url); const imageCmp = new ImageComparator(pixelDiffThreshold); const namePrefix = getCurrnetTimeStr(); const timeList = [ - 0, 0.11, 0.22, 0.34, 0.45, 0.57, 0.66, 0.71, 0.83, 0.96, - 1.1, 1.23, 1.45, 1.67, 1.88, 2.1, 2.5, 3.3, 4.7, 5.2, 6.8, - 7.5, 8.6, 9.7, 9.99, + 0, 0.11, 0.22, 0.34, 0.45, 0.57, 0.66, 0.71, 0.83, 0.96, 1.0, + 1.1, 1.23, 1.34, 1.45, 1.55, 1.67, 1.73, 1.88, 1.99, + 2.1, 2.5, 3.3, 4.7, 5.2, 6.8, 7.5, 8.6, 9.7, 9.99, ]; let marsRet, runtimeRet; @@ -58,17 +58,17 @@ async function checkScene (keyName, name, url) { const time = timeList[i]; - if (!marsPlayer.isLoop() && time > marsPlayer.duration()) { + if (!oldPlayer.isLoop() && time > oldPlayer.duration()) { break; } // - marsPlayer.gotoTime(time); - runtimePlayer.gotoTime(time); + oldPlayer.gotoTime(time); + newPlayer.gotoTime(time); // Math.seedrandom(`hit-test${i}`); - // console.log(marsPlayer.compositions[0]); - // console.log(runtimePlayer.currentComposition); + // console.log(oldPlayer.compositions[0]); + // console.log(newPlayer.currentComposition); if (Math.random() < 0.75) { const count = Math.round(Math.random() * 8); @@ -77,8 +77,8 @@ async function checkScene (keyName, name, url) { const x = Math.random() * 2.0 - 1.0; const y = Math.random() * 2.0 - 1.0; - marsRet = marsPlayer.hitTest(x, y); - runtimeRet = runtimePlayer.hitTest(x, y); + marsRet = oldPlayer.hitTest(x, y); + runtimeRet = newPlayer.hitTest(x, y); expect(marsRet.length).to.eql(runtimeRet.length); for (let k = 0; k < marsRet.length; k++) { @@ -89,13 +89,13 @@ async function checkScene (keyName, name, url) { let hitPos; if (Math.random() < 0.5) { - hitPos = marsPlayer.getRandomPointInParticle(); + hitPos = oldPlayer.getRandomPointInParticle(); } else { - hitPos = runtimePlayer.getRandomPointInParticle(); + hitPos = newPlayer.getRandomPointInParticle(); } - marsRet = marsPlayer.hitTest(hitPos[0], hitPos[1]); - runtimeRet = runtimePlayer.hitTest(hitPos[0], hitPos[1]); + marsRet = oldPlayer.hitTest(hitPos[0], hitPos[1]); + runtimeRet = newPlayer.hitTest(hitPos[0], hitPos[1]); expect(marsRet.length).to.eql(runtimeRet.length); for (let j = 0; j < marsRet.length; j++) { @@ -103,12 +103,12 @@ async function checkScene (keyName, name, url) { } } - const marsImage = await marsPlayer.readImageBuffer(); - const runtimeImage = await runtimePlayer.readImageBuffer(); + const oldImage = await oldPlayer.readImageBuffer(); + const newImage = await newPlayer.readImageBuffer(); - expect(marsImage.length).to.eql(runtimeImage.length); + expect(oldImage.length).to.eql(newImage.length); // - const pixelDiffValue = await imageCmp.compareImages(marsImage, runtimeImage); + const pixelDiffValue = await imageCmp.compareImages(oldImage, newImage); const diffCountRatio = pixelDiffValue / (canvasWidth * canvasHeight); if (pixelDiffValue > 0) { @@ -117,11 +117,11 @@ async function checkScene (keyName, name, url) { if (diffCountRatio > accumRatioThreshold) { console.error('FindDiff:', renderFramework, name, keyName, time, pixelDiffValue, url); if (dumpImageForDebug) { - const marsFileName = `${namePrefix}_${name}_${time}_mars.png`; - const runtimeFileName = `${namePrefix}_${name}_${time}_runtime.png`; + const oldFileName = `${namePrefix}_${name}_${time}_old.png`; + const newFileName = `${namePrefix}_${name}_${time}_new.png`; - await marsPlayer.saveCanvasToFile(marsFileName); - await runtimePlayer.saveCanvasToFile(runtimeFileName); + await oldPlayer.saveCanvasToFile(oldFileName); + await newPlayer.saveCanvasToFile(newFileName); } } diff --git a/web-packages/test/case/3d/index.html b/web-packages/test/case/3d/index.html index 80b47694f..e7efe0d7d 100644 --- a/web-packages/test/case/3d/index.html +++ b/web-packages/test/case/3d/index.html @@ -29,7 +29,7 @@ ui: 'bdd', globals: [ '__core-js_shared__', 'core', 'LiveReload', 'Mars', 'MarsFilters', 'MarsModel', - '__MarsConfig__', '__vite_is_modern_browser', 'MarsSpine' + '__MarsConfig__', '__vite_is_modern_browser', 'MarsSpine', 'mars' ] }); mocha.checkLeaks(); diff --git a/web-packages/test/case/3d/src/case.ts b/web-packages/test/case/3d/src/case.ts index 55fa9880e..ee83686f0 100644 --- a/web-packages/test/case/3d/src/case.ts +++ b/web-packages/test/case/3d/src/case.ts @@ -42,15 +42,14 @@ function addDescribe (renderFramework) { }); it(`${renderFramework}检查`, () => { - const { marsPlayer } = controller; - const instance = marsPlayer.player.renderer.gpu; + const { oldPlayer, newPlayer } = controller; if (renderFramework === 'webgl') { - expect(instance.level).to.eql(1); - expect(marsPlayer.player.renderer.gpu.level).to.eql(1); + expect(oldPlayer.player.gpuCapability.level).to.eql(1); + expect(newPlayer.player.gpuCapability.level).to.eql(1); } else { - expect(instance.level).to.eql(2); - expect(marsPlayer.player.renderer.gpu.level).to.eql(2); + expect(oldPlayer.player.gpuCapability.level).to.eql(2); + expect(newPlayer.player.gpuCapability.level).to.eql(2); } }); @@ -74,7 +73,7 @@ function addDescribe (renderFramework) { it(`${name}`, async () => { console.info(`[Compare]: Begin ${name}, ${url}`); - const { marsPlayer, runtimePlayer, renderFramework } = controller; + const { oldPlayer, newPlayer, renderFramework } = controller; const loadOptions = { pluginData: { @@ -85,33 +84,33 @@ function addDescribe (renderFramework) { }; const playerOptions = { pauseOnFirstFrame: true }; - await marsPlayer.initialize(url, loadOptions, playerOptions); - await runtimePlayer.initialize(url, loadOptions, playerOptions); + await oldPlayer.initialize(url, loadOptions, playerOptions); + await newPlayer.initialize(url, loadOptions, playerOptions); const imageCmp = new ImageComparator(pixelDiffThreshold); const namePrefix = getCurrnetTimeStr(); const timeList = [ - 0, 0.11, 0.22, 0.34, 0.45, 0.57, 0.71, 0.83, 0.96, - 1.1, 1.2, 1.4, 1.7, 1.9, 2.2, 2.5, 2.7, 3.3, 3.8, - 4.7, 5.2, 6.8, 7.5, 8.6, 9.7, 9.99, 12.5, 18.9, + 0, 0.11, 0.22, 0.34, 0.45, 0.57, 0.65, 0.71, 0.83, 0.96, 1.0, + 1.1, 1.2, 1.3, 1.4, 1.5, 1.7, 1.9, 2.0, 2.2, 2.5, 2.7, 3.0, 3.3, 3.8, + 4.1, 4.7, 5.2, 5.9, 6.8, 7.5, 8.6, 9.7, 9.99, 11.23, 12.5, 15.8, 18.9, ]; let maxDiffValue = 0; for (let i = 0; i < timeList.length; i++) { const time = timeList[i]; - if (!marsPlayer.isLoop() && time > marsPlayer.duration()) { + if (!oldPlayer.isLoop() && time > oldPlayer.duration()) { break; } // - marsPlayer.gotoTime(time); - runtimePlayer.gotoTime(time); - const marsImage = await marsPlayer.readImageBuffer(); - const runtimeImage = await runtimePlayer.readImageBuffer(); + oldPlayer.gotoTime(time); + newPlayer.gotoTime(time); + const oldImage = await oldPlayer.readImageBuffer(); + const newImage = await newPlayer.readImageBuffer(); - expect(marsImage.length).to.eql(runtimeImage.length); + expect(oldImage.length).to.eql(newImage.length); // - const pixelDiffValue = await imageCmp.compareImages(marsImage, runtimeImage); + const pixelDiffValue = await imageCmp.compareImages(oldImage, newImage); const diffCountRatio = pixelDiffValue / (canvasWidth * canvasHeight); if (pixelDiffValue > 0) { @@ -121,25 +120,25 @@ function addDescribe (renderFramework) { if (diffCountRatio > accumRatioThreshold) { console.error('FindDiff:', renderFramework, name, keyName, time, pixelDiffValue, url); if (dumpImageForDebug) { - const marsFileName = `${namePrefix}_${name}_${time}_mars.png`; - const runtimeFileName = `${namePrefix}_${name}_${time}_runtime.png`; + const oldFileName = `${namePrefix}_${name}_${time}_old.png`; + const newFileName = `${namePrefix}_${name}_${time}_new.png`; - await marsPlayer.saveCanvasToFile(marsFileName); - await runtimePlayer.saveCanvasToFile(runtimeFileName); + await oldPlayer.saveCanvasToFile(oldFileName); + await newPlayer.saveCanvasToFile(newFileName); } } expect(diffCountRatio).to.lte(accumRatioThreshold); } - const marsLoadCost = marsPlayer.loadSceneTime(); - const marsFirstCost = marsPlayer.firstFrameTime(); - const runtimeLoadCost = runtimePlayer.loadSceneTime(); - const runtimeFirstCost = runtimePlayer.firstFrameTime(); + const oldLoadCost = oldPlayer.loadSceneTime(); + const oldFirstCost = oldPlayer.firstFrameTime(); + const newLoadCost = newPlayer.loadSceneTime(); + const newFirstCost = newPlayer.firstFrameTime(); cmpStats.addSceneInfo( - `${keyName}@${name}`, marsLoadCost, marsFirstCost - marsLoadCost, - runtimeLoadCost, runtimeFirstCost - runtimeLoadCost, maxDiffValue + `${keyName}@${name}`, oldLoadCost, oldFirstCost - oldLoadCost, + newLoadCost, newFirstCost - newLoadCost, maxDiffValue ); console.info(`[Compare]: End ${name}, ${url}`); diff --git a/web-packages/test/case/3d/src/scene-list.ts b/web-packages/test/case/3d/src/scene-list.ts index 17f8f2b7d..5331b76c2 100644 --- a/web-packages/test/case/3d/src/scene-list.ts +++ b/web-packages/test/case/3d/src/scene-list.ts @@ -31,11 +31,12 @@ export default { url: 'https://gw.alipayobjects.com/os/gltf-asset/89748482160728/restart.json', name: 'Restart测试', }, - ring618: { - url: 'https://mdn.alipayobjects.com/mars/afts/file/A*MBmNSbmPOIIAAAAAAAAAAAAADlB4AQ', - name: '618圆环', - pass: true, - }, + /* z-fighting问题先跳过 */ + // ring618: { + // url: 'https://mdn.alipayobjects.com/mars/afts/file/A*MBmNSbmPOIIAAAAAAAAAAAAADlB4AQ', + // name: '618圆环', + // pass: true, + // }, /* 需要打开父子节点颜色继承兼容代码的项目 */ // ring818: { // url: 'https://mdn.alipayobjects.com/mars/afts/file/A*cnCMTo1seD0AAAAAAAAAAAAADlB4AQ', diff --git a/web-packages/test/case/all/index.html b/web-packages/test/case/all/index.html index c03cce37c..4beadccd9 100644 --- a/web-packages/test/case/all/index.html +++ b/web-packages/test/case/all/index.html @@ -29,7 +29,7 @@ ui: 'bdd', globals: [ '__core-js_shared__', 'core', 'LiveReload', 'Mars', 'MarsFilters', 'MarsModel', - '__MarsConfig__', '__vite_is_modern_browser' + '__MarsConfig__', '__vite_is_modern_browser', 'mars' ] }); mocha.checkLeaks(); diff --git a/web-packages/test/case/all/src/2d_inspire.html b/web-packages/test/case/all/src/2d_inspire.html index cdf9c571c..765f5fec9 100644 --- a/web-packages/test/case/all/src/2d_inspire.html +++ b/web-packages/test/case/all/src/2d_inspire.html @@ -29,7 +29,7 @@ ui: 'bdd', globals: [ '__core-js_shared__', 'core', 'LiveReload', 'Mars', 'MarsFilters', 'MarsModel', - '__MarsConfig__', '__vite_is_modern_browser', 'MarsSpine', + '__MarsConfig__', '__vite_is_modern_browser', 'MarsSpine', 'mars' ] }); mocha.checkLeaks(); diff --git a/web-packages/test/case/all/src/2d_other.html b/web-packages/test/case/all/src/2d_other.html index fe2010477..2ef0e5b0b 100644 --- a/web-packages/test/case/all/src/2d_other.html +++ b/web-packages/test/case/all/src/2d_other.html @@ -29,7 +29,7 @@ ui: 'bdd', globals: [ '__core-js_shared__', 'core', 'LiveReload', 'Mars', 'MarsFilters', 'MarsModel', - '__MarsConfig__', '__vite_is_modern_browser', 'MarsSpine', + '__MarsConfig__', '__vite_is_modern_browser', 'MarsSpine', 'mars' ] }); mocha.checkLeaks(); diff --git a/web-packages/test/case/all/src/3d.html b/web-packages/test/case/all/src/3d.html index d6243f755..2ccc58e69 100644 --- a/web-packages/test/case/all/src/3d.html +++ b/web-packages/test/case/all/src/3d.html @@ -25,6 +25,7 @@ '__MarsConfig__', 'MarsModel', 'MarsSpine', + 'mars' ] }); mocha.checkLeaks(); diff --git a/web-packages/test/case/all/src/spine.html b/web-packages/test/case/all/src/spine.html index 8a107d8e0..876de4d0b 100644 --- a/web-packages/test/case/all/src/spine.html +++ b/web-packages/test/case/all/src/spine.html @@ -25,6 +25,7 @@ '__MarsConfig__', 'MarsModel', 'MarsSpine', + 'mars' ] }); mocha.checkLeaks(); diff --git a/web-packages/test/case/spine/index.html b/web-packages/test/case/spine/index.html index 44982060e..85e1923fc 100644 --- a/web-packages/test/case/spine/index.html +++ b/web-packages/test/case/spine/index.html @@ -14,7 +14,7 @@