diff --git a/packages/core/src/ComponentsManager.ts b/packages/core/src/ComponentsManager.ts index 22df91fe8d..5f5d2c8bcc 100644 --- a/packages/core/src/ComponentsManager.ts +++ b/packages/core/src/ComponentsManager.ts @@ -3,6 +3,7 @@ import { Component } from "./Component"; import { DisorderedArray } from "./DisorderedArray"; import { Renderer } from "./Renderer"; import { Script } from "./Script"; +import { Animator } from "./animation"; /** * The manager of the components. @@ -21,7 +22,7 @@ export class ComponentsManager { private _disposeDestroyScripts: Script[] = []; // Animation - private _onUpdateAnimations: DisorderedArray = new DisorderedArray(); + private _onUpdateAnimations: DisorderedArray = new DisorderedArray(); // Render private _onUpdateRenderers: DisorderedArray = new DisorderedArray(); @@ -84,18 +85,14 @@ export class ComponentsManager { script._onPhysicsUpdateIndex = -1; } - addOnUpdateAnimations(animation: Component): void { - //@ts-ignore + addOnUpdateAnimations(animation: Animator): void { animation._onUpdateIndex = this._onUpdateAnimations.length; this._onUpdateAnimations.add(animation); } - removeOnUpdateAnimations(animation: Component): void { - //@ts-ignore + removeOnUpdateAnimations(animation: Animator): void { const replaced = this._onUpdateAnimations.deleteByIndex(animation._onUpdateIndex); - //@ts-ignore replaced && (replaced._onUpdateIndex = animation._onUpdateIndex); - //@ts-ignore animation._onUpdateIndex = -1; } @@ -151,9 +148,8 @@ export class ComponentsManager { } callAnimationUpdate(deltaTime: number): void { - this._onUpdateAnimations.forEach((element: Component) => { - //@ts-ignore - element.update(deltaTime); + this._onUpdateAnimations.forEach((element: Animator) => { + element.engine.time.frameCount > element._playFrameCount && element.update(deltaTime); }); } diff --git a/packages/core/src/animation/Animator.ts b/packages/core/src/animation/Animator.ts index 651bafed19..db2b614955 100644 --- a/packages/core/src/animation/Animator.ts +++ b/packages/core/src/animation/Animator.ts @@ -31,6 +31,11 @@ export class Animator extends Component { @assignmentClone speed: number = 1.0; + /** @internal */ + _playFrameCount: number; + /** @internal */ + _onUpdateIndex: number = -1; + protected _animatorController: AnimatorController; @ignoreClone @@ -85,6 +90,8 @@ export class Animator extends Component { this._reset(); } + this._playFrameCount = this.engine.time.frameCount; + const stateInfo = this._getAnimatorStateInfo(stateName, layerIndex); const { state, layerIndex: playLayerIndex } = stateInfo; @@ -103,6 +110,8 @@ export class Animator extends Component { animatorLayerData.layerState = LayerState.Playing; animatorLayerData.srcPlayData.reset(state, animatorStateData, state._getDuration() * normalizedTimeOffset); + + this.update(0); } /** @@ -122,12 +131,16 @@ export class Animator extends Component { this._reset(); } + this._playFrameCount = this.engine.time.frameCount; + const { state, layerIndex: playLayerIndex } = this._getAnimatorStateInfo(stateName, layerIndex); const { manuallyTransition } = this._getAnimatorLayerData(playLayerIndex); manuallyTransition.duration = normalizedTransitionDuration; manuallyTransition.offset = normalizedTimeOffset; manuallyTransition.destinationState = state; - this._crossFadeByTransition(manuallyTransition, layerIndex); + if (this._crossFadeByTransition(manuallyTransition, layerIndex)) { + this.update(0); + } } /** @@ -158,8 +171,6 @@ export class Animator extends Component { return; } - deltaTime *= this.speed; - this._updateMark++; for (let i = 0, n = animatorController.layers.length; i < n; i++) { @@ -448,7 +459,10 @@ export class Animator extends Component { const { state, playState: lastPlayState, clipTime: lastClipTime } = playData; const { _curveBindings: curveBindings } = state.clip; - playData.update(this.speed < 0); + const speed = state.speed * this.speed; + playData.frameTime += speed * delta; + + playData.update(speed < 0); const { clipTime, playState } = playData; const finished = playState === AnimatorStatePlayState.Finished; @@ -471,12 +485,9 @@ export class Animator extends Component { } } - playData.frameTime += state.speed * delta; - if (playState === AnimatorStatePlayState.Finished) { layerData.layerState = LayerState.Finished; } - eventHandlers.length && this._fireAnimationEvents(playData, eventHandlers, lastClipTime, clipTime); if (lastPlayState === AnimatorStatePlayState.UnStarted) { @@ -499,6 +510,7 @@ export class Animator extends Component { additive: boolean, aniUpdate: boolean ) { + const { speed } = this; const { crossLayerOwnerCollection } = layerData; const { _curveBindings: srcCurves } = srcPlayData.state.clip; const { state: srcState, stateData: srcStateData, playState: lastSrcPlayState } = srcPlayData; @@ -513,8 +525,14 @@ export class Animator extends Component { let crossWeight = Math.abs(destPlayData.frameTime) / duration; (crossWeight >= 1.0 || duration === 0) && (crossWeight = 1.0); - srcPlayData.update(this.speed < 0); - destPlayData.update(this.speed < 0); + const srcSpeed = srcState.speed * speed; + const destSpeed = destState.speed * speed; + + srcPlayData.frameTime += srcSpeed * delta; + destPlayData.frameTime += destSpeed * delta; + + srcPlayData.update(srcSpeed < 0); + destPlayData.update(destSpeed < 0); const { clipTime: srcClipTime, playState: srcPlayState } = srcPlayData; const { clipTime: destClipTime, playState: destPlayState } = destPlayData; @@ -545,7 +563,7 @@ export class Animator extends Component { } } - this._updateCrossFadeData(layerData, crossWeight, delta, false); + this._updateCrossFadeData(layerData, crossWeight); srcEventHandlers.length && this._fireAnimationEvents(srcPlayData, srcEventHandlers, lastSrcClipTime, srcClipTime); destEventHandlers.length && @@ -589,13 +607,12 @@ export class Animator extends Component { let crossWeight = Math.abs(destPlayData.frameTime) / duration; (crossWeight >= 1.0 || duration === 0) && (crossWeight = 1.0); - destPlayData.update(this.speed < 0); - - const { playState } = destPlayData; + const speed = state.speed * this.speed; - this._updateCrossFadeData(layerData, crossWeight, delta, true); + destPlayData.frameTime += speed * delta; + destPlayData.update(speed < 0); - const { clipTime: destClipTime } = destPlayData; + const { clipTime: destClipTime, playState } = destPlayData; const finished = playState === AnimatorStatePlayState.Finished; // When the animator is culled (aniUpdate=false), if the play state has finished, the final value needs to be calculated and saved to be applied directly. @@ -621,6 +638,8 @@ export class Animator extends Component { } } + this._updateCrossFadeData(layerData, crossWeight); + //@todo: srcState is missing the judgment of the most recent period." eventHandlers.length && this._fireAnimationEvents(destPlayData, eventHandlers, lastDestClipTime, destClipTime); @@ -659,9 +678,8 @@ export class Animator extends Component { } } - private _updateCrossFadeData(layerData: AnimatorLayerData, crossWeight: number, delta: number, fixed: boolean): void { + private _updateCrossFadeData(layerData: AnimatorLayerData, crossWeight: number): void { const { destPlayData } = layerData; - destPlayData.frameTime += destPlayData.state.speed * delta; if (crossWeight === 1.0) { if (destPlayData.playState === AnimatorStatePlayState.Finished) { layerData.layerState = LayerState.Finished; @@ -670,8 +688,6 @@ export class Animator extends Component { } layerData.switchPlayData(); layerData.crossFadeTransition = null; - } else { - fixed || (layerData.srcPlayData.frameTime += layerData.srcPlayData.state.speed * delta); } } @@ -709,16 +725,16 @@ export class Animator extends Component { } } - private _crossFadeByTransition(transition: AnimatorStateTransition, layerIndex: number): void { + private _crossFadeByTransition(transition: AnimatorStateTransition, layerIndex: number): boolean { const { name } = transition.destinationState; const stateInfo = this._getAnimatorStateInfo(name, layerIndex); const { state: crossState, layerIndex: playLayerIndex } = stateInfo; if (!crossState) { - return; + return false; } if (!crossState.clip) { Logger.warn(`The state named ${name} has no AnimationClip data.`); - return; + return false; } const animatorLayerData = this._getAnimatorLayerData(playLayerIndex); @@ -752,6 +768,8 @@ export class Animator extends Component { } animatorLayerData.crossFadeTransition = transition; + + return true; } private _fireAnimationEvents( @@ -762,7 +780,8 @@ export class Animator extends Component { ): void { const { state } = playState; const clipDuration = state.clip.length; - if (this.speed >= 0) { + + if (this.speed * state.speed >= 0) { if (clipTime < lastClipTime) { this._fireSubAnimationEvents(playState, eventHandlers, lastClipTime, state.clipEndTime * clipDuration); playState.currentEventIndex = 0; diff --git a/tests/src/core/Animator.test.ts b/tests/src/core/Animator.test.ts index b3dd7fbf12..e5a875a867 100644 --- a/tests/src/core/Animator.test.ts +++ b/tests/src/core/Animator.test.ts @@ -108,18 +108,17 @@ describe("Animator test", function () { renderer._renderFrameCount = Infinity; }); + animator.cullingMode = 1; + expect(animator.cullingMode).to.eq(1); + animator.play("Run"); let animatorLayerData = animator["_animatorLayersData"]; const srcPlayData = animatorLayerData[0]?.srcPlayData; - - animator.cullingMode = 1; - expect(animator.cullingMode).to.eq(1); animator.update(5); const curveOwner = srcPlayData.stateData.curveLayerOwner[0].curveOwner; const initValue = curveOwner.defaultValue; const currentValue = curveOwner.referenceTargetValue; - expect(Quaternion.equals(initValue, currentValue)).to.eq(true); animator.cullingMode = 0; diff --git a/tests/src/core/mesh/BufferMesh.test.ts b/tests/src/core/mesh/BufferMesh.test.ts new file mode 100644 index 0000000000..af93990c65 --- /dev/null +++ b/tests/src/core/mesh/BufferMesh.test.ts @@ -0,0 +1,161 @@ +import { + BufferMesh, + Buffer, + Entity, + VertexBufferBinding, + IndexBufferBinding, + IndexFormat, + BufferBindFlag, + BufferUsage, + MeshRenderer, + VertexElement, + VertexElementFormat +} from "@galacean/engine-core"; +import { WebGLEngine } from "@galacean/engine-rhi-webgl"; +import { expect } from "chai"; + +describe("BufferMesh", () => { + let engine: WebGLEngine; + let rootEntity: Entity; + + before(async () => { + engine = await WebGLEngine.create({ canvas: document.createElement("canvas") }); + const scene = engine.sceneManager.activeScene; + rootEntity = scene.createRootEntity(); + + engine.run(); + }); + + it("instanceCount", () => { + const mesh = new BufferMesh(engine, "buffermesh-instancecount"); + expect(mesh.instanceCount).to.equal(0); + mesh.instanceCount = 100; + expect(mesh.instanceCount).to.equal(100); + }); + + it("VertexElements", () => { + const mesh = new BufferMesh(engine, "buffermesh-vertexelements"); + expect(mesh.vertexElements.length).to.equal(0); + mesh.setVertexElements([ + new VertexElement("POSITION", 0, VertexElementFormat.Vector3, 0), + new VertexElement("NORMAL", 0, VertexElementFormat.Vector3, 12), + new VertexElement("TEXCOORD_0", 0, VertexElementFormat.Vector2, 24), + new VertexElement("COLOR_0", 0, VertexElementFormat.Vector4, 88) + ]); + expect(mesh.vertexElements.length).to.equal(4); + expect(mesh.vertexElements[0].semantic).to.equal("POSITION"); + expect(mesh.vertexElements[0].bindingIndex).to.equal(0); + expect(mesh.vertexElements[0].format).to.equal(VertexElementFormat.Vector3); + expect(mesh.vertexElements[0].offset).to.equal(0); + expect(mesh.vertexElements[1].semantic).to.equal("NORMAL"); + expect(mesh.vertexElements[1].bindingIndex).to.equal(12); + expect(mesh.vertexElements[1].format).to.equal(VertexElementFormat.Vector3); + expect(mesh.vertexElements[1].offset).to.equal(0); + expect(mesh.vertexElements[2].semantic).to.equal("TEXCOORD_0"); + expect(mesh.vertexElements[2].bindingIndex).to.equal(24); + expect(mesh.vertexElements[2].format).to.equal(VertexElementFormat.Vector2); + expect(mesh.vertexElements[2].offset).to.equal(0); + expect(mesh.vertexElements[3].semantic).to.equal("COLOR_0"); + expect(mesh.vertexElements[3].bindingIndex).to.equal(88); + expect(mesh.vertexElements[3].format).to.equal(VertexElementFormat.Vector4); + expect(mesh.vertexElements[3].offset).to.equal(0); + }); + + it("setVertexBufferBinding", () => { + const buffer = new Buffer(engine, BufferBindFlag.VertexBuffer, 4, BufferUsage.Dynamic); + buffer.setData(new Float32Array([255, 128, 64, 32])); + const buffer2 = new Buffer(engine, BufferBindFlag.VertexBuffer, 4, BufferUsage.Dynamic); + buffer2.setData(new Float32Array([128, 64, 32, 16])); + + const mrEntity1 = rootEntity.createChild("MeshRenderer1"); + const mr1 = mrEntity1.addComponent(MeshRenderer); + const mrEntity2 = rootEntity.createChild("MeshRenderer2"); + const mr2 = mrEntity2.addComponent(MeshRenderer); + + const mesh = new BufferMesh(engine, "buffermesh-vertexbufferbinding"); + mr1.mesh = mesh; + mr2.mesh = mesh; + + // Test setVertexBufferBinding(vertexBufferBindings: VertexBufferBinding, index?: number) method. + mesh.setVertexBufferBinding(new VertexBufferBinding(buffer, 1)); + expect(mesh.vertexBufferBindings.length).to.equal(1); + expect(mesh.vertexBufferBindings[0].buffer.refCount).to.equal(2); + expect(mesh.vertexBufferBindings[0].stride).to.equal(1); + + mesh.setVertexBufferBinding(new VertexBufferBinding(buffer2, 1), 3); + expect(mesh.vertexBufferBindings.length).to.equal(4); + expect(mesh.vertexBufferBindings[3].buffer.refCount).to.equal(2); + expect(mesh.vertexBufferBindings[3].stride).to.equal(1); + + // Test setVertexBufferBinding(vertexBuffer: Buffer, stride: number, index?: number) method. + mesh.setVertexBufferBinding(buffer, 2, 1); + expect(mesh.vertexBufferBindings.length).to.equal(4); + expect(mesh.vertexBufferBindings[1].buffer.refCount).to.equal(4); + expect(mesh.vertexBufferBindings[1].stride).to.equal(2); + + mesh.setVertexBufferBinding(buffer2, 1, 2); + expect(mesh.vertexBufferBindings.length).to.equal(4); + expect(mesh.vertexBufferBindings[2].buffer.refCount).to.equal(4); + expect(mesh.vertexBufferBindings[2].stride).to.equal(1); + + // Test setVertexBufferBindings(vertexBufferBindings: VertexBufferBinding[], firstIndex: number = 0) method. + const buffer3 = new Buffer(engine, BufferBindFlag.VertexBuffer, 4, BufferUsage.Dynamic); + buffer3.setData(new Float32Array([128, 32, 255, 64])); + const buffer4 = new Buffer(engine, BufferBindFlag.VertexBuffer, 4, BufferUsage.Dynamic); + buffer4.setData(new Float32Array([64, 32, 16, 16])); + mesh.setVertexBufferBindings( + [ + new VertexBufferBinding(buffer3, 1), + new VertexBufferBinding(buffer, 2), + new VertexBufferBinding(buffer2, 1), + new VertexBufferBinding(buffer4, 1) + ], + 1 + ); + expect(mesh.vertexBufferBindings.length).to.equal(5); + expect(mesh.vertexBufferBindings[0].buffer).to.equal(buffer); + expect(mesh.vertexBufferBindings[0].buffer.refCount).to.equal(4); + expect(mesh.vertexBufferBindings[0].stride).to.equal(1); + expect(mesh.vertexBufferBindings[1].buffer).to.equal(buffer3); + expect(mesh.vertexBufferBindings[1].buffer.refCount).to.equal(2); + expect(mesh.vertexBufferBindings[1].stride).to.equal(1); + expect(mesh.vertexBufferBindings[2].buffer).to.equal(buffer); + expect(mesh.vertexBufferBindings[2].buffer.refCount).to.equal(4); + expect(mesh.vertexBufferBindings[2].stride).to.equal(2); + expect(mesh.vertexBufferBindings[3].buffer).to.equal(buffer2); + expect(mesh.vertexBufferBindings[3].buffer.refCount).to.equal(2); + expect(mesh.vertexBufferBindings[3].stride).to.equal(1); + expect(mesh.vertexBufferBindings[4].buffer).to.equal(buffer4); + expect(mesh.vertexBufferBindings[4].buffer.refCount).to.equal(2); + expect(mesh.vertexBufferBindings[4].stride).to.equal(1); + }); + + it("setIndexBufferBinding", () => { + const buffer = new Buffer(engine, BufferBindFlag.IndexBuffer, 4, BufferUsage.Dynamic); + buffer.setData(new Uint16Array([0, 1, 2, 3])); + const buffer2 = new Buffer(engine, BufferBindFlag.IndexBuffer, 4, BufferUsage.Dynamic); + buffer2.setData(new Uint16Array([0, 1, 2, 3])); + + const mrEntity1 = rootEntity.createChild("MeshRenderer3"); + const mr1 = mrEntity1.addComponent(MeshRenderer); + const mrEntity2 = rootEntity.createChild("MeshRenderer4"); + const mr2 = mrEntity2.addComponent(MeshRenderer); + + const mesh = new BufferMesh(engine, "buffermesh-indexbufferbinding"); + mr1.mesh = mesh; + mr2.mesh = mesh; + + // Test setIndexBufferBinding(buffer: Buffer, format: IndexFormat) method. + mesh.setIndexBufferBinding(buffer, IndexFormat.UInt16); + expect(mesh.indexBufferBinding.buffer.refCount).to.equal(2); + expect(mesh.indexBufferBinding.format).to.equal(IndexFormat.UInt16); + + // Test setIndexBufferBinding(binding: IndexBufferBinding | null) method. + mesh.setIndexBufferBinding(new IndexBufferBinding(buffer2, IndexFormat.UInt8)); + expect(mesh.indexBufferBinding.buffer.refCount).to.equal(2); + expect(mesh.indexBufferBinding.format).to.equal(IndexFormat.UInt8); + + mesh.setIndexBufferBinding(null); + expect(mesh.indexBufferBinding).to.equal(null); + }); +}); diff --git a/tests/src/core/mesh/PrimitiveMesh.test.ts b/tests/src/core/mesh/PrimitiveMesh.test.ts new file mode 100644 index 0000000000..56e5b3ce4c --- /dev/null +++ b/tests/src/core/mesh/PrimitiveMesh.test.ts @@ -0,0 +1,191 @@ +import { Camera, GLCapabilityType, PrimitiveMesh } from "@galacean/engine-core"; +import { WebGLEngine } from "@galacean/engine-rhi-webgl"; +import { Vector3 } from "@galacean/engine-math"; +import { expect } from "chai"; + +describe("PrimitiveMesh", () => { + let engine: WebGLEngine; + + before(async () => { + engine = await WebGLEngine.create({ canvas: document.createElement("canvas") }); + engine.canvas.resizeByClientSize(); + + const rootEntity = engine.sceneManager.activeScene.createRootEntity("root"); + const cameraEntity = rootEntity.createChild("root"); + cameraEntity.addComponent(Camera); + cameraEntity.transform.setPosition(0, 0, -10); + cameraEntity.transform.lookAt(new Vector3()); + + engine.run(); + }); + + it("createSphere", () => { + // Test that createSphere works correctly. + const radius = 1.333; + const segments = 20.555556; + const floorSegments = Math.floor(segments); + const sphereMesh = PrimitiveMesh.createSphere(engine, radius, segments, false); + + const count = floorSegments + 1; + expect(sphereMesh.vertexCount).equal(count * count); + expect(sphereMesh.vertexElements.length).equal(4); + expect(sphereMesh.bounds.min).to.deep.include({ x: -radius, y: -radius, z: -radius }); + expect(sphereMesh.bounds.max).to.deep.include({ x: radius, y: radius, z: radius }); + expect(sphereMesh.getIndices().length).equal(floorSegments * floorSegments * 6); + expect(sphereMesh.vertexBufferBindings.length).equal(1); + }); + + it("createCuboid", () => { + // Test that createCuboid works correctly. + const width = 2.3; + const height = 3.2; + const depth = 4.5; + const cuboidMesh = PrimitiveMesh.createCuboid(engine, width, height, depth, false); + + expect(cuboidMesh.vertexCount).equal(24); + expect(cuboidMesh.vertexElements.length).equal(4); + expect(cuboidMesh.bounds.min).to.deep.include({ x: 0.5 * -width, y: 0.5 * -height, z: 0.5 * -depth }); + expect(cuboidMesh.bounds.max).to.deep.include({ x: 0.5 * width, y: 0.5 * height, z: 0.5 * depth }); + expect(cuboidMesh.getIndices().length).equal(36); + expect(cuboidMesh.vertexBufferBindings.length).equal(1); + }); + + it("createCylinder", () => { + // Test that createCylinder works correctly. + const radiusTop = 2; + const radiusBottom = 3; + const height = 2; + const radialSegments = 20.9999; + const heightSegments = 1.00001; + const floorRadialSegments = Math.floor(radialSegments); + const floorHeightSegments = Math.floor(heightSegments); + const cylinderMesh = PrimitiveMesh.createCylinder( + engine, + radiusTop, + radiusBottom, + height, + radialSegments, + heightSegments, + false + ); + const radius = Math.max(radiusTop, radiusBottom); + + expect(cylinderMesh.vertexCount).equal( + (floorRadialSegments + 1) * (floorHeightSegments + 1) + floorRadialSegments * 2 + 2 + ); + expect(cylinderMesh.vertexElements.length).equal(4); + expect(cylinderMesh.bounds.min).to.deep.include({ x: -radius, y: 0.5 * -height, z: -radius }); + expect(cylinderMesh.bounds.max).to.deep.include({ x: radius, y: 0.5 * height, z: radius }); + expect(cylinderMesh.getIndices().length).equal( + floorRadialSegments * floorHeightSegments * 6 + floorRadialSegments * 2 * 3 + ); + expect(cylinderMesh.vertexBufferBindings.length).equal(1); + }); + + it("createTorus", () => { + // Test that createTorus works correctly. + const radius = 1; + const tubeRadius = 0.5; + const radialSegments = 20.9999; + const tubularSegments = 40.0532; + const floorRadialSegments = Math.floor(radialSegments); + const floorTubularSegments = Math.floor(tubularSegments); + const arc = 180; + const torusMesh = PrimitiveMesh.createTorus( + engine, + radius, + tubeRadius, + radialSegments, + tubularSegments, + arc, + false + ); + const outerRadius = radius + tubeRadius; + + expect(torusMesh.vertexCount).equal((floorRadialSegments + 1) * (floorTubularSegments + 1)); + expect(torusMesh.vertexElements.length).equal(4); + expect(torusMesh.bounds.min).to.deep.include({ x: -outerRadius, y: -outerRadius, z: -tubeRadius }); + expect(torusMesh.bounds.max).to.deep.include({ x: outerRadius, y: outerRadius, z: tubeRadius }); + expect(torusMesh.getIndices().length).equal(floorRadialSegments * floorTubularSegments * 6); + expect(torusMesh.vertexBufferBindings.length).equal(1); + }); + + it("createPlane", () => { + // Test that createPlane works correctly. + const width = 2; + const height = 1.5; + const hSegments = 10.9999; + const vSegments = 9.0001; + const floorHSegments = Math.floor(hSegments); + const floorVSegments = Math.floor(vSegments); + const planeMesh = PrimitiveMesh.createPlane(engine, width, height, hSegments, vSegments, false); + + expect(planeMesh.vertexCount).equal((floorHSegments + 1) * (floorVSegments + 1)); + expect(planeMesh.vertexElements.length).equal(4); + expect(planeMesh.bounds.min).deep.include({ x: -0.5 * width, y: 0, z: -0.5 * height }); + expect(planeMesh.bounds.max).deep.include({ x: 0.5 * width, y: 0, z: 0.5 * height }); + expect(planeMesh.getIndices().length).equal(floorHSegments * floorVSegments * 6); + expect(planeMesh.vertexBufferBindings.length).equal(1); + }); + + it("createCone", () => { + // Test that createCone works correctly. + const radius = 2; + const height = 3; + const radialSegments = 20.9999; + const heightSegments = 1.00001; + const floorRadialSegments = Math.floor(radialSegments); + const floorHeightSegments = Math.floor(heightSegments); + const coneMesh = PrimitiveMesh.createCone(engine, radius, height, radialSegments, heightSegments, false); + + expect(coneMesh.vertexCount).equal((floorRadialSegments + 1) * (floorHeightSegments + 1) + floorRadialSegments + 1); + expect(coneMesh.vertexElements.length).equal(4); + expect(coneMesh.bounds.min).to.deep.include({ x: -radius, y: 0.5 * -height, z: -radius }); + expect(coneMesh.bounds.max).to.deep.include({ x: radius, y: 0.5 * height, z: radius }); + expect(coneMesh.getIndices().length).equal(floorRadialSegments * floorHeightSegments * 6 + floorRadialSegments * 3); + expect(coneMesh.vertexBufferBindings.length).equal(1); + }); + + it("createCapsule", () => { + const radius = 2; + const height = 3; + const radialSegments = 20.9999; + const heightSegments = 30.0001; + const floorRadialSegments = Math.floor(radialSegments); + const floorHeightSegments = Math.floor(heightSegments); + const capsuleMesh = PrimitiveMesh.createCapsule(engine, radius, height, radialSegments, heightSegments, false); + + expect(capsuleMesh.vertexCount).equal( + (floorRadialSegments + 1) * (floorHeightSegments + 1) + 2 * Math.pow(floorRadialSegments + 1, 2) + ); + expect(capsuleMesh.vertexElements.length).equal(4); + expect(capsuleMesh.bounds.min).to.deep.include({ x: -radius, y: 0.5 * -height - radius, z: -radius }); + expect(capsuleMesh.bounds.max).to.deep.include({ x: radius, y: 0.5 * height + radius, z: radius }); + expect(capsuleMesh.getIndices().length).equal( + (floorRadialSegments * floorHeightSegments + 2 * floorRadialSegments * floorRadialSegments) * 6 + ); + }); + + it("test limit vertex count", () => { + const radius = 1; + const segments = 300; + const floorSegments = Math.floor(segments); + const count = segments + 1; + + if (engine["_hardwareRenderer"].canIUse(GLCapabilityType.elementIndexUint)) { + const sphereMesh = PrimitiveMesh.createSphere(engine, radius, segments, false); + expect(sphereMesh.vertexCount).equal(count * count); + expect(sphereMesh.vertexElements.length).equal(4); + expect(sphereMesh.bounds.min).to.deep.include({ x: -radius, y: -radius, z: -radius }); + expect(sphereMesh.bounds.max).to.deep.include({ x: radius, y: radius, z: radius }); + expect(sphereMesh.getIndices().length).equal(floorSegments * floorSegments * 6); + expect(sphereMesh.vertexBufferBindings.length).equal(1); + } else { + expect(() => { + try { + PrimitiveMesh.createSphere(engine, radius, segments, false); + } catch (e) {} + }).throw("The vertex count is out of range."); + } + }); +});