Allow typed arrays in geometry and mesh data APIs - #9121
Conversation
Public API reportThis PR changes the public API surface (+57 / −56), per the docs' rules (@ignore / @Private / undocumented are excluded). Show API diff-BoundingBox.compute(vertices: number[] | Float32Array<ArrayBufferLike>, numVerts?: number): void
+BoundingBox.compute(vertices: ArrayLike<number>, numVerts?: number): void
-BoundingBox.static computeMinMax(vertices: number[] | Float32Array<ArrayBufferLike>, min: Vec3, max: Vec3, numVerts?: number): void
+BoundingBox.static computeMinMax(vertices: ArrayLike<number>, min: Vec3, max: Vec3, numVerts?: number): void
-BoxGeometry.blendIndices: number[] | undefined
-BoxGeometry.blendWeights: number[] | undefined
+BoxGeometry.blendIndices: ArrayLike<number> | undefined
+BoxGeometry.blendWeights: ArrayLike<number> | undefined
-BoxGeometry.colors: number[] | undefined
+BoxGeometry.colors: ArrayLike<number> | undefined
-CapsuleGeometry.blendIndices: number[] | undefined
-CapsuleGeometry.blendWeights: number[] | undefined
+CapsuleGeometry.blendIndices: ArrayLike<number> | undefined
+CapsuleGeometry.blendWeights: ArrayLike<number> | undefined
-CapsuleGeometry.colors: number[] | undefined
+CapsuleGeometry.colors: ArrayLike<number> | undefined
-CircleGeometry.blendIndices: number[] | undefined
-CircleGeometry.blendWeights: number[] | undefined
+CircleGeometry.blendIndices: ArrayLike<number> | undefined
+CircleGeometry.blendWeights: ArrayLike<number> | undefined
-CircleGeometry.colors: number[] | undefined
+CircleGeometry.colors: ArrayLike<number> | undefined
-ConeBaseGeometry.blendIndices: number[] | undefined
-ConeBaseGeometry.blendWeights: number[] | undefined
+ConeBaseGeometry.blendIndices: ArrayLike<number> | undefined
+ConeBaseGeometry.blendWeights: ArrayLike<number> | undefined
-ConeBaseGeometry.colors: number[] | undefined
-ConeBaseGeometry.tangents: number[] | undefined
-ConeGeometry.blendIndices: number[] | undefined
-ConeGeometry.blendWeights: number[] | undefined
+ConeBaseGeometry.colors: ArrayLike<number> | undefined
+ConeBaseGeometry.tangents: ArrayLike<number> | undefined
+ConeGeometry.blendIndices: ArrayLike<number> | undefined
+ConeGeometry.blendWeights: ArrayLike<number> | undefined
-ConeGeometry.colors: number[] | undefined
+ConeGeometry.colors: ArrayLike<number> | undefined
-CylinderGeometry.blendIndices: number[] | undefined
-CylinderGeometry.blendWeights: number[] | undefined
+CylinderGeometry.blendIndices: ArrayLike<number> | undefined
+CylinderGeometry.blendWeights: ArrayLike<number> | undefined
-CylinderGeometry.colors: number[] | undefined
+CylinderGeometry.colors: ArrayLike<number> | undefined
-DomeGeometry.blendIndices: number[] | undefined
-DomeGeometry.blendWeights: number[] | undefined
+DomeGeometry.blendIndices: ArrayLike<number> | undefined
+DomeGeometry.blendWeights: ArrayLike<number> | undefined
-DomeGeometry.colors: number[] | undefined
+DomeGeometry.colors: ArrayLike<number> | undefined
-Geometry.blendIndices: number[] | undefined
-Geometry.blendWeights: number[] | undefined
+Geometry.blendIndices: ArrayLike<number> | undefined
+Geometry.blendWeights: ArrayLike<number> | undefined
-Geometry.colors: number[] | undefined
-Geometry.indices: number[] | undefined
-Geometry.normals: number[] | undefined
-Geometry.positions: number[] | undefined
-Geometry.tangents: number[] | undefined
-Geometry.uvs1: number[] | undefined
-Geometry.uvs: number[] | undefined
+Geometry.colors: ArrayLike<number> | undefined
+Geometry.indices: number[] | Uint8Array<ArrayBufferLike> | Uint32Array<ArrayBufferLike> | Uint16Array<ArrayBufferLike> | undefined
+Geometry.normals: ArrayLike<number> | undefined
+Geometry.positions: ArrayLike<number> | undefined
+Geometry.tangents: ArrayLike<number> | undefined
+Geometry.uvs1: ArrayLike<number> | undefined
+Geometry.uvs: ArrayLike<number> | undefined
-Mesh.getColors(colors: number[] | ArrayBufferView<ArrayBufferLike>): number
+Mesh.getColors(colors: NumericArray): number
-Mesh.getNormals(normals: number[] | ArrayBufferView<ArrayBufferLike>): number
-Mesh.getPositions(positions: number[] | ArrayBufferView<ArrayBufferLike>): number
-Mesh.getUvs(channel: number, uvs: number[] | ArrayBufferView<ArrayBufferLike>): number
-Mesh.getVertexStream(semantic: string, data: number[] | ArrayBufferView<ArrayBufferLike>): number
+Mesh.getNormals(normals: NumericArray): number
+Mesh.getPositions(positions: NumericArray): number
+Mesh.getUvs(channel: number, uvs: NumericArray): number
+Mesh.getVertexStream(semantic: string, data: NumericArray): number
-Mesh.setColors(colors: number[] | ArrayBufferView<ArrayBufferLike>, componentCount?: number, numVertices?: number): void
-Mesh.setColors32(colors: number[] | ArrayBufferView<ArrayBufferLike>, numVertices?: number): void
+Mesh.setColors(colors: ArrayLike<number>, componentCount?: number, numVertices?: number): void
+Mesh.setColors32(colors: ArrayLike<number>, numVertices?: number): void
-Mesh.setNormals(normals: number[] | ArrayBufferView<ArrayBufferLike>, componentCount?: number, numVertices?: number): void
-Mesh.setPositions(positions: number[] | ArrayBufferView<ArrayBufferLike>, componentCount?: number, numVertices?: number): void
-Mesh.setUvs(channel: number, uvs: number[] | ArrayBufferView<ArrayBufferLike>, componentCount?: number, numVertices?: number): void
-Mesh.setVertexStream(semantic: string, data: number[] | ArrayBufferView<ArrayBufferLike>, componentCount: number, numVertices?: number, dataType?: number, dataTypeNormalize?: boolean, asInt?: boolean): void
+Mesh.setNormals(normals: ArrayLike<number>, componentCount?: number, numVertices?: number): void
+Mesh.setPositions(positions: ArrayLike<number>, componentCount?: number, numVertices?: number): void
+Mesh.setUvs(channel: number, uvs: ArrayLike<number>, componentCount?: number, numVertices?: number): void
+Mesh.setVertexStream(semantic: string, data: ArrayLike<number>, componentCount: number, numVertices?: number, dataType?: number, dataTypeNormalize?: boolean, asInt?: boolean): void
-MorphTarget.constructor(options: { aabb: BoundingBox; defaultWeight: number; deltaNormals: ArrayBuffer; deltaPositions: ArrayBuffer; name: string; preserveData: boolean }, ...args: any[])
+MorphTarget.constructor(options: { aabb: BoundingBox; defaultWeight: number; deltaNormals: ArrayLike<number>; deltaPositions: ArrayLike<number>; name: string; preserveData: boolean }, ...args: any[])
-PlaneGeometry.blendIndices: number[] | undefined
-PlaneGeometry.blendWeights: number[] | undefined
+PlaneGeometry.blendIndices: ArrayLike<number> | undefined
+PlaneGeometry.blendWeights: ArrayLike<number> | undefined
-PlaneGeometry.colors: number[] | undefined
+PlaneGeometry.colors: ArrayLike<number> | undefined
-SphereGeometry.blendIndices: number[] | undefined
-SphereGeometry.blendWeights: number[] | undefined
+SphereGeometry.blendIndices: ArrayLike<number> | undefined
+SphereGeometry.blendWeights: ArrayLike<number> | undefined
-SphereGeometry.colors: number[] | undefined
+SphereGeometry.colors: ArrayLike<number> | undefined
-TorusGeometry.blendIndices: number[] | undefined
-TorusGeometry.blendWeights: number[] | undefined
+TorusGeometry.blendIndices: ArrayLike<number> | undefined
+TorusGeometry.blendWeights: ArrayLike<number> | undefined
-TorusGeometry.colors: number[] | undefined
+TorusGeometry.colors: ArrayLike<number> | undefined
-function calculateNormals(positions: number[], indices: number[]): number[]
-function calculateTangents(positions: number[], normals: number[], uvs: number[], indices: number[]): number[]
+function calculateNormals(positions: ArrayLike<number>, indices: ArrayLike<number>): number[]
+function calculateTangents(positions: ArrayLike<number>, normals: ArrayLike<number>, uvs: ArrayLike<number>, indices: ArrayLike<number>): number[]
+type NumericArray = number[] | Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64ArrayInformational only — this never fails the build. |
Build size reportThis PR changes the size of the minified bundles.
|
There was a problem hiding this comment.
Pull request overview
This PR resolves TypeScript/JSDoc typing mismatches around geometry/mesh data APIs by widening read-only inputs to ArrayLike<number> (supporting both number[] and typed arrays), introducing a writable NumericArray type for getter destinations, and fixing Mesh#getVertexStream array-population behavior when streams haven’t been applied yet.
Changes:
- Widened JSDoc types across
Geometry→Mesh→BoundingBox(andgeometry-utils) to accept typed arrays viaArrayLike<number>. - Fixed
Mesh#getVertexStreamwhen writing into JS arrays for unapplied streams (now copies element-wise instead of pushing a nested array). - Added focused tests covering typed-array + array behavior for geometry utils and mesh stream readback, and removed stale
@ts-ignorefrom examples.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| test/scene/mesh.test.mjs | Adds regression tests for Mesh#getPositions / getIndices readback before/after update(). |
| test/scene/geometry/geometry-utils.test.mjs | Adds tests ensuring normals/tangents behave identically for arrays vs typed arrays. |
| src/scene/morph-target.js | Corrects morph target delta docs to numeric arrays (ArrayLike<number>). |
| src/scene/mesh.js | Introduces NumericArray typedef, widens setter params to ArrayLike<number>, and fixes array-destination stream copying. |
| src/scene/geometry/geometry.js | Widens stored geometry data fields and indices typing to match mesh APIs. |
| src/scene/geometry/geometry-utils.js | Widens calculateNormals / calculateTangents params to ArrayLike<number>. |
| src/core/shape/bounding-box.js | Widens bounding-box vertex inputs to ArrayLike<number>. |
| examples/src/examples/graphics/mesh-morph.example.mjs | Removes unnecessary @ts-ignore after typing fixes. |
| examples/src/examples/graphics/mesh-generation.example.mjs | Removes unnecessary @ts-ignore after typing fixes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
src/scene/mesh.js:861
- Similar to getVertexStream(), this typed-array destination branch always uses a JS loop. If the staged index data is a typed array, using TypedArray#set with a subarray of the used range is faster and consistent with IndexBuffer#readData.
// destination data is typed array, copy as much of the data as it can hold
Debug.assert(indices.length >= count, 'Destination array is too small to receive all index data.');
const numValues = Math.min(indices.length, count);
for (let i = 0; i < numValues; i++) {
indices[i] = streamIndices[i];
}
Fixes #9120.
calculateNormalsandcalculateTangentshave always worked with typed arrays - the engine's own glb parser and several examples pass them - but were documented as acceptingnumber[]only, so TypeScript users (and two of our examples, via@ts-ignore) could not call them with aFloat32Array. This widens the JSDoc across the whole chain those functions sit in, so the types are consistent fromGeometrythroughMeshtoBoundingBox.Read-only parameters use
ArrayLike<number>, which accepts bothnumber[]and any typed array. Parameters that are written into (theMeshgetters) use a newNumericArraytypedef instead, sinceArrayLikeis read-only.Changes:
calculateNormals/calculateTangents: all parameters acceptArrayLike<number>; return values are unchanged (number[])Geometry: data fields acceptArrayLike<number>;indicesacceptsnumber[]|Uint8Array|Uint16Array|Uint32Arrayto matchMesh#setIndicesMesh:setVertexStream/setPositions/setNormals/setUvs/setColors/setColors32acceptArrayLike<number>;getVertexStream/getPositions/getNormals/getUvs/getColorsacceptNumericArray.setIndices/getIndicesare unchanged.MorphTarget:options.deltaPositionsandoptions.deltaNormalswere documented asArrayBuffer, which was simply wrong - they are numeric arraysBoundingBox#compute/BoundingBox.computeMinMax:verticesacceptsArrayLike<number>Mesh#getVertexStreampopulating an array destination incorrectly when the stream had not been applied yet: it pushed the source array as a single element, somesh.getPositions([])beforemesh.update()returned[[x, y, z, ...]]instead of a flat array (and aliased the caller's own data). It now copies element-wise, matching the typed-array branch,getIndicesandVertexIterator#readData.@ts-ignore engine-tsdcomments that existed only because of the above typingsThese changes also resolve four pre-existing type errors in the repository: the
calculateNormalscall inglb-parser, two inMesh(ArrayBufferViewhas neitherlengthnorset), and one inMorphTarget.API Changes:
Mesh#getVertexStream(andgetPositions/getNormals/getUvs/getColors) returning flat data for array destinations where it previously returned a single nested element - code that worked around the old result by readingout[0]would need updating.Examples:
graphics/mesh-generationandgraphics/mesh-morph: removed now-unnecessary@ts-ignorecommentsTests:
test/scene/mesh.test.mjscovering vertex stream readback into arrays and typed arrays, before and afterupdate(), plus indicestest/scene/geometry/geometry-utils.test.mjscovering normals and tangents from both arrays and typed arrays