Skip to content

Commit

Permalink
First draft for sparse accessor support
Browse files Browse the repository at this point in the history
  • Loading branch information
javagl committed Dec 18, 2024
1 parent 3055909 commit 5611795
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 31 deletions.
69 changes: 42 additions & 27 deletions packages/engine/Source/Scene/GltfIndexBufferLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,35 +240,50 @@ function createIndicesTypedArray(indexBufferLoader, bufferViewTypedArray) {
const accessorId = indexBufferLoader._accessorId;
const accessor = gltf.accessors[accessorId];
const count = accessor.count;
const indexDatatype = accessor.componentType;
const indexSize = IndexDatatype.getSizeInBytes(indexDatatype);

let arrayBuffer = bufferViewTypedArray.buffer;
let byteOffset = bufferViewTypedArray.byteOffset + accessor.byteOffset;

if (byteOffset % indexSize !== 0) {
const byteLength = count * indexSize;
const view = new Uint8Array(arrayBuffer, byteOffset, byteLength);
const copy = new Uint8Array(view);
arrayBuffer = copy.buffer;
byteOffset = 0;
deprecationWarning(
"index-buffer-unaligned",
`The index array is not aligned to a ${indexSize}-byte boundary.`,
);
}
const byteOffset = accessor.byteOffset;
const componentType = accessor.componentType;
return GltfIndexBufferLoader.createIndicesTypedArrayFromBufferViewTypedArray(
bufferViewTypedArray,
byteOffset,
componentType,
count,
);
}

let typedArray;
if (indexDatatype === IndexDatatype.UNSIGNED_BYTE) {
typedArray = new Uint8Array(arrayBuffer, byteOffset, count);
} else if (indexDatatype === IndexDatatype.UNSIGNED_SHORT) {
typedArray = new Uint16Array(arrayBuffer, byteOffset, count);
} else if (indexDatatype === IndexDatatype.UNSIGNED_INT) {
typedArray = new Uint32Array(arrayBuffer, byteOffset, count);
}
GltfIndexBufferLoader.createIndicesTypedArrayFromBufferViewTypedArray =
function (bufferViewTypedArray, byteOffset, componentType, count) {
const indexSize = IndexDatatype.getSizeInBytes(componentType);

let arrayBuffer = bufferViewTypedArray.buffer;
let arrayBufferByteOffset = bufferViewTypedArray.byteOffset + byteOffset;

if (arrayBufferByteOffset % indexSize !== 0) {
const byteLength = count * indexSize;
const view = new Uint8Array(
arrayBuffer,
arrayBufferByteOffset,
byteLength,
);
const copy = new Uint8Array(view);
arrayBuffer = copy.buffer;
arrayBufferByteOffset = 0;
deprecationWarning(
"index-buffer-unaligned",
`The index array is not aligned to a ${indexSize}-byte boundary.`,
);
}

return typedArray;
}
let typedArray;
if (componentType === IndexDatatype.UNSIGNED_BYTE) {
typedArray = new Uint8Array(arrayBuffer, arrayBufferByteOffset, count);
} else if (componentType === IndexDatatype.UNSIGNED_SHORT) {
typedArray = new Uint16Array(arrayBuffer, arrayBufferByteOffset, count);
} else if (componentType === IndexDatatype.UNSIGNED_INT) {
typedArray = new Uint32Array(arrayBuffer, arrayBufferByteOffset, count);
}

return typedArray;
};

