glTF Exploration

How to 3D

Chapter 3: Triangle Meshes

glTF Exploration

Your next milestone involves loading in 3D models. Ultimately, you need a file format that supports textures and animations. glTF is one such format. It is open standard from the Khronos Group, the same folks that manage the WebGL specification. Today we explore this file format. Follow these steps to get your first model loaded in:

Create or reuse a renderer.
Model a simple cube in Blender and export it as a glTF file.
Place the glTF file in the public folder of the renderer. By placing it here, it can be downloaded from a script at runtime.
In a script containing common utilities, define this static readFromUrl method that reads in text from a URL:
async function readFromUrl(url: string): Promise<string> {
  let text: string = await fetch(url).then(response => response.text());
  return text;
}
async function readFromUrl(url: string): Promise<string> {
  let text: string = await fetch(url).then(response => response.text());
  return text;
}
Reading URLs can be slow, and we don't want to freeze the browser during the read. So, we use the fetch function to read the URL asynchonously. The function is marked async, which means it can only be called from functions also marked async. To pause the asynchronous caller (but not the entire browser) until readFromUrl has completed, you precede the call with await:
async function someCaller() {
  let config = await readFromUrl("config.json");   // pause until reading is done
  console.log(config);
}
async function someCaller() {
  let config = await readFromUrl("config.json");   // pause until reading is done
  console.log(config);
}
This is just an example call. You'll write your own call later on.
Define a Trimesh class in its own file. Declare instance variables for an array of vertex positions. Later you'll add more instance variables for indices and other vertex data.
Give Trimesh a constructor that accepts an array of positions.
Define in Trimesh a static method named readFromGltf that takes a URL parameter as a string. Have it read the URL.
Parse the fetched JSON and print the results to the console.
Load the cube from your initialize function—which is already marked async. Inspect the parsed object. It is complex.
Skim the first few pages of the glTF Cheat Sheet. The only part of the file that we are concerned with today is the meshes and buffers.
Write code to extract the vertex positions for mesh 0. The glTF structure is complex, so this will require some poking around. Once you know where to find the position data, call this helper function to unpack it from its binary form into an array of Vector3:
static async extractVector3s(gltf: any, accessorIndex: number): Promise<Vector3[]> {
  const accessor = gltf.accessors[accessorIndex];
  const bufferView = gltf.bufferViews[accessor.bufferView];
  const buffer = gltf.buffers[bufferView.buffer];

  let arrayBuffer = await fetch(buffer.uri)
    .then(response => response.blob())
    .then(blob => new Response(blob).arrayBuffer());

  const slice = arrayBuffer.slice(bufferView.byteOffset, bufferView.byteOffset + bufferView.byteLength);
  const scalars = new Float32Array(slice);

  const vectors = [];
  for (let i = 0; i < scalars.length; i += 3) {
    vectors.push(new Vector3(scalars[i], scalars[i + 1], scalars[i + 2]));
  }

  return vectors;
}
static async extractVector3s(gltf: any, accessorIndex: number): Promise<Vector3[]> {
  const accessor = gltf.accessors[accessorIndex];
  const bufferView = gltf.bufferViews[accessor.bufferView];
  const buffer = gltf.buffers[bufferView.buffer];

  let arrayBuffer = await fetch(buffer.uri)
    .then(response => response.blob())
    .then(blob => new Response(blob).arrayBuffer());

  const slice = arrayBuffer.slice(bufferView.byteOffset, bufferView.byteOffset + bufferView.byteLength);
  const scalars = new Float32Array(slice);

  const vectors = [];
  for (let i = 0; i < scalars.length; i += 3) {
    vectors.push(new Vector3(scalars[i], scalars[i + 1], scalars[i + 2]));
  }

  return vectors;
}
Return an instance of Trimesh and render it as points.

There's nothing to turn in because this isn't graded. We'll build on this exercise in Thursday's lab.

Lab: Color by Normal →