diff --git a/README.md b/README.md index 41b4c24ece..6f511c5993 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,14 @@ ![npm-download](https://img.shields.io/npm/dm/oasis-engine) [![codecov](https://codecov.io/gh/oasis-engine/engine/branch/main/graph/badge.svg?token=KR2UBKE3OX)](https://codecov.io/gh/oasis-engine/engine) -This is a **web-first** and **mobile-first** high-performance real-time development platform. Use **component system design** and pursue ease of use and light weight. Developers can independently use and write Typescript scripts to develop projects using pure code. +This is a **web-first** and **mobile-first** high-performance real-time interactive engine. Use **component system design** and pursue ease of use and light weight. Developers can independently use and write Typescript scripts to develop projects using pure code. ## Features - 🖥  **Platform** - Suppport HTML5 and Alipay miniprogram - 🔮  **Graphics** - Advanced 2D + 3D graphics engine - 🏃  **Animation** - Powerful animation system +- 🧱  **Physics** - Powerful and easy-to-use physical features - 📑  **Scripts** - Use TypeScript to write logic efficiently ## Usage diff --git a/lerna.json b/lerna.json index 2d86f82025..ee78876ba4 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "npmClient": "npm", - "version": "0.7.0-beta.4", + "version": "0.7.0-beta.6", "bootstrap": { "hoist": true }, diff --git a/packages/core/package.json b/packages/core/package.json index 502913146e..f10f2df357 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@oasis-engine/core", - "version": "0.7.0-beta.4", + "version": "0.7.0-beta.6", "license": "MIT", "main": "dist/main.js", "module": "dist/module.js", @@ -14,9 +14,9 @@ "types/**/*" ], "dependencies": { - "@oasis-engine/math": "0.7.0-beta.4" + "@oasis-engine/math": "0.7.0-beta.6" }, "devDependencies": { - "@oasis-engine/design": "0.7.0-beta.4" + "@oasis-engine/design": "0.7.0-beta.6" } } diff --git a/packages/core/src/2d/sprite/Sprite.ts b/packages/core/src/2d/sprite/Sprite.ts index 5d9c637f02..e32f826833 100644 --- a/packages/core/src/2d/sprite/Sprite.ts +++ b/packages/core/src/2d/sprite/Sprite.ts @@ -112,8 +112,9 @@ export class Sprite extends RefObject { set pivot(value: Vector2) { const pivot = this._pivot; - if (pivot === value || pivot.x !== value.x || pivot.y !== value.y) { - pivot.setValue(value.x, value.y); + const { x, y } = value; + if (pivot === value || pivot.x !== x || pivot.y !== y) { + pivot.setValue(x, y); this._setDirtyFlagTrue(DirtyFlag.positions); } } diff --git a/packages/core/src/2d/text/TextRenderer.ts b/packages/core/src/2d/text/TextRenderer.ts index d54b446855..2e54ab06c7 100644 --- a/packages/core/src/2d/text/TextRenderer.ts +++ b/packages/core/src/2d/text/TextRenderer.ts @@ -329,19 +329,12 @@ export class TextRenderer extends Renderer { * @override */ protected _updateBounds(worldBounds: BoundingBox): void { - const sprite = this._sprite; - if (sprite && sprite.texture) { - if (this._customLocalBounds && this._customRootEntity) { - const worldMatrix = this._customRootEntity.transform.worldMatrix; - BoundingBox.transform(this._customLocalBounds, worldMatrix, worldBounds); - } else { - const localBounds = sprite.bounds; - const worldMatrix = this._entity.transform.worldMatrix; - BoundingBox.transform(localBounds, worldMatrix, worldBounds); - } + if (this._customLocalBounds && this._customRootEntity) { + const worldMatrix = this._customRootEntity.transform.worldMatrix; + BoundingBox.transform(this._customLocalBounds, worldMatrix, worldBounds); } else { - worldBounds.min.setValue(0, 0, 0); - worldBounds.max.setValue(0, 0, 0); + const worldMatrix = this._entity.transform.worldMatrix; + BoundingBox.transform(this._sprite.bounds, worldMatrix, worldBounds); } } @@ -378,7 +371,34 @@ export class TextRenderer extends Renderer { const { width, height } = trimData; const canvas = TextUtils.updateCanvas(width, height, trimData.data); this._clearTexture(); - const { _sprite: sprite } = this; + const { _sprite: sprite, horizontalAlignment, verticalAlignment } = this; + + // Handle the case that width or height of text is larger than real width or height. + const { pixelsPerUnit, pivot } = sprite; + switch (horizontalAlignment) { + case TextHorizontalAlignment.Left: + pivot.x = (this.width * pixelsPerUnit) / width * 0.5; + break; + case TextHorizontalAlignment.Right: + pivot.x = 1 - (this.width * pixelsPerUnit) / width * 0.5; + break; + case TextHorizontalAlignment.Center: + pivot.x = 0.5; + break; + } + switch (verticalAlignment) { + case TextVerticalAlignment.Top: + pivot.y = 1 - (this.height * pixelsPerUnit) / height * 0.5; + break; + case TextVerticalAlignment.Bottom: + pivot.y = (this.height * pixelsPerUnit) / height * 0.5; + break; + case TextVerticalAlignment.Center: + pivot.y = 0.5; + break; + } + sprite.pivot = pivot; + // If add fail, set texture for sprite. if (!this.engine._dynamicTextAtlasManager.addSprite(sprite, canvas)) { const texture = new Texture2D(this.engine, width, height); diff --git a/packages/core/src/Renderer.ts b/packages/core/src/Renderer.ts index cdbdb4b624..1ca9d1b801 100644 --- a/packages/core/src/Renderer.ts +++ b/packages/core/src/Renderer.ts @@ -14,7 +14,7 @@ import { ShaderMacroCollection } from "./shader/ShaderMacroCollection"; import { Transform } from "./Transform"; /** - * Renderable component. + * Basis for all renderers. * @decorator `@dependentComponents(Transform)` */ @dependentComponents(Transform) @@ -177,26 +177,10 @@ export class Renderer extends Component { setMaterial(index: number, material: Material): void; setMaterial(indexOrMaterial: number | Material, material: Material = null): void { - let index; if (typeof indexOrMaterial === "number") { - index = indexOrMaterial; + this._setMaterial(indexOrMaterial, material); } else { - index = 0; - material = indexOrMaterial; - } - - const materials = this._materials; - if (index >= materials.length) { - materials.length = index + 1; - } - - const materialsInstance = this._materialsInstanced; - const internalMaterial = materials[index]; - if (internalMaterial !== material) { - materials[index] = material; - index < materialsInstance.length && (materialsInstance[index] = false); - internalMaterial && internalMaterial._addRefCount(-1); - material && material._addRefCount(1); + this._setMaterial(0, indexOrMaterial); } } @@ -314,8 +298,9 @@ export class Renderer extends Component { this.shaderData._addRefCount(-1); - for (let i = 0, n = this._materials.length; i < n; i++) { - this._materials[i]._addRefCount(-1); + const materials = this._materials; + for (let i = 0, n = materials.length; i < n; i++) { + materials[i]?._addRefCount(-1); } } @@ -330,4 +315,21 @@ export class Renderer extends Component { this._materials[index] = insMaterial; return insMaterial; } + + private _setMaterial(index: number, material: Material): void { + const materials = this._materials; + if (index >= materials.length) { + materials.length = index + 1; + } + + const internalMaterial = materials[index]; + if (internalMaterial !== material) { + const materialsInstance = this._materialsInstanced; + index < materialsInstance.length && (materialsInstance[index] = false); + + internalMaterial && internalMaterial._addRefCount(-1); + material && material._addRefCount(1); + materials[index] = material; + } + } } diff --git a/packages/core/src/animation/internal/AnimationCurveOwner.ts b/packages/core/src/animation/internal/AnimationCurveOwner.ts index 9b4bf29005..11ef24a3b7 100644 --- a/packages/core/src/animation/internal/AnimationCurveOwner.ts +++ b/packages/core/src/animation/internal/AnimationCurveOwner.ts @@ -18,7 +18,7 @@ export class AnimationCurveOwner { readonly component: Component; readonly defaultValue: InterpolableValue; readonly fixedPoseValue: InterpolableValue; - + /** @internal */ _hasSavedDefaultValue: boolean = false; @@ -43,9 +43,10 @@ export class AnimationCurveOwner { this.component = target.transform; break; case AnimationProperty.BlendShapeWeights: - this.defaultValue = new Float32Array(4); - this.fixedPoseValue = new Float32Array(4); this.component = target.getComponent(SkinnedMeshRenderer); + const weightLength = (this.component).blendShapeWeights.length; + this.defaultValue = new Float32Array(weightLength); + this.fixedPoseValue = new Float32Array(weightLength); break; } } @@ -64,7 +65,7 @@ export class AnimationCurveOwner { case AnimationProperty.BlendShapeWeights: const { blendShapeWeights } = this.component; for (let i = 0, length = blendShapeWeights.length; i < length; ++i) { - this.defaultValue[i] = (this.component).blendShapeWeights[i]; + this.defaultValue[i] = blendShapeWeights[i]; } break; } diff --git a/packages/core/src/graphic/Mesh.ts b/packages/core/src/graphic/Mesh.ts index b1622e1982..b6e6de5922 100644 --- a/packages/core/src/graphic/Mesh.ts +++ b/packages/core/src/graphic/Mesh.ts @@ -34,6 +34,8 @@ export abstract class Mesh extends RefObject { _indexBufferBinding: IndexBufferBinding = null; /** @internal */ _vertexElements: VertexElement[] = []; + /** @internal */ + _enableVAO: boolean = true; private _subMeshes: SubMesh[] = []; private _updateFlagManager: UpdateFlagManager = new UpdateFlagManager(); @@ -139,6 +141,18 @@ export abstract class Mesh extends RefObject { this._updateFlagManager.dispatch(); } + /** + * @internal + */ + _setVertexBufferBinding(index: number, binding: VertexBufferBinding): void { + if (this._getRefCount() > 0) { + const lastBinding = this._vertexBufferBindings[index]; + lastBinding && lastBinding._buffer._addRefCount(-1); + binding._buffer._addRefCount(1); + } + this._vertexBufferBindings[index] = binding; + } + /** * @internal */ @@ -176,15 +190,6 @@ export abstract class Mesh extends RefObject { } } - protected _setVertexBufferBinding(index: number, binding: VertexBufferBinding): void { - if (this._getRefCount() > 0) { - const lastBinding = this._vertexBufferBindings[index]; - lastBinding && lastBinding._buffer._addRefCount(-1); - binding._buffer._addRefCount(1); - } - this._vertexBufferBindings[index] = binding; - } - protected _setIndexBufferBinding(binding: IndexBufferBinding | null): void { if (binding) { this._indexBufferBinding = binding; diff --git a/packages/core/src/graphic/VertexElement.ts b/packages/core/src/graphic/VertexElement.ts index 6c117ccea0..ffc39d9cf1 100644 --- a/packages/core/src/graphic/VertexElement.ts +++ b/packages/core/src/graphic/VertexElement.ts @@ -1,5 +1,5 @@ +import { BufferUtil, ElementInfo } from "./BufferUtil"; import { VertexElementFormat } from "./enums/VertexElementFormat"; -import { ElementInfo, BufferUtil } from "./BufferUtil"; /** * Vertex element. @@ -27,6 +27,10 @@ export class VertexElement { return this._offset; } + set offset(value: number) { + this._offset = value; + } + /** * Vertex data format. */ @@ -41,6 +45,10 @@ export class VertexElement { return this._bindingIndex; } + set bindingIndex(value: number) { + this._bindingIndex = value; + } + /** * Instance cadence, the number of instances drawn for each vertex in the buffer, non-instance elements must be 0. */ diff --git a/packages/core/src/mesh/BlendShapeManager.ts b/packages/core/src/mesh/BlendShapeManager.ts index 1a894ab6a6..c21e2bf2c1 100644 --- a/packages/core/src/mesh/BlendShapeManager.ts +++ b/packages/core/src/mesh/BlendShapeManager.ts @@ -1,46 +1,67 @@ -import { Vector3 } from "@oasis-engine/math"; +import { Vector2, Vector3 } from "@oasis-engine/math"; import { BoolUpdateFlag } from "../BoolUpdateFlag"; import { Engine } from "../Engine"; -import { VertexElement, VertexElementFormat } from "../graphic"; +import { Buffer } from "../graphic/Buffer"; +import { BufferBindFlag } from "../graphic/enums/BufferBindFlag"; +import { BufferUsage } from "../graphic/enums/BufferUsage"; +import { VertexElementFormat } from "../graphic/enums/VertexElementFormat"; +import { VertexBufferBinding } from "../graphic/VertexBufferBinding"; +import { VertexElement } from "../graphic/VertexElement"; import { ListenerUpdateFlag } from "../ListenerUpdateFlag"; +import { Shader } from "../shader/Shader"; +import { ShaderData } from "../shader/ShaderData"; import { Texture2DArray, TextureFilterMode, TextureFormat } from "../texture"; import { BlendShape } from "./BlendShape"; import { ModelMesh } from "./ModelMesh"; +import { SkinnedMeshRenderer } from "./SkinnedMeshRenderer"; /** * @internal */ export class BlendShapeManager { + private static _blendShapeMacro = Shader.getMacroByName("OASIS_BLENDSHAPE"); + private static _blendShapeTextureMacro = Shader.getMacroByName("OASIS_BLENDSHAPE_TEXTURE"); + private static _blendShapeNormalMacro = Shader.getMacroByName("OASIS_BLENDSHAPE_NORMAL"); + private static _blendShapeTangentMacro = Shader.getMacroByName("OASIS_BLENDSHAPE_TANGENT"); + + private static _blendShapeWeightsProperty = Shader.getPropertyByName("u_blendShapeWeights"); + private static _blendShapeTextureProperty = Shader.getPropertyByName("u_blendShapeTexture"); + private static _blendShapeTextureInfoProperty = Shader.getPropertyByName("u_blendShapeTextureInfo"); + /** @internal */ - _useBlendNormal: boolean = true; - /** @internal */ - _useBlendTangent: boolean = true; + _blendShapeCount: number = 0; /** @internal */ _blendShapes: BlendShape[] = []; /** @internal */ _blendShapeNames: string[]; /** @internal */ - _blendShapeCount: number = 0; - /** @internal */ _layoutDirtyFlag: ListenerUpdateFlag = new ListenerUpdateFlag(); /** @internal */ _subDataDirtyFlags: BoolUpdateFlag[] = []; - /** @internal */ - _dataTextureBuffer: Float32Array; + _vertexTexture: Texture2DArray; /** @internal */ - _dataTexture: Texture2DArray; + _vertexBuffers: Buffer[] = []; /** @internal */ - readonly _dataTextureInfo: Vector3 = new Vector3(); - + _vertices: Float32Array; + + private _useBlendNormal: boolean = false; + private _useBlendTangent: boolean = false; + private _vertexElementCount: number = 0; + private _vertexElementOffset: number; + private _storeInVertexBufferInfo: Vector2[] = []; + private _maxCountSingleVertexBuffer: number = 0; private readonly _engine: Engine; - private readonly _lastUpdateLayoutAndCountInfo: Vector3 = new Vector3(0, 0, 0); + private readonly _modelMesh: ModelMesh; + private readonly _lastCreateHostInfo: Vector3 = new Vector3(0, 0, 0); private readonly _canUseTextureStoreData: boolean = true; + private readonly _dataTextureInfo: Vector3 = new Vector3(); - constructor(engine: Engine) { + constructor(engine: Engine, modelMesh: ModelMesh) { this._engine = engine; + this._modelMesh = modelMesh; this._canUseTextureStoreData = this._engine._hardwareRenderer.capability.canUseFloatTextureBlendShape; - this._layoutDirtyFlag.listener = this._updateUsePropertyFlag.bind(this); + this._layoutDirtyFlag.listener = this._updateLayoutChange.bind(this); } /** @@ -51,7 +72,7 @@ export class BlendShapeManager { this._blendShapeCount++; blendShape._addLayoutChangeFlag(this._layoutDirtyFlag); - this._updateUsePropertyFlag(blendShape); + this._updateLayoutChange(blendShape); this._subDataDirtyFlags.push(blendShape._createSubDataDirtyFlag()); } @@ -60,8 +81,9 @@ export class BlendShapeManager { * @internal */ _clearBlendShapes(): void { - this._useBlendNormal = true; - this._useBlendTangent = true; + this._useBlendNormal = false; + this._useBlendTangent = false; + this._vertexElementCount = 0; this._blendShapes.length = 0; this._blendShapeCount = 0; @@ -76,30 +98,82 @@ export class BlendShapeManager { /** * @internal */ - _useTextureStore(): boolean { - if (!this._canUseTextureStoreData) { - return false; - } + _updateShaderData(shaderData: ShaderData, skinnedMeshRenderer: SkinnedMeshRenderer): void { + const blendShapeCount = this._blendShapeCount; + if (blendShapeCount > 0) { + shaderData.enableMacro(BlendShapeManager._blendShapeMacro); + shaderData.enableMacro("OASIS_BLENDSHAPE_COUNT", blendShapeCount.toString()); + if (this._useTextureMode()) { + shaderData.enableMacro(BlendShapeManager._blendShapeTextureMacro); + shaderData.setTexture(BlendShapeManager._blendShapeTextureProperty, this._vertexTexture); + shaderData.setVector3(BlendShapeManager._blendShapeTextureInfoProperty, this._dataTextureInfo); + shaderData.setFloatArray(BlendShapeManager._blendShapeWeightsProperty, skinnedMeshRenderer._blendShapeWeights); + } else { + const maxBlendCount = this._getVertexBufferModeSupportCount(); + if (blendShapeCount > maxBlendCount) { + let condensedBlendShapeWeights = skinnedMeshRenderer._condensedBlendShapeWeights; + if (!condensedBlendShapeWeights) { + condensedBlendShapeWeights = new Float32Array(maxBlendCount); + skinnedMeshRenderer._condensedBlendShapeWeights = condensedBlendShapeWeights; + } + this._filterCondensedBlendShapeWeights(skinnedMeshRenderer._blendShapeWeights, condensedBlendShapeWeights); + shaderData.setFloatArray(BlendShapeManager._blendShapeWeightsProperty, condensedBlendShapeWeights); + this._modelMesh._enableVAO = false; + } else { + shaderData.setFloatArray( + BlendShapeManager._blendShapeWeightsProperty, + skinnedMeshRenderer._blendShapeWeights + ); + this._modelMesh._enableVAO = true; + } + shaderData.disableMacro(BlendShapeManager._blendShapeTextureMacro); + } - if (this._useBlendNormal || this._useBlendTangent) { - return this._blendShapeCount > 4; + if (this._useBlendNormal) { + shaderData.enableMacro(BlendShapeManager._blendShapeNormalMacro); + } else { + shaderData.disableMacro(BlendShapeManager._blendShapeNormalMacro); + } + if (this._useBlendTangent) { + shaderData.enableMacro(BlendShapeManager._blendShapeTangentMacro); + } else { + shaderData.disableMacro(BlendShapeManager._blendShapeTangentMacro); + } } else { - return this._blendShapeCount > 8; + shaderData.disableMacro(BlendShapeManager._blendShapeMacro); + shaderData.disableMacro("OASIS_BLENDSHAPE_COUNT"); } } /** * @internal */ - _layoutOrCountChange(): boolean { - const lastInfo = this._lastUpdateLayoutAndCountInfo; - if ( - lastInfo.x !== this._blendShapeCount || - !!lastInfo.y !== this._useBlendNormal || - !!lastInfo.z !== this._useBlendTangent - ) { - return true; + _useTextureMode(): boolean { + if (!this._canUseTextureStoreData) { + return false; } + return this._blendShapeCount > this._getVertexBufferModeSupportCount(); + } + + /** + * @internal + */ + _layoutOrCountChange(): boolean { + const last = this._lastCreateHostInfo; + return last.x !== this._blendShapeCount || !!last.y !== this._useBlendNormal || !!last.z !== this._useBlendTangent; + } + + /** + * @internal + */ + _vertexElementsNeedUpdate(): boolean { + const maxSupportCount = this._getVertexBufferModeSupportCount(); + const info = this._lastCreateHostInfo; + return ( + Math.min(info.x, maxSupportCount) !== Math.min(this._blendShapeCount, maxSupportCount) || + !!info.y !== this._useBlendNormal || + !!info.z !== this._useBlendTangent + ); } /** @@ -118,39 +192,148 @@ export class BlendShapeManager { /** * @internal */ - _updateVertexElements(modelMesh: ModelMesh, offset: number): number { - let elementCount = 0; - for (let i = 0, n = this._blendShapeCount; i < n; i++) { - modelMesh._addVertexElement(new VertexElement(`POSITION_BS${i}`, offset, VertexElementFormat.Vector3, 0)); + _addVertexElements(modelMesh: ModelMesh): void { + let offset = 0; + this._vertexElementOffset = modelMesh._vertexElements.length; + for (let i = 0, n = Math.min(this._blendShapeCount, this._getVertexBufferModeSupportCount()); i < n; i++) { + modelMesh._addVertexElement(new VertexElement(`POSITION_BS${i}`, offset, VertexElementFormat.Vector3, 1)); offset += 12; - elementCount += 3; if (this._useBlendNormal) { - modelMesh._addVertexElement(new VertexElement(`NORMAL_BS${i}`, offset, VertexElementFormat.Vector3, 0)); + modelMesh._addVertexElement(new VertexElement(`NORMAL_BS${i}`, offset, VertexElementFormat.Vector3, 1)); offset += 12; - elementCount += 3; } if (this._useBlendTangent) { - modelMesh._addVertexElement(new VertexElement(`TANGENT_BS${i}`, offset, VertexElementFormat.Vector3, 0)); - elementCount += 3; + modelMesh._addVertexElement(new VertexElement(`TANGENT_BS${i}`, offset, VertexElementFormat.Vector3, 1)); + offset += 12; } } + } - this._lastUpdateLayoutAndCountInfo.setValue(this._blendShapeCount, +this._useBlendNormal, +this._useBlendTangent); - return elementCount; + /** + * @internal + */ + _update(vertexCountChange: boolean, noLongerAccessible: boolean): void { + const { vertexCount } = this._modelMesh; + const useTexture = this._useTextureMode(); + const createHost = this._layoutOrCountChange() || vertexCountChange; + + if (createHost) { + if (useTexture) { + this._createTextureArray(vertexCount); + } else { + this._createVertexBuffers(vertexCount, noLongerAccessible); + } + this._lastCreateHostInfo.setValue(this._blendShapeCount, +this._useBlendNormal, +this._useBlendTangent); + } + if (this._needUpdateData()) { + if (useTexture) { + this._updateTextureArray(vertexCount, createHost); + } else { + this._updateVertexBuffers(vertexCount, createHost); + } + } } /** * @internal */ - _updateDataToVertices( - vertices: Float32Array, - offset: number, - vertexCount: number, - elementCount: number, - force: boolean - ): void { - const blendShapes = this._blendShapes; + _releaseMemoryCache(): void { + const { _blendShapes: blendShapes } = this; + const { length: blendShapeCount } = blendShapes; + + const blendShapeNamesMap = new Array(blendShapeCount); + for (let i = 0; i < blendShapeCount; i++) { + blendShapeNamesMap[i] = blendShapes[i].name; + } + this._blendShapeNames = blendShapeNamesMap; + + this._layoutDirtyFlag.destroy(); + const dataChangedFlags = this._subDataDirtyFlags; + for (let i = 0, n = dataChangedFlags.length; i < n; i++) { + dataChangedFlags[i].destroy(); + } + + this._layoutDirtyFlag = null; + this._subDataDirtyFlags = null; + this._blendShapes = null; + this._vertices = null; + } + + private _createVertexBuffers(vertexCount: number, noLongerAccessible: boolean): void { + const { + _engine: engine, + _modelMesh: modelMesh, + _blendShapeCount: blendShapeCount, + _vertexBuffers: vertexBuffers + } = this; + const vertexFloatCount = this._vertexElementCount * 3; + const vertexByteCount = vertexFloatCount * 4; + const maxCountSingleBuffer = Math.floor(255 / vertexByteCount); // 255: Attribute MaxStride + const bufferCount = Math.ceil(blendShapeCount / maxCountSingleBuffer); + const floatCount = vertexFloatCount * vertexCount * Math.min(maxCountSingleBuffer, blendShapeCount); + + vertexBuffers.length = bufferCount; + this._vertices = new Float32Array(floatCount); + this._maxCountSingleVertexBuffer = maxCountSingleBuffer; + this._storeInVertexBufferInfo.length = blendShapeCount; + + for (let i = 0; i < bufferCount; i++) { + const lastIndex = bufferCount - 1; + const containCount = i === lastIndex ? blendShapeCount - lastIndex * maxCountSingleBuffer : maxCountSingleBuffer; + const stride = containCount * vertexByteCount; + const byteLength = stride * vertexCount; + + const usage = noLongerAccessible ? BufferUsage.Static : BufferUsage.Dynamic; + + const blendShapeBuffer = new Buffer(engine, BufferBindFlag.VertexBuffer, byteLength, usage); + modelMesh._setVertexBufferBinding(i + 1, new VertexBufferBinding(blendShapeBuffer, stride)); + vertexBuffers[i] = blendShapeBuffer; + } + } + + private _createTextureArray(vertexCount: number): void { + const maxTextureSize = this._engine._hardwareRenderer.capability.maxTextureSize; + const vertexPixelStride = this._vertexElementCount; + + let textureWidth = vertexPixelStride * vertexCount; + let textureHeight = 1; + if (textureWidth > maxTextureSize) { + textureHeight = Math.ceil(textureWidth / maxTextureSize); + textureWidth = maxTextureSize; + } + + let blendShapeDataTexture = this._vertexTexture; + const blendShapeCount = this._blendShapes.length; + + blendShapeDataTexture && blendShapeDataTexture.destroy(); + + blendShapeDataTexture = new Texture2DArray( + this._engine, + textureWidth, + textureHeight, + blendShapeCount, + TextureFormat.R32G32B32A32, + false + ); + blendShapeDataTexture.filterMode = TextureFilterMode.Point; + + this._vertices = new Float32Array(blendShapeCount * textureWidth * textureHeight * 4); + this._vertexTexture = blendShapeDataTexture; + this._dataTextureInfo.setValue(vertexPixelStride, textureWidth, textureHeight); + } + + /** + * @internal + */ + _updateVertexBuffers(vertexCount: number, force: boolean): void { + const { _blendShapes: blendShapes, _maxCountSingleVertexBuffer: maxCountSingleBuffer } = this; + const { _vertices: vertices, _vertexBuffers: vertexBuffers, _storeInVertexBufferInfo: storeInfos } = this; + const subDataDirtyFlags = this._subDataDirtyFlags; + const blendShapeFloatStride = this._vertexElementCount * 3; + const blendShapeByteStride = blendShapeFloatStride * 4; + + // @todo: should fix bug when dataChangedFlag is true for (let i = 0, n = blendShapes.length; i < n; i++) { const dataChangedFlag = subDataDirtyFlags[i]; if (force || dataChangedFlag.flag) { @@ -161,9 +344,20 @@ export class BlendShapeManager { throw "BlendShape frame deltaPositions length must same with mesh vertexCount."; } + const bufferIndex = Math.floor(i / maxCountSingleBuffer); + const indexInBuffer = i % maxCountSingleBuffer; + const buffer = vertexBuffers[bufferIndex]; + const bufferFloatStride = buffer.byteLength / (vertexCount * 4); + + let offset = indexInBuffer * blendShapeFloatStride; + + let storeInfo = storeInfos[i]; + storeInfo || (storeInfos[i] = storeInfo = new Vector2()); + storeInfo.setValue(bufferIndex + 1, indexInBuffer * blendShapeByteStride); // BlendShape buffer is start from 1 + const { deltaPositions } = endFrame; for (let j = 0; j < vertexCount; j++) { - const start = elementCount * j + offset; + const start = offset + bufferFloatStride * j; const deltaPosition = deltaPositions[j]; if (deltaPosition) { vertices[start] = deltaPosition.x; @@ -177,7 +371,7 @@ export class BlendShapeManager { const { deltaNormals } = endFrame; if (deltaNormals) { for (let j = 0; j < vertexCount; j++) { - const start = elementCount * j + offset; + const start = offset + bufferFloatStride * j; const deltaNormal = deltaNormals[j]; if (deltaNormal) { vertices[start] = deltaNormal.x; @@ -193,7 +387,7 @@ export class BlendShapeManager { const { deltaTangents } = endFrame; if (deltaTangents) { for (let j = 0; j < vertexCount; j++) { - const start = elementCount * j + offset; + const start = offset + bufferFloatStride * j; const deltaTangent = deltaTangents[j]; if (deltaTangent) { vertices[start] = deltaTangent.x; @@ -204,76 +398,28 @@ export class BlendShapeManager { } offset += 3; } + + if (indexInBuffer === maxCountSingleBuffer - 1 || i === n - 1) { + // @todo: can optimize in setData + buffer.setData(vertices, 0, 0, buffer.byteLength / 4); + } + dataChangedFlag.flag = false; } } } - /** - * @internal - */ - _updateTexture( - layoutOrCountChange: boolean, - vertexCountChange: boolean, - needUpdateBlendShape: boolean, - vertexCount: number - ): void { - let reCreateTexture = !this._dataTexture || layoutOrCountChange || vertexCountChange; - if (reCreateTexture) { - this._createDataTexture(vertexCount); - this._lastUpdateLayoutAndCountInfo.setValue(this._blendShapeCount, +this._useBlendNormal, +this._useBlendTangent); - } - if (needUpdateBlendShape) { - this._updateDataToTexture(vertexCount, reCreateTexture); - } - } - - private _createDataTexture(vertexCount: number): void { - const maxTextureSize = this._engine._hardwareRenderer.capability.maxTextureSize; - - let vertexPixelStride = 1; - this._useBlendNormal && vertexPixelStride++; - this._useBlendTangent && vertexPixelStride++; - - let textureWidth = vertexPixelStride * vertexCount; - let textureHeight = 1; - if (textureWidth > maxTextureSize) { - textureHeight = Math.ceil(textureWidth / maxTextureSize); - textureWidth = maxTextureSize; - } - - let blendShapeDataTexture = this._dataTexture; - const blendShapeCount = this._blendShapes.length; - - blendShapeDataTexture && blendShapeDataTexture.destroy(); - - blendShapeDataTexture = new Texture2DArray( - this._engine, - textureWidth, - textureHeight, - blendShapeCount, - TextureFormat.R32G32B32A32, - false - ); - blendShapeDataTexture.filterMode = TextureFilterMode.Point; - - this._dataTextureBuffer = new Float32Array(blendShapeCount * textureWidth * textureHeight * 4); - this._dataTexture = blendShapeDataTexture; - this._dataTextureInfo.setValue(vertexPixelStride, textureWidth, textureHeight); - } - - private _updateDataToTexture(vertexCount: number, force: boolean): void { + private _updateTextureArray(vertexCount: number, force: boolean): void { const { _blendShapes: blendShapes, - _dataTexture: dataTexture, - _dataTextureBuffer: buffer, + _vertexTexture: vertexTexture, + _vertices: vertices, _subDataDirtyFlags: subDataDirtyFlags } = this; - let offset = 0; for (let i = 0, n = blendShapes.length; i < n; i++) { const subDirtyFlag = subDataDirtyFlags[i]; - const subBlendShapeDataStride = dataTexture.width * dataTexture.height * 4; + const subBlendShapeDataStride = vertexTexture.width * vertexTexture.height * 4; if (force || subDirtyFlag.flag) { const { frames } = blendShapes[i]; const frameCount = frames.length; @@ -281,61 +427,121 @@ export class BlendShapeManager { if (frameCount > 0 && endFrame.deltaPositions.length !== vertexCount) { throw "BlendShape frame deltaPositions length must same with mesh vertexCount."; } + const { deltaPositions, deltaNormals, deltaTangents } = endFrame; - offset = i * subBlendShapeDataStride; + let offset = i * subBlendShapeDataStride; for (let j = 0; j < vertexCount; j++) { const position = deltaPositions[j]; - buffer[offset] = position.x; - buffer[offset + 1] = position.y; - buffer[offset + 2] = position.z; + vertices[offset] = position.x; + vertices[offset + 1] = position.y; + vertices[offset + 2] = position.z; offset += 4; if (deltaNormals) { const normal = deltaNormals[j]; - buffer[offset] = normal.x; - buffer[offset + 1] = normal.y; - buffer[offset + 2] = normal.z; + vertices[offset] = normal.x; + vertices[offset + 1] = normal.y; + vertices[offset + 2] = normal.z; offset += 4; } if (deltaTangents) { const tangent = deltaTangents[j]; - buffer[offset] = tangent.x; - buffer[offset + 1] = tangent.y; - buffer[offset + 2] = tangent.z; + vertices[offset] = tangent.x; + vertices[offset + 1] = tangent.y; + vertices[offset + 2] = tangent.z; offset += 4; } } subDirtyFlag.flag = false; } } - dataTexture.setPixelBuffer(0, buffer); + vertexTexture.setPixelBuffer(0, vertices); } - /** - * @internal - */ - _releaseMemoryCache(): void { - const { _blendShapes: blendShapes } = this; - const blendShapeNamesMap = new Array(blendShapes.length); - for (let i = 0, n = blendShapes.length; i < n; i++) { - blendShapeNamesMap[i] = blendShapes[i].name; + private _updateLayoutChange(blendShape: BlendShape): void { + const notFirst = this._blendShapeCount > 1; + let vertexElementCount = 1; + let useBlendNormal = blendShape._useBlendShapeNormal; + let useBlendTangent = blendShape._useBlendShapeTangent; + if (notFirst) { + useBlendNormal &&= this._useBlendNormal; + useBlendTangent &&= this._useBlendTangent; } - this._blendShapeNames = blendShapeNamesMap; - this._layoutDirtyFlag.destroy(); - const dataChangedFlags = this._subDataDirtyFlags; - for (let i = 0, n = dataChangedFlags.length; i < n; i++) { - dataChangedFlags[i].destroy(); + useBlendNormal && vertexElementCount++; + useBlendTangent && vertexElementCount++; + + this._useBlendNormal = useBlendNormal; + this._useBlendTangent = useBlendTangent; + this._vertexElementCount = vertexElementCount; + } + + private _attributeModeUpdateVertexElement( + vertexElements: VertexElement[], + vertexBufferStoreInfo: Vector2[], + index: number, + condensedIndex: number + ): void { + let elementOffset = this._vertexElementOffset + this._vertexElementCount * condensedIndex; + + let { x: bufferIndex, y: offset } = vertexBufferStoreInfo[index]; + const vertexElement = vertexElements[elementOffset]; + vertexElement.bindingIndex = bufferIndex; + vertexElement.offset = offset; + if (this._useBlendNormal) { + const vertexElement = vertexElements[++elementOffset]; + offset += 12; + vertexElement.bindingIndex = bufferIndex; + vertexElement.offset = offset; } + if (this._useBlendTangent) { + const vertexElement = vertexElements[++elementOffset]; + offset += 12; + vertexElement.bindingIndex = bufferIndex; + vertexElement.offset = offset; + } + } - this._layoutDirtyFlag = null; - this._subDataDirtyFlags = null; - this._blendShapes = null; + private _getVertexBufferModeSupportCount(): number { + if (this._useBlendNormal || this._useBlendTangent) { + return 4; + } else { + return 8; + } } - private _updateUsePropertyFlag(blendShape: BlendShape): void { - this._useBlendNormal = blendShape._useBlendShapeNormal && this._useBlendNormal; - this._useBlendTangent = blendShape._useBlendShapeTangent && this._useBlendTangent; + private _filterCondensedBlendShapeWeights( + blendShapeWeights: Float32Array, + condensedBlendShapeWeights: Float32Array + ): void { + const condensedWeightsCount = condensedBlendShapeWeights.length; + const vertexElements = this._modelMesh._vertexElements; + const vertexBufferStoreInfo = this._storeInVertexBufferInfo; + let thresholdWeight = Number.POSITIVE_INFINITY; + let thresholdIndex: number; + for (let i = 0, n = Math.min(blendShapeWeights.length, this._blendShapeCount); i < n; i++) { + const weight = blendShapeWeights[i]; + if (i < condensedWeightsCount) { + this._attributeModeUpdateVertexElement(vertexElements, vertexBufferStoreInfo, i, i); + condensedBlendShapeWeights[i] = weight; + if (weight < thresholdWeight) { + thresholdWeight = weight; + thresholdIndex = i; + } + } else if (weight > thresholdWeight) { + this._attributeModeUpdateVertexElement(vertexElements, vertexBufferStoreInfo, i, thresholdIndex); + condensedBlendShapeWeights[thresholdIndex] = weight; + + thresholdWeight = Number.POSITIVE_INFINITY; + for (let j = 0; j < condensedWeightsCount; j++) { + const condensedWeight = condensedBlendShapeWeights[j]; + if (condensedWeight < thresholdWeight) { + thresholdWeight = condensedWeight; + thresholdIndex = j; + } + } + } + } } } diff --git a/packages/core/src/mesh/ModelMesh.ts b/packages/core/src/mesh/ModelMesh.ts index 787a9b86f8..98975f2605 100644 --- a/packages/core/src/mesh/ModelMesh.ts +++ b/packages/core/src/mesh/ModelMesh.ts @@ -28,7 +28,7 @@ export class ModelMesh extends Mesh { private _vertexSlotChanged: boolean = true; private _vertexChangeFlag: number = 0; private _indicesChangeFlag: boolean = false; - private _elementCount: number = 0; + private _vertexStrideFloat: number = 0; private _lastUploadVertexCount: number = -1; private _positions: Vector3[] = []; @@ -85,7 +85,7 @@ export class ModelMesh extends Mesh { constructor(engine: Engine, name?: string) { super(engine); this.name = name; - this._blendShapeManager = new BlendShapeManager(engine); + this._blendShapeManager = new BlendShapeManager(engine, this); } /** @@ -433,7 +433,7 @@ export class ModelMesh extends Mesh { } /** - * Upload Mesh Data to the graphics API. + * Upload Mesh Data to GPU. * @param noLongerAccessible - Whether to access data later. If true, you'll never access data anymore (free memory cache) */ uploadData(noLongerAccessible: boolean): void { @@ -441,31 +441,20 @@ export class ModelMesh extends Mesh { throw "Not allowed to access data while accessible is false."; } - const blendManager = this._blendShapeManager; - const blendTextureStore = blendManager._useTextureStore(); - const blendLayoutOrCountChange = blendManager._layoutOrCountChange(); - const blendDataUpdate = blendManager._needUpdateData(); - const blendVertexElementChanged = !blendTextureStore && blendLayoutOrCountChange; - const vertexElementUpdate = this._vertexSlotChanged || blendVertexElementChanged; - - // Vertex element change - if (vertexElementUpdate) { - this._updateVertexElements(blendVertexElementChanged); - } - const { _vertexCount: vertexCount } = this; + const vertexElementChanged = this._updateVertexElements(); const vertexCountChange = this._lastUploadVertexCount !== vertexCount; // Vertex count change const vertexBuffer = this._vertexBufferBindings[0]?._buffer; if (vertexCountChange) { vertexBuffer?.destroy(); - const elementCount = this._elementCount; + const elementCount = this._vertexStrideFloat; const vertexFloatCount = elementCount * vertexCount; const vertices = new Float32Array(vertexFloatCount); this._verticesFloat32 = vertices; this._verticesUint8 = new Uint8Array(vertices.buffer); - this._updateVertices(vertices, !blendTextureStore, true); + this._updateVertices(vertices, true); const newVertexBuffer = new Buffer( this._engine, @@ -477,24 +466,23 @@ export class ModelMesh extends Mesh { this._setVertexBufferBinding(0, new VertexBufferBinding(newVertexBuffer, elementCount * 4)); this._lastUploadVertexCount = vertexCount; } else { - const blendVerticesUpdate = !blendTextureStore && blendDataUpdate; - if (this._vertexChangeFlag & ValueChanged.All || blendVerticesUpdate) { + if (this._vertexChangeFlag & ValueChanged.All) { const vertices = this._verticesFloat32; - this._updateVertices(vertices, blendVerticesUpdate, vertexElementUpdate); + this._updateVertices(vertices, vertexElementChanged); vertexBuffer.setData(vertices); } } - const { _indices } = this; + const { _indices: indices } = this; const indexBuffer = this._indexBufferBinding?._buffer; - if (_indices) { - if (!indexBuffer || _indices.byteLength != indexBuffer.byteLength) { + if (indices) { + if (!indexBuffer || indices.byteLength != indexBuffer.byteLength) { indexBuffer?.destroy(); - const newIndexBuffer = new Buffer(this._engine, BufferBindFlag.IndexBuffer, _indices); + const newIndexBuffer = new Buffer(this._engine, BufferBindFlag.IndexBuffer, indices); this._setIndexBufferBinding(new IndexBufferBinding(newIndexBuffer, this._indicesFormat)); this._indicesChangeFlag = false; } else if (this._indicesChangeFlag) { - indexBuffer.setData(_indices); + indexBuffer.setData(indices); if (this._indexBufferBinding._format !== this._indicesFormat) { this._setIndexBufferBinding(new IndexBufferBinding(indexBuffer, this._indicesFormat)); } @@ -505,9 +493,8 @@ export class ModelMesh extends Mesh { this._setIndexBufferBinding(null); } - if (blendTextureStore) { - blendManager._updateTexture(blendLayoutOrCountChange, vertexCountChange, blendDataUpdate, vertexCount); - } + const { _blendShapeManager: blendShapeManager } = this; + blendShapeManager._blendShapeCount > 0 && blendShapeManager._update(vertexCountChange, noLongerAccessible); if (noLongerAccessible) { this._accessible = false; @@ -524,95 +511,100 @@ export class ModelMesh extends Mesh { this._accessible && this._releaseCache(); } - private _updateVertexElements(blendVertexElementChanged: boolean): void { - this._clearVertexElements(); - this._addVertexElement(POSITION_VERTEX_ELEMENT); - - let offset = 12; - let elementCount = 3; - if (this._normals) { - this._addVertexElement(new VertexElement("NORMAL", offset, VertexElementFormat.Vector3, 0)); - offset += 12; - elementCount += 3; - } - if (this._colors) { - this._addVertexElement(new VertexElement("COLOR_0", offset, VertexElementFormat.Vector4, 0)); - offset += 16; - elementCount += 4; - } - if (this._boneWeights) { - this._addVertexElement(new VertexElement("WEIGHTS_0", offset, VertexElementFormat.Vector4, 0)); - offset += 16; - elementCount += 4; - } - if (this._boneIndices) { - this._addVertexElement(new VertexElement("JOINTS_0", offset, VertexElementFormat.UByte4, 0)); - offset += 4; - elementCount += 1; - } - if (this._tangents) { - this._addVertexElement(new VertexElement("TANGENT", offset, VertexElementFormat.Vector4, 0)); - offset += 16; - elementCount += 4; - } - if (this._uv) { - this._addVertexElement(new VertexElement("TEXCOORD_0", offset, VertexElementFormat.Vector2, 0)); - offset += 8; - elementCount += 2; - } - if (this._uv1) { - this._addVertexElement(new VertexElement("TEXCOORD_1", offset, VertexElementFormat.Vector2, 0)); - offset += 8; - elementCount += 2; - } - if (this._uv2) { - this._addVertexElement(new VertexElement("TEXCOORD_2", offset, VertexElementFormat.Vector2, 0)); - offset += 8; - elementCount += 2; - } - if (this._uv3) { - this._addVertexElement(new VertexElement("TEXCOORD_3", offset, VertexElementFormat.Vector2, 0)); - offset += 8; - elementCount += 2; - } - if (this._uv4) { - this._addVertexElement(new VertexElement("TEXCOORD_4", offset, VertexElementFormat.Vector2, 0)); - offset += 8; - elementCount += 2; - } - if (this._uv5) { - this._addVertexElement(new VertexElement("TEXCOORD_5", offset, VertexElementFormat.Vector2, 0)); - offset += 8; - elementCount += 2; - } - if (this._uv6) { - this._addVertexElement(new VertexElement("TEXCOORD_6", offset, VertexElementFormat.Vector2, 0)); - offset += 8; - elementCount += 2; - } - if (this._uv7) { - this._addVertexElement(new VertexElement("TEXCOORD_7", offset, VertexElementFormat.Vector2, 0)); - offset += 8; - elementCount += 2; - } - this._vertexSlotChanged = false; + private _updateVertexElements(): boolean { + const blendShapeManager = this._blendShapeManager; + const attributeMode = !blendShapeManager._useTextureMode(); - if (blendVertexElementChanged) { - elementCount += this._blendShapeManager._updateVertexElements(this, offset); - } + if (this._vertexSlotChanged || (attributeMode && blendShapeManager._vertexElementsNeedUpdate())) { + let offset = 12; + let elementCount = 3; + this._clearVertexElements(); + this._addVertexElement(POSITION_VERTEX_ELEMENT); - this._elementCount = elementCount; + if (this._normals) { + this._addVertexElement(new VertexElement("NORMAL", offset, VertexElementFormat.Vector3, 0)); + offset += 12; + elementCount += 3; + } + if (this._colors) { + this._addVertexElement(new VertexElement("COLOR_0", offset, VertexElementFormat.Vector4, 0)); + offset += 16; + elementCount += 4; + } + if (this._boneWeights) { + this._addVertexElement(new VertexElement("WEIGHTS_0", offset, VertexElementFormat.Vector4, 0)); + offset += 16; + elementCount += 4; + } + if (this._boneIndices) { + this._addVertexElement(new VertexElement("JOINTS_0", offset, VertexElementFormat.UByte4, 0)); + offset += 4; + elementCount += 1; + } + if (this._tangents) { + this._addVertexElement(new VertexElement("TANGENT", offset, VertexElementFormat.Vector4, 0)); + offset += 16; + elementCount += 4; + } + if (this._uv) { + this._addVertexElement(new VertexElement("TEXCOORD_0", offset, VertexElementFormat.Vector2, 0)); + offset += 8; + elementCount += 2; + } + if (this._uv1) { + this._addVertexElement(new VertexElement("TEXCOORD_1", offset, VertexElementFormat.Vector2, 0)); + offset += 8; + elementCount += 2; + } + if (this._uv2) { + this._addVertexElement(new VertexElement("TEXCOORD_2", offset, VertexElementFormat.Vector2, 0)); + offset += 8; + elementCount += 2; + } + if (this._uv3) { + this._addVertexElement(new VertexElement("TEXCOORD_3", offset, VertexElementFormat.Vector2, 0)); + offset += 8; + elementCount += 2; + } + if (this._uv4) { + this._addVertexElement(new VertexElement("TEXCOORD_4", offset, VertexElementFormat.Vector2, 0)); + offset += 8; + elementCount += 2; + } + if (this._uv5) { + this._addVertexElement(new VertexElement("TEXCOORD_5", offset, VertexElementFormat.Vector2, 0)); + offset += 8; + elementCount += 2; + } + if (this._uv6) { + this._addVertexElement(new VertexElement("TEXCOORD_6", offset, VertexElementFormat.Vector2, 0)); + offset += 8; + elementCount += 2; + } + if (this._uv7) { + this._addVertexElement(new VertexElement("TEXCOORD_7", offset, VertexElementFormat.Vector2, 0)); + offset += 8; + elementCount += 2; + } + if (attributeMode) { + blendShapeManager._blendShapeCount > 0 && blendShapeManager._addVertexElements(this); + } + this._vertexSlotChanged = false; + this._vertexStrideFloat = elementCount; + return true; + } + return false; } - private _updateVertices(vertices: Float32Array, blendVerticesUpdate: boolean, force: boolean): void { + private _updateVertices(vertices: Float32Array, force: boolean): void { // prettier-ignore - const { _elementCount,_vertexCount, _positions, _normals, _colors, _vertexChangeFlag, _boneWeights, _boneIndices, _tangents, _uv, _uv1, _uv2, _uv3, _uv4, _uv5, _uv6, _uv7 } = this; + const { _vertexStrideFloat,_vertexCount, _positions, _normals, _colors, _vertexChangeFlag, _boneWeights, _boneIndices, _tangents, _uv, _uv1, _uv2, _uv3, _uv4, _uv5, _uv6, _uv7 } = this; force && (this._vertexChangeFlag = ValueChanged.All); if (_vertexChangeFlag & ValueChanged.Position) { for (let i = 0; i < _vertexCount; i++) { - const start = _elementCount * i; + const start = _vertexStrideFloat * i; const position = _positions[i]; vertices[start] = position.x; vertices[start + 1] = position.y; @@ -625,7 +617,7 @@ export class ModelMesh extends Mesh { if (_normals) { if (_vertexChangeFlag & ValueChanged.Normal) { for (let i = 0; i < _vertexCount; i++) { - const start = _elementCount * i + offset; + const start = _vertexStrideFloat * i + offset; const normal = _normals[i]; if (normal) { vertices[start] = normal.x; @@ -640,7 +632,7 @@ export class ModelMesh extends Mesh { if (_colors) { if (_vertexChangeFlag & ValueChanged.Color) { for (let i = 0; i < _vertexCount; i++) { - const start = _elementCount * i + offset; + const start = _vertexStrideFloat * i + offset; const color = _colors[i]; if (color) { vertices[start] = color.r; @@ -656,7 +648,7 @@ export class ModelMesh extends Mesh { if (_boneWeights) { if (_vertexChangeFlag & ValueChanged.BoneWeight) { for (let i = 0; i < _vertexCount; i++) { - const start = _elementCount * i + offset; + const start = _vertexStrideFloat * i + offset; const weight = _boneWeights[i]; if (weight) { vertices[start] = weight.x; @@ -673,7 +665,7 @@ export class ModelMesh extends Mesh { if (_vertexChangeFlag & ValueChanged.BoneIndex) { const { _verticesUint8 } = this; for (let i = 0; i < _vertexCount; i++) { - const start = _elementCount * i + offset; + const start = _vertexStrideFloat * i + offset; const joint = _boneIndices[i]; if (joint) { const internalStart = start * 4; @@ -690,7 +682,7 @@ export class ModelMesh extends Mesh { if (_tangents) { if (_vertexChangeFlag & ValueChanged.Tangent) { for (let i = 0; i < _vertexCount; i++) { - const start = _elementCount * i + offset; + const start = _vertexStrideFloat * i + offset; const tangent = _tangents[i]; if (tangent) { vertices[start] = tangent.x; @@ -704,7 +696,7 @@ export class ModelMesh extends Mesh { if (_uv) { if (_vertexChangeFlag & ValueChanged.UV) { for (let i = 0; i < _vertexCount; i++) { - const start = _elementCount * i + offset; + const start = _vertexStrideFloat * i + offset; const uv = _uv[i]; if (uv) { vertices[start] = uv.x; @@ -717,7 +709,7 @@ export class ModelMesh extends Mesh { if (_uv1) { if (_vertexChangeFlag & ValueChanged.UV1) { for (let i = 0; i < _vertexCount; i++) { - const start = _elementCount * i + offset; + const start = _vertexStrideFloat * i + offset; const uv = _uv1[i]; if (uv) { vertices[start] = uv.x; @@ -730,7 +722,7 @@ export class ModelMesh extends Mesh { if (_uv2) { if (_vertexChangeFlag & ValueChanged.UV2) { for (let i = 0; i < _vertexCount; i++) { - const start = _elementCount * i + offset; + const start = _vertexStrideFloat * i + offset; const uv = _uv2[i]; if (uv) { vertices[start] = uv.x; @@ -743,7 +735,7 @@ export class ModelMesh extends Mesh { if (_uv3) { if (_vertexChangeFlag & ValueChanged.UV3) { for (let i = 0; i < _vertexCount; i++) { - const start = _elementCount * i + offset; + const start = _vertexStrideFloat * i + offset; const uv = _uv3[i]; if (uv) { vertices[start] = uv.x; @@ -756,7 +748,7 @@ export class ModelMesh extends Mesh { if (_uv4) { if (_vertexChangeFlag & ValueChanged.UV4) { for (let i = 0; i < _vertexCount; i++) { - const start = _elementCount * i + offset; + const start = _vertexStrideFloat * i + offset; const uv = _uv4[i]; if (uv) { vertices[start] = uv.x; @@ -769,7 +761,7 @@ export class ModelMesh extends Mesh { if (_uv5) { if (_vertexChangeFlag & ValueChanged.UV5) { for (let i = 0; i < _vertexCount; i++) { - const start = _elementCount * i + offset; + const start = _vertexStrideFloat * i + offset; const uv = _uv5[i]; if (uv) { vertices[start] = uv.x; @@ -782,7 +774,7 @@ export class ModelMesh extends Mesh { if (_uv6) { if (_vertexChangeFlag & ValueChanged.UV6) { for (let i = 0; i < _vertexCount; i++) { - const start = _elementCount * i + offset; + const start = _vertexStrideFloat * i + offset; const uv = _uv6[i]; if (uv) { vertices[start] = uv.x; @@ -795,7 +787,7 @@ export class ModelMesh extends Mesh { if (_uv7) { if (_vertexChangeFlag & ValueChanged.UV7) { for (let i = 0; i < _vertexCount; i++) { - const start = _elementCount * i + offset; + const start = _vertexStrideFloat * i + offset; const uv = _uv7[i]; if (uv) { vertices[start] = uv.x; @@ -806,10 +798,6 @@ export class ModelMesh extends Mesh { offset += 2; } this._vertexChangeFlag = 0; - - if (blendVerticesUpdate) { - this._blendShapeManager._updateDataToVertices(vertices, offset, _vertexCount, _elementCount, force); - } } private _releaseCache(): void { diff --git a/packages/core/src/mesh/SkinnedMeshRenderer.ts b/packages/core/src/mesh/SkinnedMeshRenderer.ts index 70421a8e58..3954ec95a8 100644 --- a/packages/core/src/mesh/SkinnedMeshRenderer.ts +++ b/packages/core/src/mesh/SkinnedMeshRenderer.ts @@ -15,17 +15,9 @@ import { Skin } from "./Skin"; * SkinnedMeshRenderer. */ export class SkinnedMeshRenderer extends MeshRenderer { - private static _blendShapeMacro = Shader.getMacroByName("OASIS_BLENDSHAPE"); - private static _blendShapeTextureMacro = Shader.getMacroByName("OASIS_BLENDSHAPE_TEXTURE"); - private static _blendShapeNormalMacro = Shader.getMacroByName("OASIS_BLENDSHAPE_NORMAL"); - private static _blendShapeTangentMacro = Shader.getMacroByName("OASIS_BLENDSHAPE_TANGENT"); - private static _jointCountProperty = Shader.getPropertyByName("u_jointCount"); private static _jointSamplerProperty = Shader.getPropertyByName("u_jointSampler"); private static _jointMatrixProperty = Shader.getPropertyByName("u_jointMatrix"); - private static _blendShapeWeightsProperty = Shader.getPropertyByName("u_blendShapeWeights"); - private static _blendShapeTextureProperty = Shader.getPropertyByName("u_blendShapeTexture"); - private static _blendShapeTextureInfoProperty = Shader.getPropertyByName("u_blendShapeTextureInfo"); private static _maxJoints: number = 0; @@ -44,7 +36,8 @@ export class SkinnedMeshRenderer extends MeshRenderer { /** Whether to use joint texture. Automatically used when the device can't support the maximum number of bones. */ private _useJointTexture: boolean = false; private _skin: Skin; - private _blendShapeWeights: Float32Array; + _blendShapeWeights: Float32Array; + _condensedBlendShapeWeights: Float32Array; /** * The weights of the BlendShapes. @@ -79,34 +72,8 @@ export class SkinnedMeshRenderer extends MeshRenderer { shaderData.setFloatArray(SkinnedMeshRenderer._jointMatrixProperty, this.matrixPalette); } - const blendShapeManager = (this.mesh)._blendShapeManager; - if (blendShapeManager._blendShapeCount > 0) { - shaderData.enableMacro(SkinnedMeshRenderer._blendShapeMacro); - shaderData.enableMacro("OASIS_BLENDSHAPE_COUNT", blendShapeManager._blendShapeCount.toString()); - if (blendShapeManager._useTextureStore()) { - shaderData.enableMacro(SkinnedMeshRenderer._blendShapeTextureMacro); - shaderData.setTexture(SkinnedMeshRenderer._blendShapeTextureProperty, blendShapeManager._dataTexture); - shaderData.setVector3(SkinnedMeshRenderer._blendShapeTextureInfoProperty, blendShapeManager._dataTextureInfo); - } else { - shaderData.disableMacro(SkinnedMeshRenderer._blendShapeTextureMacro); - } - - shaderData.setFloatArray(SkinnedMeshRenderer._blendShapeWeightsProperty, this._blendShapeWeights); - - if (blendShapeManager._useBlendNormal) { - shaderData.enableMacro(SkinnedMeshRenderer._blendShapeNormalMacro); - } else { - shaderData.disableMacro(SkinnedMeshRenderer._blendShapeNormalMacro); - } - if (blendShapeManager._useBlendTangent) { - shaderData.enableMacro(SkinnedMeshRenderer._blendShapeTangentMacro); - } else { - shaderData.disableMacro(SkinnedMeshRenderer._blendShapeTangentMacro); - } - } else { - shaderData.disableMacro(SkinnedMeshRenderer._blendShapeMacro); - shaderData.disableMacro("OASIS_BLENDSHAPE_COUNT"); - } + const mesh = this.mesh; + mesh._blendShapeManager._updateShaderData(shaderData, this); } /** diff --git a/packages/design/package.json b/packages/design/package.json index 68919b6d00..e519651a8b 100755 --- a/packages/design/package.json +++ b/packages/design/package.json @@ -1,6 +1,6 @@ { "name": "@oasis-engine/design", - "version": "0.7.0-beta.4", + "version": "0.7.0-beta.6", "license": "MIT", "main": "dist/main.js", "module": "dist/module.js", @@ -13,6 +13,6 @@ ], "types": "types/index.d.ts", "dependencies": { - "@oasis-engine/math": "0.7.0-beta.4" + "@oasis-engine/math": "0.7.0-beta.6" } } diff --git a/packages/draco/package.json b/packages/draco/package.json index 4932303682..2a1ce04b40 100644 --- a/packages/draco/package.json +++ b/packages/draco/package.json @@ -1,6 +1,6 @@ { "name": "@oasis-engine/draco", - "version": "0.7.0-beta.4", + "version": "0.7.0-beta.6", "license": "MIT", "scripts": { "b:types": "tsc" @@ -13,6 +13,6 @@ "types/**/*" ], "dependencies": { - "@oasis-engine/core": "0.7.0-beta.4" + "@oasis-engine/core": "0.7.0-beta.6" } } diff --git a/packages/loader/package.json b/packages/loader/package.json index 2e44ff4b7c..46b9ca84ce 100755 --- a/packages/loader/package.json +++ b/packages/loader/package.json @@ -1,6 +1,6 @@ { "name": "@oasis-engine/loader", - "version": "0.7.0-beta.4", + "version": "0.7.0-beta.6", "license": "MIT", "types": "types/index.d.ts", "scripts": { @@ -14,9 +14,9 @@ "types/**/*" ], "dependencies": { - "@oasis-engine/core": "0.7.0-beta.4", - "@oasis-engine/draco": "0.7.0-beta.4", - "@oasis-engine/math": "0.7.0-beta.4", - "@oasis-engine/rhi-webgl": "0.7.0-beta.4" + "@oasis-engine/core": "0.7.0-beta.6", + "@oasis-engine/draco": "0.7.0-beta.6", + "@oasis-engine/math": "0.7.0-beta.6", + "@oasis-engine/rhi-webgl": "0.7.0-beta.6" } } diff --git a/packages/math/package.json b/packages/math/package.json index 463d9d2ebb..30707c9ec6 100755 --- a/packages/math/package.json +++ b/packages/math/package.json @@ -1,6 +1,6 @@ { "name": "@oasis-engine/math", - "version": "0.7.0-beta.4", + "version": "0.7.0-beta.6", "license": "MIT", "main": "dist/main.js", "module": "dist/module.js", diff --git a/packages/oasis-engine/package.json b/packages/oasis-engine/package.json index e313ca9ba2..ea346a895f 100755 --- a/packages/oasis-engine/package.json +++ b/packages/oasis-engine/package.json @@ -1,6 +1,6 @@ { "name": "oasis-engine", - "version": "0.7.0-beta.4", + "version": "0.7.0-beta.6", "license": "MIT", "scripts": { "b:types": "tsc" @@ -14,9 +14,9 @@ "types/**/*" ], "dependencies": { - "@oasis-engine/core": "0.7.0-beta.4", - "@oasis-engine/loader": "0.7.0-beta.4", - "@oasis-engine/math": "0.7.0-beta.4", - "@oasis-engine/rhi-webgl": "0.7.0-beta.4" + "@oasis-engine/core": "0.7.0-beta.6", + "@oasis-engine/loader": "0.7.0-beta.6", + "@oasis-engine/math": "0.7.0-beta.6", + "@oasis-engine/rhi-webgl": "0.7.0-beta.6" } } diff --git a/packages/physics-lite/package.json b/packages/physics-lite/package.json index afad790f11..cfd97e4736 100644 --- a/packages/physics-lite/package.json +++ b/packages/physics-lite/package.json @@ -1,6 +1,6 @@ { "name": "@oasis-engine/physics-lite", - "version": "0.7.0-beta.4", + "version": "0.7.0-beta.6", "license": "MIT", "main": "dist/main.js", "module": "dist/module.js", @@ -15,8 +15,8 @@ "types/**/*" ], "dependencies": { - "@oasis-engine/design": "0.7.0-beta.4", - "oasis-engine": "0.7.0-beta.4" + "@oasis-engine/design": "0.7.0-beta.6", + "oasis-engine": "0.7.0-beta.6" }, "publishConfig": { "access": "public" diff --git a/packages/physics-physx/package.json b/packages/physics-physx/package.json index afb0edbfe3..0aa210d598 100644 --- a/packages/physics-physx/package.json +++ b/packages/physics-physx/package.json @@ -1,6 +1,6 @@ { "name": "@oasis-engine/physics-physx", - "version": "0.7.0-beta.4", + "version": "0.7.0-beta.6", "license": "MIT", "main": "dist/main.js", "module": "dist/module.js", @@ -15,8 +15,8 @@ "types/**/*" ], "dependencies": { - "@oasis-engine/design": "0.7.0-beta.4", - "oasis-engine": "0.7.0-beta.4" + "@oasis-engine/design": "0.7.0-beta.6", + "oasis-engine": "0.7.0-beta.6" }, "publishConfig": { "access": "public" diff --git a/packages/rhi-webgl/package.json b/packages/rhi-webgl/package.json index 6ec31dfa66..4d045f6c53 100755 --- a/packages/rhi-webgl/package.json +++ b/packages/rhi-webgl/package.json @@ -1,6 +1,6 @@ { "name": "@oasis-engine/rhi-webgl", - "version": "0.7.0-beta.4", + "version": "0.7.0-beta.6", "license": "MIT", "main": "dist/main.js", "module": "dist/module.js", @@ -14,10 +14,10 @@ "types/**/*" ], "dependencies": { - "@oasis-engine/core": "0.7.0-beta.4", - "@oasis-engine/math": "0.7.0-beta.4" + "@oasis-engine/core": "0.7.0-beta.6", + "@oasis-engine/math": "0.7.0-beta.6" }, "devDependencies": { - "@oasis-engine/design": "0.7.0-beta.4" + "@oasis-engine/design": "0.7.0-beta.6" } } diff --git a/packages/rhi-webgl/src/GLPrimitive.ts b/packages/rhi-webgl/src/GLPrimitive.ts index d7eb3aef93..2212723dd3 100644 --- a/packages/rhi-webgl/src/GLPrimitive.ts +++ b/packages/rhi-webgl/src/GLPrimitive.ts @@ -14,7 +14,7 @@ import { WebGLRenderer } from "./WebGLRenderer"; * GL platform primitive. */ export class GLPrimitive implements IPlatformPrimitive { - protected attribLocArray: number[]; + protected attribLocArray: number[] = []; protected readonly _primitive: Mesh; protected readonly canUseInstancedArrays: boolean; @@ -35,8 +35,10 @@ export class GLPrimitive implements IPlatformPrimitive { draw(shaderProgram: any, subMesh: SubMesh): void { const gl = this.gl; const primitive = this._primitive; + // @ts-ignore + const useVao = this._useVao && primitive._enableVAO; - if (this._useVao) { + if (useVao) { if (!this.vao.has(shaderProgram.id)) { this.registerVAO(shaderProgram); } @@ -52,7 +54,7 @@ export class GLPrimitive implements IPlatformPrimitive { if (!_instanceCount) { if (_indexBufferBinding) { - if (this._useVao) { + if (useVao) { gl.drawElements(topology, count, _glIndexType, start * _glIndexByteCount); } else { const { _nativeBuffer } = _indexBufferBinding.buffer; @@ -109,7 +111,7 @@ export class GLPrimitive implements IPlatformPrimitive { // @ts-ignore const vertexBufferBindings = primitive._vertexBufferBindings; - this.attribLocArray = []; + this.attribLocArray.length = 0; const attributeLocation = shaderProgram.attributeLocation; const attributes = primitive._vertexElementMap; @@ -131,8 +133,8 @@ export class GLPrimitive implements IPlatformPrimitive { } gl.enableVertexAttribArray(loc); - const { size, type, normalized } = element._glElementInfo; - gl.vertexAttribPointer(loc, size, type, normalized, stride, element.offset); + const elementInfo = element._glElementInfo; + gl.vertexAttribPointer(loc, elementInfo.size, elementInfo.type, elementInfo.normalized, stride, element.offset); if (this.canUseInstancedArrays) { gl.vertexAttribDivisor(loc, element.instanceStepRate); }