VertexAttributes

In which you acquire a couple of utility classes for managing vertex attributes.

Recall that vertex attributes are the locations, color, and other properties that are assigned to each individual vertex in an object. You must migrate the attributes from arrays in RAM to a VBO in VRAM. The following two classes, VertexAttribute and VertexAttributes, will help you organize and transfer your attributes.

export class VertexAttribute {
  constructor(name, nvertices, ncomponents, floats, usage = gl.STATIC_DRAW) {
    this.name = name;
    this.nvertices = nvertices;
    this.ncomponents = ncomponents;

    this.buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(floats), usage);
    gl.bindBuffer(gl.ARRAY_BUFFER, null);
  }

  update(floats) {
    gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
    gl.bufferSubData(gl.ARRAY_BUFFER, 0, new Float32Array(floats));
    gl.bindBuffer(gl.ARRAY_BUFFER, null);
  }

  destroy() {
    gl.deleteBuffer(this.buffer);
  }
}

export class VertexAttributes {
  constructor() {
    this.nvertices = -1;
    this.indexBuffer = null;
    this.attributes = [];
  }

  addAttribute(name, nvertices, ncomponents, floats, usage = gl.STATIC_DRAW) {
    if (this.nvertices >= 0 && nvertices != this.nvertices) {
      throw "Attributes must have same number of vertices.";
    }

    this.nvertices = nvertices;
    let attribute = new VertexAttribute(name, nvertices, ncomponents, floats, usage);
    this.attributes.push(attribute);

    return attribute;
  }

  addIndices(ints, usage = gl.STATIC_DRAW) {
    this.indexCount = ints.length;
    this.indexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(ints), usage);
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
  }

  destroy() {
    for (let attribute of this.attributes) {
      attribute.destroy();
    }

    if (this.indexBuffer) {
      gl.deleteBuffer(this.indexBuffer);
    }
  }

  [Symbol.iterator]() {
    return this.attributes.values();
  }

  get vertexCount() {
    return this.nvertices;
  }
}

Create a new file named vertex-attributes.js in your hello-orange project folder. Add this source code to the file.

To create a new VBO with two vertices, each having a position and color, you'd create an instance of VertexAttributes like this:

const positions = [
  0, 0, 0,
  0.5, 0.5, 0,
];

const colors = [
  1, 0, 0,
  0, 0, 1,
];

const attributes = new VertexAttributes();
attributes.addAttribute('position', 2, 3, positions);
attributes.addAttribute('color', 2, 3, colors);

Don't add this code yet, however. You'll put all this code together in a moment.

The 2 in the addAttribute calls means there are two vertices, and the 3 means each attribute has three components. For the position, these are the x-, y-, and z-coordinates. For the color, these are the red, green, and blue intensities.