|
|
|
|
 |
glTF File Format Tutorials
Unlocking the power ... |
|
|
|
The glTF File Format.
The complete code is given below for the loader/dumper. You can try an online version here (LINK).
The 100 lines of code below is a glTF parser written in vanilla JavaScript for loading, parsing and dumping the data for the 3d model in the file.
class glTFLoader {
async load(url, gltfPath) {
try {
const response = await fetch(url);
const gltf = await response.json();
gltf.gltfPath = gltfPath;
const parsedData = await this.parseGLTF(gltf);
return parsedData;
} catch (error) {
console.error('Error loading GLTF file:', error);
throw error;
}
}
async parseGLTF(gltf) {
const parsedData = {
buffers: await this.loadBuffers(gltf),
bufferViews: gltf.bufferViews,
accessors: gltf.accessors,
meshes: gltf.meshes,
nodes: gltf.nodes,
materials: gltf.materials,
textures: gltf.textures,
animations: gltf.animations,
};
this.extractMeshData(parsedData);
this.extractAnimations(parsedData);
this.extractTextures(parsedData);
return parsedData;
}
async loadBuffers(gltf) {
const bufferPromises = gltf.buffers.map(buffer => fetch( gltf.gltfPath + buffer.uri ).then(res => res.arrayBuffer()));
return await Promise.all(bufferPromises);
}
extractMeshData(parsedData) {
parsedData.meshes.forEach(mesh => {
mesh.primitives.forEach(primitive => {
const positionAccessorIndex = primitive.attributes.POSITION;
const positionAccessor = parsedData.accessors[positionAccessorIndex];
const positionBufferView = parsedData.bufferViews[positionAccessor.bufferView];
const positionBuffer = parsedData.buffers[positionBufferView.buffer];
const positions = new Float32Array(
positionBuffer,
positionBufferView.byteOffset + (positionAccessor.byteOffset || 0),
positionAccessor.count * (positionAccessor.type === 'VEC3' ? 3 : 1)
);
primitive.positions = positions;
if (primitive.indices !== undefined) {
const indexAccessor = parsedData.accessors[primitive.indices];
const indexBufferView = parsedData.bufferViews[indexAccessor.bufferView];
const indexBuffer = parsedData.buffers[indexBufferView.buffer];
const indices = new Uint16Array(
indexBuffer,
indexBufferView.byteOffset + (indexAccessor.byteOffset || 0),
indexAccessor.count
);
primitive.indices = indices;
}
if (primitive.attributes.NORMAL !== undefined) {
const normalAccessorIndex = primitive.attributes.NORMAL;
const normalAccessor = parsedData.accessors[normalAccessorIndex];
const normalBufferView = parsedData.bufferViews[normalAccessor.bufferView];
const normalBuffer = parsedData.buffers[normalBufferView.buffer];
const normals = new Float32Array(
normalBuffer,
normalBufferView.byteOffset + (normalAccessor.byteOffset || 0),
normalAccessor.count * (normalAccessor.type === 'VEC3' ? 3 : 1)
);
primitive.normals = normals;
}
if (primitive.attributes.TEXCOORD_0 !== undefined) {
const texCoordAccessorIndex = primitive.attributes.TEXCOORD_0;
const texCoordAccessor = parsedData.accessors[texCoordAccessorIndex];
const texCoordBufferView = parsedData.bufferViews[texCoordAccessor.bufferView];
const texCoordBuffer = parsedData.buffers[texCoordBufferView.buffer];
const texCoords = new Float32Array(
texCoordBuffer,
texCoordBufferView.byteOffset + (texCoordAccessor.byteOffset || 0),
texCoordAccessor.count * (texCoordAccessor.type === 'VEC2' ? 2 : 1)
);
primitive.texCoords = texCoords;
}
});
});
}
extractAnimations(parsedData) {
if (parsedData.animations) {
parsedData.animations.forEach(animation => {
animation.samplers.forEach(sampler => {
const inputAccessor = parsedData.accessors[sampler.input];
const inputBufferView = parsedData.bufferViews[inputAccessor.bufferView];
const inputBuffer = parsedData.buffers[inputBufferView.buffer];
sampler.inputData = new Float32Array(
inputBuffer,
inputBufferView.byteOffset + (inputAccessor.byteOffset || 0),
inputAccessor.count
);
const outputAccessor = parsedData.accessors[sampler.output];
const outputBufferView = parsedData.bufferViews[outputAccessor.bufferView];
const outputBuffer = parsedData.buffers[outputBufferView.buffer];
sampler.outputData = new Float32Array(
outputBuffer,
outputBufferView.byteOffset + (outputAccessor.byteOffset || 0),
outputAccessor.count * (outputAccessor.type === 'VEC3' ? 3 : 1)
);
});
});
}
}
extractTextures(parsedData) {
if (parsedData.textures) {
parsedData.textures.forEach(texture => {
if (texture.source !== undefined) {
const image = parsedData.images[texture.source];
texture.imageUri = image.uri;
}
});
}
}
}
// Function to display statistics
function displayStats(parsedData) {
const stats = {
numberOfMeshes: parsedData.meshes ? parsedData.meshes.length : 0,
numberOfNodes: parsedData.nodes ? parsedData.nodes.length : 0,
numberOfMaterials: parsedData.materials ? parsedData.materials.length : 0,
numberOfTextures: parsedData.textures ? parsedData.textures.length : 0,
numberOfAnimations: parsedData.animations ? parsedData.animations.length : 0,
};
let details = `
Number of Meshes: ${stats.numberOfMeshes}<br>
Number of Nodes: ${stats.numberOfNodes}<br>
Number of Materials: ${stats.numberOfMaterials}<br>
Number of Textures: ${stats.numberOfTextures}<br>
Number of Animations: ${stats.numberOfAnimations}<br>
<h2>Mesh Details:</h2>
`;
parsedData.meshes.forEach((mesh, meshIndex) => {
details += `Mesh ${meshIndex} - Name: ${mesh.name || 'Unnamed'}<br>`;
mesh.primitives.forEach((primitive, primitiveIndex) => {
const verticesCount = primitive.positions ? primitive.positions.length / 3 : 0;
const indicesCount = primitive.indices ? primitive.indices.length : 0;
details += `
${' '.repeat(8)}Primitive ${primitiveIndex}:<br>
${' '.repeat(16)}Number of Vertices: ${verticesCount}<br>
${' '.repeat(16)}Number of Indices: ${indicesCount}<br>
`;
});
});
const statsDiv = document.createElement('div');
document.body.appendChild( statsDiv );
statsDiv.innerHTML = details;
}
// Function to load GLTF file and display statistics
async function loadGLTFAndShowStats(url, gltfPath) {
const loader = new glTFLoader();
try {
const parsedData = await loader.load(url, gltfPath);
displayStats(parsedData);
} catch (error) {
console.error('An error occurred:', error);
}
}
const gltfFileUrl = 'https://webgpulab.xbdev.net/var/resources/gltf/lowpolyworld.gltf';
const gltfPath = 'https://webgpulab.xbdev.net/var/resources/gltf/';
let div = document.createElement('div');
document.body.appendChild( div );
div.innerHTML = `
Vanilla glTF loader/parser in native JavaScript<br><br>
File: ${gltfFileUrl}<br><br>
`;
loadGLTFAndShowStats(gltfFileUrl, gltfPath);
console.log('done..');
This is what the output for the loader/dump script above looks like:
 | Resources & Links |  |
• glTF Loader/Dumper LINK
• glTF Tutorials/Code/Examples LINK
|
|
|
|
|
|
|
|