diff --git a/src/core/InternalMeshAttributeBuffer.js b/src/core/InternalMeshAttributeBuffer.js index a98758db..8d70f017 100644 --- a/src/core/InternalMeshAttributeBuffer.js +++ b/src/core/InternalMeshAttributeBuffer.js @@ -315,9 +315,11 @@ export class InternalMeshAttributeBuffer { } /** + * @template {import("./Mesh.js").GetVertexDataOptionsTemplate} T * @param {import("./Mesh.js").AttributeType} attributeType + * @param {import("./Mesh.js").GetVertexDataOptions} [options] */ - *getVertexData(attributeType) { + *getVertexData(attributeType, { vec } = {}) { const attributeSettings = this.getAttributeSettings(attributeType); if (!attributeSettings) { throw new Error("The attribute does not contain the specified attribute type."); @@ -362,7 +364,11 @@ export class InternalMeshAttributeBuffer { while (i <= this.buffer.byteLength - this.arrayStride) { const x = getFunction(i + attributeSettings.offset + valueByteSize * 0, true); const y = getFunction(i + attributeSettings.offset + valueByteSize * 1, true); - yield new Vec2(x, y); + if (!vec) { + yield new Vec2(x, y); + } else { + yield vec.set(x, y); + } i += this.arrayStride; } } else if (attributeSettings.componentCount == 3) { @@ -371,7 +377,12 @@ export class InternalMeshAttributeBuffer { const x = getFunction(i + attributeSettings.offset + valueByteSize * 0, true); const y = getFunction(i + attributeSettings.offset + valueByteSize * 1, true); const z = getFunction(i + attributeSettings.offset + valueByteSize * 2, true); - yield new Vec3(x, y, z); + if (!vec) { + yield new Vec3(x, y, z); + } else { + const castVec = /** @type {Vec3} */ (vec); + yield castVec.set(x, y, z); + } i += this.arrayStride; } } else if (attributeSettings.componentCount == 4) { @@ -381,7 +392,12 @@ export class InternalMeshAttributeBuffer { const y = getFunction(i + attributeSettings.offset + valueByteSize * 1, true); const z = getFunction(i + attributeSettings.offset + valueByteSize * 2, true); const w = getFunction(i + attributeSettings.offset + valueByteSize * 3, true); - yield new Vec4(x, y, z, w); + if (!vec) { + yield new Vec4(x, y, z, w); + } else { + const castVec = /** @type {Vec4} */ (vec); + yield castVec.set(x, y, z, w); + } i += this.arrayStride; } } diff --git a/src/core/Mesh.js b/src/core/Mesh.js index 5b97ada2..0593fde6 100644 --- a/src/core/Mesh.js +++ b/src/core/Mesh.js @@ -9,6 +9,14 @@ import { InternalMeshAttributeBuffer } from "./InternalMeshAttributeBuffer.js"; /** @typedef {() => void} OnIndexBufferChangeCallback */ +/** @typedef {import("../mod.js").Vec2 | import("../mod.js").Vec3 | import("../mod.js").Vec4} GetVertexDataOptionsTemplate */ + +/** + * @template {GetVertexDataOptionsTemplate} T + * @typedef GetVertexDataOptions + * @property {T} [vec] + */ + export class Mesh { /** @type {InternalMeshAttributeBuffer[]} */ #buffers = []; @@ -311,14 +319,16 @@ export class Mesh { /** * Iterates over the vertex data of the specified attribute. + * @template {GetVertexDataOptionsTemplate} T * @param {AttributeType} attributeType + * @param {GetVertexDataOptions} [options] */ - getVertexData(attributeType) { + getVertexData(attributeType, options) { const buffer = this.#getInternalAttributeBuffer(attributeType); if (!buffer) { throw new Error("This mesh does not contain an attribute with the specified type. Either add a vertex state that includes this attribute or add vertex data using setVertexData()."); } - return buffer.getVertexData(attributeType); + return buffer.getVertexData(attributeType, options); } // TODO: change the signature so that you can only provide an ArrayBuffer diff --git a/test/unit/src/core/InternalMeshAttributeBuffer/InternalMeshAttributeBuffer.test.js b/test/unit/src/core/InternalMeshAttributeBuffer/InternalMeshAttributeBuffer.test.js index f80c1dc7..c2b8e7f8 100644 --- a/test/unit/src/core/InternalMeshAttributeBuffer/InternalMeshAttributeBuffer.test.js +++ b/test/unit/src/core/InternalMeshAttributeBuffer/InternalMeshAttributeBuffer.test.js @@ -1,4 +1,4 @@ -import { assertEquals, assertExists, assertThrows } from "std/testing/asserts.ts"; +import { assertEquals, assertExists, assertStrictEquals, assertThrows } from "std/testing/asserts.ts"; import { Mesh, Vec2, Vec3, Vec4 } from "../../../../../src/mod.js"; import { assertVecAlmostEquals } from "../../../../../src/util/asserts.js"; import { InternalMeshAttributeBuffer } from "../../../../../src/core/InternalMeshAttributeBuffer.js"; @@ -930,6 +930,50 @@ Deno.test({ }, }); +/** + * @param {number} componentCount + * @param {new () => Vec2 | Vec3 | Vec4} VecConstructor + * @param {(Vec2 | Vec3 | Vec4)[]} vectors + */ +function runProvidedTest(componentCount, VecConstructor, vectors) { + const buffer = new InternalMeshAttributeBuffer({ + attributeSettings: [{ offset: 0, format: Mesh.AttributeFormat.FLOAT32, componentCount, attributeType: Mesh.AttributeType.POSITION }], + }); + buffer.setVertexCount(2); + buffer.setVertexData(Mesh.AttributeType.POSITION, vectors, false); + + const vec = new VecConstructor(); + let i = 0; + for (const pos of buffer.getVertexData(Mesh.AttributeType.POSITION, { vec })) { + assertStrictEquals(pos, vec); + assertVecAlmostEquals(pos, vectors[i]); + i++; + } + + assertEquals(i, vectors.length); +} + +Deno.test({ + name: "getVertexData() yielding provided Vec2", + fn() { + runProvidedTest(2, Vec2, [new Vec2(1, 2), new Vec2(4, 5)]); + }, +}); + +Deno.test({ + name: "getVertexData() yielding provided Vec3", + fn() { + runProvidedTest(3, Vec3, [new Vec3(1, 2, 3), new Vec3(4, 5, 6)]); + }, +}); + +Deno.test({ + name: "getVertexData() yielding provided Vec4", + fn() { + runProvidedTest(4, Vec4, [new Vec4(1, 2, 3, 4), new Vec4(5, 6, 7, 8)]); + }, +}); + Deno.test({ name: "getVertexData() yielding different AttributeFormats", fn() {