From eb912d95ca0d2056e1a4bf919da63942ae5ac9e3 Mon Sep 17 00:00:00 2001 From: Olivier Guyot Date: Thu, 16 May 2019 22:04:55 +0200 Subject: [PATCH] Webgl / refactor utilities to work only on typed arrays The base webgl renderer module now has two types of utilities: * `writeXFeatureInstructions` will write a series of values in a given typed array, which represent how a given feature will be rendered; for points, this means position, size, color, etc. * `writeXFeatureToBuffers` will fill up the given index & vertex buffers with values based on the provided render instructions As such, the logic for rendering features is: user-input style > instructions array >(*) index/vertex buffers > draw (*) this transformation is intended to be done on a worker. --- src/ol/renderer/webgl/Layer.js | 185 +++++++++----- test/spec/ol/renderer/webgl/layer.test.js | 290 +++++++++++++++------- 2 files changed, 316 insertions(+), 159 deletions(-) diff --git a/src/ol/renderer/webgl/Layer.js b/src/ol/renderer/webgl/Layer.js index 6e2b87d9f9..2f34eb2f93 100644 --- a/src/ol/renderer/webgl/Layer.js +++ b/src/ol/renderer/webgl/Layer.js @@ -61,90 +61,145 @@ class WebGLLayerRenderer extends LayerRenderer { /** - * Pushes vertices and indices to the given buffers using the geometry coordinates and the following properties - * from the feature: - * - `color` - * - `opacity` - * - `size` (for points) - * - `u0`, `v0`, `u1`, `v1` (for points) - * - `rotateWithView` (for points) - * - `width` (for lines) - * Custom attributes can be designated using the `opt_attributes` argument, otherwise other properties on the - * feature will be ignored. - * @param {import("../../webgl/Buffer").default} vertexBuffer WebGL buffer in which new vertices will be pushed. - * @param {import("../../webgl/Buffer").default} indexBuffer WebGL buffer in which new indices will be pushed. - * @param {import("../../format/GeoJSON").GeoJSONFeature} geojsonFeature Feature in geojson format, coordinates - * expressed in EPSG:4326. - * @param {Array} [opt_attributes] Custom attributes. An array of properties which will be read from the - * feature and pushed in the buffer in the given order. Note: attributes can only be numerical! Any other type or - * NaN will result in `0` being pushed in the buffer. + * @param {Float32Array} instructions Instructons array in which to write. + * @param {number} elementIndex Index from which render instructions will be written. + * @param {number} x Point center X coordinate + * @param {number} y Point center Y coordinate + * @param {number} u0 Left texture coordinate + * @param {number} v0 Bottom texture coordinate + * @param {number} u1 Right texture coordinate + * @param {number} v1 Top texture coordinate + * @param {number} size Radius of the point + * @param {number} opacity Opacity + * @param {boolean} rotateWithView If true, the point will stay aligned with the view + * @param {Array} color Array holding red, green, blue, alpha values + * @return {number} Index from which the next element should be written + * @private */ -export function pushFeatureToBuffer(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes) { - if (!geojsonFeature.geometry) { - return; - } - switch (geojsonFeature.geometry.type) { - case 'Point': - pushPointFeatureToBuffer_(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes); - return; - default: - return; - } +export function writePointFeatureInstructions(instructions, elementIndex, x, y, u0, v0, u1, v1, size, opacity, rotateWithView, color) { + let i = elementIndex; + instructions[i++] = x; + instructions[i++] = y; + instructions[i++] = u0; + instructions[i++] = v0; + instructions[i++] = u1; + instructions[i++] = v1; + instructions[i++] = size; + instructions[i++] = opacity; + instructions[i++] = rotateWithView ? 1 : 0; + instructions[i++] = color[0]; + instructions[i++] = color[1]; + instructions[i++] = color[2]; + instructions[i++] = color[3]; + return i; } const tmpArray_ = []; +const bufferPositions_ = {vertexPosition: 0, indexPosition: 0}; + +export const POINT_INSTRUCTIONS_COUNT = 13; +export const POINT_VERTEX_STRIDE = 12; + +function writePointVertex(buffer, pos, x, y, offsetX, offsetY, u, v, opacity, rotateWithView, red, green, blue, alpha) { + buffer[pos + 0] = x; + buffer[pos + 1] = y; + buffer[pos + 2] = offsetX; + buffer[pos + 3] = offsetY; + buffer[pos + 4] = u; + buffer[pos + 5] = v; + buffer[pos + 6] = opacity; + buffer[pos + 7] = rotateWithView; + buffer[pos + 8] = red; + buffer[pos + 9] = green; + buffer[pos + 10] = blue; + buffer[pos + 11] = alpha; +} + +function writeCustomAttrs(buffer, pos, customAttrs) { + if (customAttrs.length) { + buffer.set(customAttrs, pos); + } +} + +/** + * An object holding positions both in an index and a vertex buffer. + * @typedef {Object} BufferPositions + * @property {number} vertexPosition Position in the vertex buffer + * @property {number} indexPosition Position in the index buffer + */ /** * Pushes a quad (two triangles) based on a point geometry - * @param {import("../../webgl/Buffer").default} vertexBuffer WebGL buffer - * @param {import("../../webgl/Buffer").default} indexBuffer WebGL buffer - * @param {import("../../format/GeoJSON").GeoJSONFeature} geojsonFeature Feature - * @param {Array} [opt_attributes] Custom attributes + * @param {Float32Array} instructions Array of render instructions for points. + * @param {number} elementIndex Index from which render instructions will be read. + * @param {Float32Array} vertexBuffer Buffer in the form of a typed array. + * @param {Uint16Array|Uint32Array} indexBuffer Buffer in the form of a typed array. + * @param {BufferPositions} [bufferPositions] Buffer write positions; if not specified, positions will be set at 0. + * @param {number} [count] Amount of render instructions that will be read. Default value is POINT_INSTRUCTIONS_COUNT + * but a higher value can be provided; all values beyond the default count will be put in the vertices buffer as + * is, thus allowing specifying custom attributes. Please note: this value should not vary inside the same buffer or + * rendering will break. + * @return {BufferPositions} New buffer positions where to write next + * @property {number} vertexPosition New position in the vertex buffer where future writes should start. + * @property {number} indexPosition New position in the index buffer where future writes should start. * @private */ -function pushPointFeatureToBuffer_(vertexBuffer, indexBuffer, geojsonFeature, opt_attributes) { - const stride = 12 + (opt_attributes !== undefined ? opt_attributes.length : 0); +export function writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer, bufferPositions, count) { + const count_ = count > POINT_INSTRUCTIONS_COUNT ? count : POINT_INSTRUCTIONS_COUNT; - const x = geojsonFeature.geometry.coordinates[0]; - const y = geojsonFeature.geometry.coordinates[1]; - const u0 = geojsonFeature.properties.u0; - const v0 = geojsonFeature.properties.v0; - const u1 = geojsonFeature.properties.u1; - const v1 = geojsonFeature.properties.v1; - const size = geojsonFeature.properties.size; - const opacity = geojsonFeature.properties.opacity; - const rotateWithView = geojsonFeature.properties.rotateWithView; - const color = geojsonFeature.properties.color; - const red = color[0]; - const green = color[1]; - const blue = color[2]; - const alpha = color[3]; - const baseIndex = vertexBuffer.getArray().length / stride; + const x = instructions[elementIndex + 0]; + const y = instructions[elementIndex + 1]; + const u0 = instructions[elementIndex + 2]; + const v0 = instructions[elementIndex + 3]; + const u1 = instructions[elementIndex + 4]; + const v1 = instructions[elementIndex + 5]; + const size = instructions[elementIndex + 6]; + const opacity = instructions[elementIndex + 7]; + const rotateWithView = instructions[elementIndex + 8]; + const red = instructions[elementIndex + 9]; + const green = instructions[elementIndex + 10]; + const blue = instructions[elementIndex + 11]; + const alpha = instructions[elementIndex + 12]; + + // the default vertex buffer stride is 12, plus additional custom values if any + const baseStride = POINT_VERTEX_STRIDE; + const stride = baseStride + count_ - POINT_INSTRUCTIONS_COUNT; // read custom numerical attributes on the feature - const customAttributeValues = tmpArray_; - customAttributeValues.length = opt_attributes ? opt_attributes.length : 0; - for (let i = 0; i < customAttributeValues.length; i++) { - customAttributeValues[i] = parseFloat(geojsonFeature.properties[opt_attributes[i]]) || 0; + const customAttrs = tmpArray_; + customAttrs.length = count_ - POINT_INSTRUCTIONS_COUNT; + for (let i = 0; i < customAttrs.length; i++) { + customAttrs[i] = instructions[elementIndex + POINT_INSTRUCTIONS_COUNT + i]; } + let vPos = bufferPositions ? bufferPositions.vertexPosition : 0; + let iPos = bufferPositions ? bufferPositions.indexPosition : 0; + const baseIndex = vPos / stride; + // push vertices for each of the four quad corners (first standard then custom attributes) - vertexBuffer.getArray().push(x, y, -size / 2, -size / 2, u0, v0, opacity, rotateWithView, red, green, blue, alpha); - Array.prototype.push.apply(vertexBuffer.getArray(), customAttributeValues); + writePointVertex(vertexBuffer, vPos, x, y, -size / 2, -size / 2, u0, v0, opacity, rotateWithView, red, green, blue, alpha); + writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs); + vPos += stride; - vertexBuffer.getArray().push(x, y, +size / 2, -size / 2, u1, v0, opacity, rotateWithView, red, green, blue, alpha); - Array.prototype.push.apply(vertexBuffer.getArray(), customAttributeValues); + writePointVertex(vertexBuffer, vPos, x, y, +size / 2, -size / 2, u1, v0, opacity, rotateWithView, red, green, blue, alpha); + writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs); + vPos += stride; - vertexBuffer.getArray().push(x, y, +size / 2, +size / 2, u1, v1, opacity, rotateWithView, red, green, blue, alpha); - Array.prototype.push.apply(vertexBuffer.getArray(), customAttributeValues); + writePointVertex(vertexBuffer, vPos, x, y, +size / 2, +size / 2, u1, v1, opacity, rotateWithView, red, green, blue, alpha); + writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs); + vPos += stride; - vertexBuffer.getArray().push(x, y, -size / 2, +size / 2, u0, v1, opacity, rotateWithView, red, green, blue, alpha); - Array.prototype.push.apply(vertexBuffer.getArray(), customAttributeValues); + writePointVertex(vertexBuffer, vPos, x, y, -size / 2, +size / 2, u0, v1, opacity, rotateWithView, red, green, blue, alpha); + writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs); + vPos += stride; - indexBuffer.getArray().push( - baseIndex, baseIndex + 1, baseIndex + 3, - baseIndex + 1, baseIndex + 2, baseIndex + 3 - ); + indexBuffer[iPos++] = baseIndex; indexBuffer[iPos++] = baseIndex + 1; indexBuffer[iPos++] = baseIndex + 3; + indexBuffer[iPos++] = baseIndex + 1; indexBuffer[iPos++] = baseIndex + 2; indexBuffer[iPos++] = baseIndex + 3; + + bufferPositions_.vertexPosition = vPos; + bufferPositions_.indexPosition = iPos; + + return bufferPositions_; } /** diff --git a/test/spec/ol/renderer/webgl/layer.test.js b/test/spec/ol/renderer/webgl/layer.test.js index 4d48e2df0c..a2007f2206 100644 --- a/test/spec/ol/renderer/webgl/layer.test.js +++ b/test/spec/ol/renderer/webgl/layer.test.js @@ -1,5 +1,7 @@ -import WebGLLayerRenderer, {getBlankTexture, pushFeatureToBuffer} from '../../../../../src/ol/renderer/webgl/Layer.js'; -import WebGLArrayBuffer from '../../../../../src/ol/webgl/Buffer.js'; +import WebGLLayerRenderer, { + getBlankTexture, POINT_INSTRUCTIONS_COUNT, POINT_VERTEX_STRIDE, + writePointFeatureInstructions, writePointFeatureToBuffers +} from '../../../../../src/ol/renderer/webgl/Layer.js'; import Layer from '../../../../../src/ol/layer/Layer.js'; @@ -28,111 +30,211 @@ describe('ol.renderer.webgl.Layer', function() { }); - describe('pushFeatureToBuffer', function() { - let vertexBuffer, indexBuffer; + describe('writePointFeatureInstructions', function() { + let instructions; beforeEach(function() { - vertexBuffer = new WebGLArrayBuffer(); - indexBuffer = new WebGLArrayBuffer(); + instructions = new Float32Array(100); }); - it('does nothing if the feature has no geometry', function() { - const feature = { - type: 'Feature', - id: 'AFG', - properties: { - color: [0.5, 1, 0.2, 0.7], - size: 3 - }, - geometry: null - }; - pushFeatureToBuffer(vertexBuffer, indexBuffer, feature); - expect(vertexBuffer.getArray().length).to.eql(0); - expect(indexBuffer.getArray().length).to.eql(0); + it('writes instructions corresponding to the given parameters', function() { + const baseIndex = 17; + writePointFeatureInstructions(instructions, baseIndex, + 1, 2, 3, 4, 5, 6, + 7, 8, true, [10, 11, 12, 13]); + expect(instructions[baseIndex + 0]).to.eql(1); + expect(instructions[baseIndex + 1]).to.eql(2); + expect(instructions[baseIndex + 2]).to.eql(3); + expect(instructions[baseIndex + 3]).to.eql(4); + expect(instructions[baseIndex + 4]).to.eql(5); + expect(instructions[baseIndex + 5]).to.eql(6); + expect(instructions[baseIndex + 6]).to.eql(7); + expect(instructions[baseIndex + 7]).to.eql(8); + expect(instructions[baseIndex + 8]).to.eql(1); + expect(instructions[baseIndex + 9]).to.eql(10); + expect(instructions[baseIndex + 10]).to.eql(11); + expect(instructions[baseIndex + 11]).to.eql(12); + expect(instructions[baseIndex + 12]).to.eql(13); }); - it('adds two triangles with the correct attributes for a point geometry', function() { - const feature = { - type: 'Feature', - id: 'AFG', - properties: { - color: [0.5, 1, 0.2, 0.7], - size: 3 - }, - geometry: { - type: 'Point', - coordinates: [-75, 47] - } - }; - const attributePerVertex = 12; - pushFeatureToBuffer(vertexBuffer, indexBuffer, feature); - expect(vertexBuffer.getArray().length).to.eql(attributePerVertex * 4); - expect(indexBuffer.getArray().length).to.eql(6); + it('correctly chains writes', function() { + let baseIndex = 0; + baseIndex = writePointFeatureInstructions(instructions, baseIndex, + 1, 2, 3, 4, 5, 6, + 7, 8, true, [10, 11, 12, 13]); + baseIndex = writePointFeatureInstructions(instructions, baseIndex, + 1, 2, 3, 4, 5, 6, + 7, 8, true, [10, 11, 12, 13]); + writePointFeatureInstructions(instructions, baseIndex, + 1, 2, 3, 4, 5, 6, + 7, 8, true, [10, 11, 12, 13]); + expect(instructions[baseIndex + 0]).to.eql(1); + expect(instructions[baseIndex + 1]).to.eql(2); + expect(instructions[baseIndex + 2]).to.eql(3); + expect(instructions[baseIndex + 3]).to.eql(4); + expect(instructions[baseIndex + 4]).to.eql(5); + expect(instructions[baseIndex + 5]).to.eql(6); + expect(instructions[baseIndex + 6]).to.eql(7); + expect(instructions[baseIndex + 7]).to.eql(8); + expect(instructions[baseIndex + 8]).to.eql(1); + expect(instructions[baseIndex + 9]).to.eql(10); + expect(instructions[baseIndex + 10]).to.eql(11); + expect(instructions[baseIndex + 11]).to.eql(12); + expect(instructions[baseIndex + 12]).to.eql(13); + }); + }); + + describe('writePointFeatureToBuffers', function() { + let vertexBuffer, indexBuffer, instructions, elementIndex; + + beforeEach(function() { + vertexBuffer = new Float32Array(100); + indexBuffer = new Uint16Array(100); + instructions = new Float32Array(100); + elementIndex = 3; + + writePointFeatureInstructions(instructions, elementIndex, + 1, 2, 3, 4, 5, 6, + 7, 8, true, [10, 11, 12, 13]); }); - it('correctly sets indices & coordinates for several features', function() { - const feature = { - type: 'Feature', - id: 'AFG', - properties: { - color: [0.5, 1, 0.2, 0.7], - size: 3 - }, - geometry: { - type: 'Point', - coordinates: [-75, 47] - } - }; - const attributePerVertex = 12; - pushFeatureToBuffer(vertexBuffer, indexBuffer, feature); - pushFeatureToBuffer(vertexBuffer, indexBuffer, feature); - expect(vertexBuffer.getArray()[0]).to.eql(-75); - expect(vertexBuffer.getArray()[1]).to.eql(47); - expect(vertexBuffer.getArray()[0 + attributePerVertex]).to.eql(-75); - expect(vertexBuffer.getArray()[1 + attributePerVertex]).to.eql(47); + it('writes correctly to the buffers (without custom attributes)', function() { + const stride = POINT_VERTEX_STRIDE; + const positions = writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer); - // first point - expect(indexBuffer.getArray()[0]).to.eql(0); - expect(indexBuffer.getArray()[1]).to.eql(1); - expect(indexBuffer.getArray()[2]).to.eql(3); - expect(indexBuffer.getArray()[3]).to.eql(1); - expect(indexBuffer.getArray()[4]).to.eql(2); - expect(indexBuffer.getArray()[5]).to.eql(3); + expect(vertexBuffer[0]).to.eql(1); + expect(vertexBuffer[1]).to.eql(2); + expect(vertexBuffer[2]).to.eql(-3.5); + expect(vertexBuffer[3]).to.eql(-3.5); + expect(vertexBuffer[4]).to.eql(3); + expect(vertexBuffer[5]).to.eql(4); + expect(vertexBuffer[6]).to.eql(8); + expect(vertexBuffer[7]).to.eql(1); + expect(vertexBuffer[8]).to.eql(10); + expect(vertexBuffer[9]).to.eql(11); + expect(vertexBuffer[10]).to.eql(12); + expect(vertexBuffer[11]).to.eql(13); - // second point - expect(indexBuffer.getArray()[6]).to.eql(4); - expect(indexBuffer.getArray()[7]).to.eql(5); - expect(indexBuffer.getArray()[8]).to.eql(7); - expect(indexBuffer.getArray()[9]).to.eql(5); - expect(indexBuffer.getArray()[10]).to.eql(6); - expect(indexBuffer.getArray()[11]).to.eql(7); + expect(vertexBuffer[stride + 0]).to.eql(1); + expect(vertexBuffer[stride + 1]).to.eql(2); + expect(vertexBuffer[stride + 2]).to.eql(+3.5); + expect(vertexBuffer[stride + 3]).to.eql(-3.5); + expect(vertexBuffer[stride + 4]).to.eql(5); + expect(vertexBuffer[stride + 5]).to.eql(4); + + expect(vertexBuffer[stride * 2 + 0]).to.eql(1); + expect(vertexBuffer[stride * 2 + 1]).to.eql(2); + expect(vertexBuffer[stride * 2 + 2]).to.eql(+3.5); + expect(vertexBuffer[stride * 2 + 3]).to.eql(+3.5); + expect(vertexBuffer[stride * 2 + 4]).to.eql(5); + expect(vertexBuffer[stride * 2 + 5]).to.eql(6); + + expect(vertexBuffer[stride * 3 + 0]).to.eql(1); + expect(vertexBuffer[stride * 3 + 1]).to.eql(2); + expect(vertexBuffer[stride * 3 + 2]).to.eql(-3.5); + expect(vertexBuffer[stride * 3 + 3]).to.eql(+3.5); + expect(vertexBuffer[stride * 3 + 4]).to.eql(3); + expect(vertexBuffer[stride * 3 + 5]).to.eql(6); + + expect(indexBuffer[0]).to.eql(0); + expect(indexBuffer[1]).to.eql(1); + expect(indexBuffer[2]).to.eql(3); + expect(indexBuffer[3]).to.eql(1); + expect(indexBuffer[4]).to.eql(2); + expect(indexBuffer[5]).to.eql(3); + + expect(positions.indexPosition).to.eql(6); + expect(positions.vertexPosition).to.eql(stride * 4); }); - it('correctly adds custom attributes', function() { - const feature = { - type: 'Feature', - id: 'AFG', - properties: { - color: [0.5, 1, 0.2, 0.7], - custom: 4, - customString: '5', - custom2: 12.4, - customString2: 'abc' - }, - geometry: { - type: 'Point', - coordinates: [-75, 47] - } - }; - const attributePerVertex = 16; - pushFeatureToBuffer(vertexBuffer, indexBuffer, feature, ['custom', 'custom2', 'customString', 'customString2']); - expect(vertexBuffer.getArray().length).to.eql(attributePerVertex * 4); - expect(indexBuffer.getArray().length).to.eql(6); - expect(vertexBuffer.getArray()[12]).to.eql(4); - expect(vertexBuffer.getArray()[13]).to.eql(12.4); - expect(vertexBuffer.getArray()[14]).to.eql(5); - expect(vertexBuffer.getArray()[15]).to.eql(0); + it('writes correctly to the buffers (with custom attributes)', function() { + instructions[elementIndex + POINT_INSTRUCTIONS_COUNT] = 101; + instructions[elementIndex + POINT_INSTRUCTIONS_COUNT + 1] = 102; + instructions[elementIndex + POINT_INSTRUCTIONS_COUNT + 2] = 103; + + const stride = POINT_VERTEX_STRIDE + 3; + const positions = writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer, + undefined, POINT_INSTRUCTIONS_COUNT + 3); + + expect(vertexBuffer[0]).to.eql(1); + expect(vertexBuffer[1]).to.eql(2); + expect(vertexBuffer[2]).to.eql(-3.5); + expect(vertexBuffer[3]).to.eql(-3.5); + expect(vertexBuffer[4]).to.eql(3); + expect(vertexBuffer[5]).to.eql(4); + expect(vertexBuffer[6]).to.eql(8); + expect(vertexBuffer[7]).to.eql(1); + expect(vertexBuffer[8]).to.eql(10); + expect(vertexBuffer[9]).to.eql(11); + expect(vertexBuffer[10]).to.eql(12); + expect(vertexBuffer[11]).to.eql(13); + + expect(vertexBuffer[12]).to.eql(101); + expect(vertexBuffer[13]).to.eql(102); + expect(vertexBuffer[14]).to.eql(103); + + expect(vertexBuffer[stride + 12]).to.eql(101); + expect(vertexBuffer[stride + 13]).to.eql(102); + expect(vertexBuffer[stride + 14]).to.eql(103); + + expect(vertexBuffer[stride * 2 + 12]).to.eql(101); + expect(vertexBuffer[stride * 2 + 13]).to.eql(102); + expect(vertexBuffer[stride * 2 + 14]).to.eql(103); + + expect(vertexBuffer[stride * 3 + 12]).to.eql(101); + expect(vertexBuffer[stride * 3 + 13]).to.eql(102); + expect(vertexBuffer[stride * 3 + 14]).to.eql(103); + + expect(indexBuffer[0]).to.eql(0); + expect(indexBuffer[1]).to.eql(1); + expect(indexBuffer[2]).to.eql(3); + expect(indexBuffer[3]).to.eql(1); + expect(indexBuffer[4]).to.eql(2); + expect(indexBuffer[5]).to.eql(3); + + expect(positions.indexPosition).to.eql(6); + expect(positions.vertexPosition).to.eql(stride * 4); }); + + it('correctly chains buffer writes', function() { + const stride = POINT_VERTEX_STRIDE; + let positions = writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer); + positions = writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer, positions); + positions = writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer, positions); + + expect(vertexBuffer[0]).to.eql(1); + expect(vertexBuffer[1]).to.eql(2); + expect(vertexBuffer[2]).to.eql(-3.5); + expect(vertexBuffer[3]).to.eql(-3.5); + + expect(vertexBuffer[stride * 4 + 0]).to.eql(1); + expect(vertexBuffer[stride * 4 + 1]).to.eql(2); + expect(vertexBuffer[stride * 4 + 2]).to.eql(-3.5); + expect(vertexBuffer[stride * 4 + 3]).to.eql(-3.5); + + expect(vertexBuffer[stride * 8 + 0]).to.eql(1); + expect(vertexBuffer[stride * 8 + 1]).to.eql(2); + expect(vertexBuffer[stride * 8 + 2]).to.eql(-3.5); + expect(vertexBuffer[stride * 8 + 3]).to.eql(-3.5); + + expect(indexBuffer[6 + 0]).to.eql(4); + expect(indexBuffer[6 + 1]).to.eql(5); + expect(indexBuffer[6 + 2]).to.eql(7); + expect(indexBuffer[6 + 3]).to.eql(5); + expect(indexBuffer[6 + 4]).to.eql(6); + expect(indexBuffer[6 + 5]).to.eql(7); + + expect(indexBuffer[6 * 2 + 0]).to.eql(8); + expect(indexBuffer[6 * 2 + 1]).to.eql(9); + expect(indexBuffer[6 * 2 + 2]).to.eql(11); + expect(indexBuffer[6 * 2 + 3]).to.eql(9); + expect(indexBuffer[6 * 2 + 4]).to.eql(10); + expect(indexBuffer[6 * 2 + 5]).to.eql(11); + + expect(positions.indexPosition).to.eql(6 * 3); + expect(positions.vertexPosition).to.eql(stride * 4 * 3); + }); + }); describe('getBlankTexture', function() {