function handleError(indexBufferLoader, error) {
indexBufferLoader.unload();
Expand Down
27 changes: 23 additions & 4 deletions packages/engine/Source/Scene/GltfLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -748,11 +748,30 @@ function getBufferViewLoader(loader, bufferViewId) {
}

function getPackedTypedArray(gltf, accessor, bufferViewTypedArray) {
let byteOffset = accessor.byteOffset;
const byteOffset = accessor.byteOffset;
const byteStride = getAccessorByteStride(gltf, accessor);
const count = accessor.count;
const componentCount = numberOfComponentsForType(accessor.type);
const componentType = accessor.componentType;
const type = accessor.type;
return GltfLoader.getPackedTypedArrayFromBufferViewTypedArray(
bufferViewTypedArray,
byteOffset,
type,
componentType,
byteStride,
count,
);
}

GltfLoader.getPackedTypedArrayFromBufferViewTypedArray = function (
bufferViewTypedArray,
byteOffset,
type,
componentType,
byteStride,
count,
) {
const componentCount = numberOfComponentsForType(type);
const componentByteLength = ComponentDatatype.getSizeInBytes(componentType);
const defaultByteStride = componentByteLength * componentCount;
const componentsLength = count * componentCount;
Expand All @@ -775,7 +794,7 @@ function getPackedTypedArray(gltf, accessor, bufferViewTypedArray) {

const dataView = new DataView(bufferViewTypedArray.buffer);
const components = new Array(componentCount);
const componentReader = getComponentReader(accessor.componentType);
const componentReader = getComponentReader(componentType);
byteOffset = bufferViewTypedArray.byteOffset + byteOffset;

for (let i = 0; i < count; ++i) {
Expand All @@ -793,7 +812,7 @@ function getPackedTypedArray(gltf, accessor, bufferViewTypedArray) {
}

return accessorTypedArray;
}
};

function loadDefaultAccessorValues(accessor, values) {
const accessorType = accessor.type;
Expand Down
115 changes: 115 additions & 0 deletions packages/engine/Source/Scene/GltfVertexBufferLoader.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import Check from "../Core/Check.js";
import ComponentDatatype from "../Core/ComponentDatatype.js";
import defaultValue from "../Core/defaultValue.js";
import defined from "../Core/defined.js";
import DeveloperError from "../Core/DeveloperError.js";
import Buffer from "../Renderer/Buffer.js";
import BufferUsage from "../Renderer/BufferUsage.js";
import AttributeType from "./AttributeType.js";
import getComponentWriter from "./getComponentWriter.js";
import GltfIndexBufferLoader from "./GltfIndexBufferLoader.js";
import GltfLoader from "./GltfLoader.js";
import numberOfComponentsForType from "./GltfPipeline/numberOfComponentsForType.js";
import JobType from "./JobType.js";
import ModelComponents from "./ModelComponents.js";
import ResourceLoader from "./ResourceLoader.js";
Expand Down Expand Up @@ -335,6 +340,15 @@ async function loadFromBufferView(vertexBufferLoader) {
}

vertexBufferLoader._typedArray = bufferViewLoader.typedArray;

if (defined(vertexBufferLoader._accessorId)) {
const accessor =
vertexBufferLoader._gltf.accessors[vertexBufferLoader._accessorId];
if (defined(accessor.sparse)) {
await handleSparseAccessor(vertexBufferLoader, accessor);
}
}

vertexBufferLoader._state = ResourceLoaderState.PROCESSING;
return vertexBufferLoader;
} catch (error) {
Expand All @@ -346,13 +360,114 @@ async function loadFromBufferView(vertexBufferLoader) {
}
}

async function handleSparseAccessor(vertexBufferLoader, accessor) {
const sparse = accessor.sparse;

console.log("There, there, sparse!", sparse);
const count = sparse.count;

const indices = sparse.indices;
const indicesBufferViewId = indices.bufferView;
const indicesBufferViewTypedArray = await loadDependencyBufferViewTypedArray(
vertexBufferLoader,
indicesBufferViewId,
);

const indicesByteOffset = indices.byteOffset;
const indicesComponentType = indices.componentType;
const indicesTypedArray =
GltfIndexBufferLoader.createIndicesTypedArrayFromBufferViewTypedArray(
indicesBufferViewTypedArray,
indicesByteOffset,
indicesComponentType,
count,
);
console.log("indices typed array ", indicesTypedArray);

const values = sparse.values;
const valuesBufferViewId = values.bufferView;
const valuesBufferViewTypedArray = await loadDependencyBufferViewTypedArray(
vertexBufferLoader,
valuesBufferViewId,
);

const valuesByteOffset = values.byteOffset;
const valuesComponentType = accessor.componentType;
const componentByteLength =
ComponentDatatype.getSizeInBytes(valuesComponentType);
const type = accessor.type;
const componentCount = numberOfComponentsForType(type);
const byteStride = componentByteLength * componentCount;
const valuesTypedArray =
GltfLoader.getPackedTypedArrayFromBufferViewTypedArray(
valuesBufferViewTypedArray,
valuesByteOffset,
type,
valuesComponentType,
byteStride,
count,
);
console.log("values typed array ", valuesTypedArray);

const targetTypedArray = vertexBufferLoader._typedArray;
const targetDataView = new DataView(
targetTypedArray.buffer,
targetTypedArray.byteOffset,
targetTypedArray.byteLength,
);
const componentWriter = getComponentWriter(valuesComponentType);

console.log("target typed array ", targetTypedArray);
const n = indicesTypedArray.length;
for (let i = 0; i < n; i++) {
const index = indicesTypedArray[i];
for (let c = 0; c < componentCount; c++) {
const value = valuesTypedArray[i * componentCount + c];
console.log(
`setting ${value} at component ${c} of index ${index} of ${targetTypedArray}`,
);
componentWriter(targetDataView, index * componentCount + c, value);
}
}
console.log("target typed array now ", targetTypedArray);
}

function handleError(vertexBufferLoader, error) {
vertexBufferLoader.unload();
vertexBufferLoader._state = ResourceLoaderState.FAILED;
const errorMessage = "Failed to load vertex buffer";
throw vertexBufferLoader.getError(errorMessage, error);
}

async function loadDependencyBufferViewTypedArray(
vertexBufferLoader,
bufferViewId,
) {
vertexBufferLoader._state = ResourceLoaderState.LOADING;
const resourceCache = vertexBufferLoader._resourceCache;
try {
const bufferViewLoader = resourceCache.getBufferViewLoader({
gltf: vertexBufferLoader._gltf,
bufferViewId: bufferViewId,
gltfResource: vertexBufferLoader._gltfResource,
baseResource: vertexBufferLoader._baseResource,
});
vertexBufferLoader._bufferViewLoader = bufferViewLoader;
await bufferViewLoader.load();

if (vertexBufferLoader.isDestroyed()) {
return;
}
return bufferViewLoader.typedArray;
} catch (error) {
if (vertexBufferLoader.isDestroyed()) {
return;
}

handleError(vertexBufferLoader, error);
}
}

function CreateVertexBufferJob() {
this.typedArray = undefined;
this.context = undefined;
Expand Down
59 changes: 59 additions & 0 deletions packages/engine/Source/Scene/getComponentWriter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import ComponentDatatype from "../Core/ComponentDatatype.js";

/**
* Returns a function to write data into a DataView
*
* @param {number} componentType Type to convert the data to.
* @returns {ComponentWriter} Function that writes.
*
* @private
*/
function getComponentWriter(componentType) {
switch (componentType) {
case ComponentDatatype.BYTE:
return function (dataView, index, input) {
dataView.setInt8(index, input);
};
case ComponentDatatype.UNSIGNED_BYTE:
return function (dataView, index, input) {
dataView.setUint8(index, input);
};
case ComponentDatatype.SHORT:
return function (dataView, index, input) {
dataView.setInt16(index * 2, input, true);
};
case ComponentDatatype.UNSIGNED_SHORT:
return function (dataView, index, input) {
dataView.setUint16(index * 2, input, true);
};
case ComponentDatatype.INT:
return function (dataView, index, input) {
dataView.setInt32(index * 4, input, true);
};
case ComponentDatatype.UNSIGNED_INT:
return function (dataView, index, input) {
dataView.setUint32(index * 4, input, true);
};
case ComponentDatatype.FLOAT:
return function (dataView, index, input) {
dataView.setFloat32(index * 4, input, true);
};
case ComponentDatatype.DOUBLE:
return function (dataView, index, input) {
dataView.setFloat64(index * 8, input, true);
};
}
}

/**
* A function to write components into a data view
* @callback ComponentWriter
*
* @param {DataView} dataView The data view to write to.
* @param {number} index The index of the component.
* @param {number} input The value to write at the given index.
*
* @private
*/

export default getComponentWriter;

0 comments on commit 5611795

Please sign in to comment.