@spearwolf/twopoint5d
Version:
a library to create 2.5d realtime graphics and pixelart with three.js
392 lines • 20.5 kB
JavaScript
import { describe, expect, test } from 'vitest';
import { InstancedVertexObjectGeometry } from './InstancedVertexObjectGeometry.js';
import { VertexObjectDescriptor } from './VertexObjectDescriptor.js';
describe('vertex-buffers-geometry-updates', () => {
describe('InstancedVertexObjectGeometry', () => {
const baseDesc = new VertexObjectDescriptor({
vertexCount: 4,
indices: [0, 1, 2, 0, 2, 3],
attributes: {
position: {
components: ['x', 'y', 'z'],
type: 'float32',
bufferName: 'positions',
},
},
});
const instancedDesc = new VertexObjectDescriptor({
meshCount: 1,
attributes: {
color: {
components: ['r', 'g', 'b', 'a'],
type: 'uint8',
},
foo: {
size: 1,
type: 'float32',
},
bar: {
size: 2,
type: 'float32',
},
impact: {
size: 1,
type: 'uint32',
usage: 'dynamic',
},
},
});
const makeInstancedGeometry = () => {
const geometry = new InstancedVertexObjectGeometry(instancedDesc, 10, baseDesc, 1);
const pool = geometry.instancedPool;
const vo0 = pool.createVO();
vo0.setColor([1, 2, 3, 4]);
vo0.foo = 100;
vo0.setBar([101, 102]);
vo0.impact = 1000;
const vo1 = pool.createVO();
vo1.setColor([5, 6, 7, 8]);
vo1.foo = 103;
vo1.setBar([104, 105]);
vo1.impact = 1001;
const vo2 = pool.createVO();
vo2.setColor([9, 10, 11, 12]);
vo2.foo = 106;
vo2.setBar([107, 108]);
vo2.impact = 1002;
const base = geometry.basePool.createVO();
base.setPosition([
0, 1, 2,
3, 4, 5,
6, 7, 8,
9, 10, 11,
]);
return [geometry, pool, vo0, vo1, vo2, base];
};
describe('create buffers and arrays', () => {
test('position', () => {
const [geometry] = makeInstancedGeometry();
const static_float32 = geometry.baseBuffers.get('positions').array;
const positionAttribute = geometry.getAttribute('position');
expect(positionAttribute.isBufferAttribute).toBe(true);
expect(positionAttribute.array).toBe(static_float32);
expect(static_float32).toBe(geometry.basePool.buffer.buffers.get('positions').typedArray);
});
test('color', () => {
const [geometry] = makeInstancedGeometry();
expect(geometry.instancedBuffers.get('static_uint8').array).toBe(geometry.instancedPool.buffer.buffers.get('static_uint8').typedArray);
const colorAttribute = geometry.getAttribute('color');
expect(colorAttribute.isInstancedBufferAttribute).toBe(true);
expect(colorAttribute.array).toBe(geometry.instancedPool.buffer.buffers.get('static_uint8').typedArray);
});
test('foo, bar', () => {
const [geometry] = makeInstancedGeometry();
expect(geometry.instancedBuffers.get('static_float32').array).toBe(geometry.instancedPool.buffer.buffers.get('static_float32').typedArray);
const fooAttribute = geometry.getAttribute('foo');
expect(fooAttribute.isInterleavedBufferAttribute).toBe(true);
expect(fooAttribute.array).toBe(geometry.instancedPool.buffer.buffers.get('static_float32').typedArray);
const barAttribute = geometry.getAttribute('bar');
expect(barAttribute.isInterleavedBufferAttribute).toBe(true);
expect(barAttribute.array).toBe(geometry.instancedPool.buffer.buffers.get('static_float32').typedArray);
});
test('impact', () => {
const [geometry] = makeInstancedGeometry();
expect(geometry.instancedBuffers.get('dynamic_uint32').array).toBe(geometry.instancedPool.buffer.buffers.get('dynamic_uint32').typedArray);
const impactAttribute = geometry.getAttribute('impact');
expect(impactAttribute.isInstancedBufferAttribute).toBe(true);
expect(impactAttribute.array).toBe(geometry.instancedPool.buffer.buffers.get('dynamic_uint32').typedArray);
});
});
describe('fromBuffersData', () => {
test('position: zero-copy', () => {
const [geometry, , , , , base] = makeInstancedGeometry();
const buffer = geometry.basePool.buffer.buffers.get('positions');
const initialPositions = buffer.typedArray;
const positionAttribute = geometry.getAttribute('position');
expect(positionAttribute.array).toBe(initialPositions);
expect(geometry.basePool.capacity).toBe(1);
expect(base.x0).toBe(0);
expect(base.y0).toBe(1);
expect(base.z0).toBe(2);
expect(base.x3).toBe(9);
expect(base.y3).toBe(10);
expect(base.z3).toBe(11);
const positions = new Float32Array([
100, 101, 102,
103, 104, 105,
106, 107, 108,
109, 110, 111,
]);
geometry.basePool.fromBuffersData({
capacity: 1,
usedCount: 1,
buffers: {
positions,
},
});
expect(base.x0).toBe(100);
expect(base.y0).toBe(101);
expect(base.z0).toBe(102);
expect(base.x3).toBe(109);
expect(base.y3).toBe(110);
expect(base.z3).toBe(111);
expect(buffer.typedArray).not.toBe(initialPositions);
expect(buffer.typedArray).toBe(positions);
expect(positionAttribute.array).toBe(initialPositions);
geometry.update();
expect(positionAttribute.array).toBe(positions);
expect(positionAttribute.array).not.toBe(initialPositions);
});
test('position: copy', () => {
const [geometry, , , , , base] = makeInstancedGeometry();
const buffer = geometry.baseBuffers.get('positions');
const initialPositions = buffer.array;
const positionAttribute = geometry.getAttribute('position');
expect(positionAttribute.array).toBe(initialPositions);
expect(geometry.basePool.capacity).toBe(1);
expect(base.x0).toBe(0);
expect(base.y0).toBe(1);
expect(base.z0).toBe(2);
expect(base.x3).toBe(9);
expect(base.y3).toBe(10);
expect(base.z3).toBe(11);
const positions = new Float32Array([
100, 101, 102,
103, 104, 105,
106, 107, 108,
109, 110, 111,
]);
geometry.basePool.fromBuffersData({
capacity: 1,
usedCount: 1,
buffers: {
positions,
},
}, true);
expect(base.x0).toBe(100);
expect(base.y0).toBe(101);
expect(base.z0).toBe(102);
expect(base.x3).toBe(109);
expect(base.y3).toBe(110);
expect(base.z3).toBe(111);
geometry.update();
expect(positionAttribute.array).not.toBe(positions);
expect(positionAttribute.array).toBe(initialPositions);
});
test('position: copy (because of smaller array)', () => {
const [geometry, , , , , base] = makeInstancedGeometry();
const buffer = geometry.baseBuffers.get('positions');
const initialPositions = buffer.array;
const positionAttribute = geometry.getAttribute('position');
expect(base.x0).toBe(0);
expect(base.y0).toBe(1);
expect(base.z0).toBe(2);
expect(base.x1).toBe(3);
expect(base.y1).toBe(4);
expect(base.z1).toBe(5);
expect(base.x2).toBe(6);
expect(base.y2).toBe(7);
expect(base.z2).toBe(8);
expect(base.x3).toBe(9);
expect(base.y3).toBe(10);
expect(base.z3).toBe(11);
const positions = new Float32Array([
100, 101, 102,
103, 104, 105,
]);
geometry.basePool.fromBuffersData({
capacity: 1,
usedCount: 1,
buffers: {
positions,
},
});
expect(base.x0).toBe(100);
expect(base.y0).toBe(101);
expect(base.z0).toBe(102);
expect(base.x1).toBe(103);
expect(base.y1).toBe(104);
expect(base.z1).toBe(105);
expect(base.x2).toBe(6);
expect(base.y2).toBe(7);
expect(base.z2).toBe(8);
expect(base.x3).toBe(9);
expect(base.y3).toBe(10);
expect(base.z3).toBe(11);
geometry.update();
expect(positionAttribute.array).not.toBe(positions);
expect(positionAttribute.array).toBe(initialPositions);
});
test('foo: zero-copy', () => {
const [geometry, , vo0, vo1, vo2] = makeInstancedGeometry();
const buffer = geometry.instancedBuffers.get('static_float32');
const initialDataArray = buffer.array;
const fooAttribute = geometry.getAttribute('foo');
expect(fooAttribute.data.array).toBe(initialDataArray);
expect(geometry.instancedPool.capacity).toBe(10);
expect(vo0.foo).toBe(100);
expect(vo1.foo).toBe(103);
expect(vo2.foo).toBe(106);
const dataArray = new Float32Array([
500, 500, 500,
503, 503, 503,
506, 506, 506,
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
]);
geometry.instancedPool.fromBuffersData({
capacity: 10,
usedCount: 3,
buffers: {
static_float32: dataArray,
},
});
expect(vo0.foo).toBe(500);
expect(vo1.foo).toBe(503);
expect(vo2.foo).toBe(506);
geometry.update();
expect(fooAttribute.array).toBe(dataArray);
expect(fooAttribute.array).not.toBe(initialDataArray);
});
});
test('first (initial) update', () => {
const [geometry, pool] = makeInstancedGeometry();
expect(pool.usedCount).toBe(3);
expect(pool.buffer.toAttributeArrays(['color', 'foo', 'bar', 'impact'], 0, pool.usedCount)).toEqual({
color: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),
foo: new Float32Array([100, 103, 106]),
bar: new Float32Array([101, 102, 104, 105, 107, 108]),
impact: new Uint32Array([1000, 1001, 1002]),
});
expect(geometry.getAttribute('position').version, 'position').toBe(0);
expect(geometry.getAttribute('color').version, 'color').toBe(0);
expect(geometry.getAttribute('impact').version, 'impact').toBe(0);
expect(geometry.getAttribute('foo').data.version, 'foo').toBe(0);
expect(geometry.getAttribute('bar').data.version, 'bar').toBe(0);
expect(geometry.drawRange).toEqual({ start: 0, count: Infinity });
geometry.update();
expect(geometry.getAttribute('position').version, 'position').toBeGreaterThan(0);
expect(geometry.getAttribute('color').version, 'color').toBeGreaterThan(0);
expect(geometry.getAttribute('impact').version, 'impact').toBeGreaterThan(0);
expect(geometry.getAttribute('foo').data.version, 'foo').toBeGreaterThan(0);
expect(geometry.getAttribute('bar').data.version, 'bar').toBeGreaterThan(0);
expect(geometry.instanceCount).toEqual(3);
});
test('dynamic auto update', () => {
const [geometry] = makeInstancedGeometry();
geometry.update();
const position_serial = geometry.getAttribute('position').version;
const color_serial = geometry.getAttribute('color').version;
const impact_serial = geometry.getAttribute('impact').version;
const foo_serial = geometry.getAttribute('foo').data.version;
const bar_serial = geometry.getAttribute('bar').data.version;
geometry.update();
expect(geometry.getAttribute('position').version, 'position').toBe(position_serial);
expect(geometry.getAttribute('color').version, 'color').toBe(color_serial);
expect(geometry.getAttribute('impact').version, 'impact').toBeGreaterThan(impact_serial);
expect(geometry.getAttribute('foo').data.version, 'foo').toBe(foo_serial);
expect(geometry.getAttribute('bar').data.version, 'bar').toBe(bar_serial);
});
test('touch color', () => {
const [geometry] = makeInstancedGeometry();
geometry.update();
const position_serial = geometry.getAttribute('position').version;
const color_serial = geometry.getAttribute('color').version;
const impact_serial = geometry.getAttribute('impact').version;
const foo_serial = geometry.getAttribute('foo').data.version;
const bar_serial = geometry.getAttribute('bar').data.version;
geometry.touch('color');
geometry.update();
expect(geometry.getAttribute('position').version, 'position').toBe(position_serial);
expect(geometry.getAttribute('color').version, 'color').toBeGreaterThan(color_serial);
expect(geometry.getAttribute('impact').version, 'impact').toBeGreaterThan(impact_serial);
expect(geometry.getAttribute('foo').data.version, 'foo').toBe(foo_serial);
expect(geometry.getAttribute('bar').data.version, 'bar').toBe(bar_serial);
});
test('touch foo:interleaved', () => {
const [geometry] = makeInstancedGeometry();
geometry.update();
const position_serial = geometry.getAttribute('position').version;
const color_serial = geometry.getAttribute('color').version;
const impact_serial = geometry.getAttribute('impact').version;
const foo_serial = geometry.getAttribute('foo').data.version;
const bar_serial = geometry.getAttribute('bar').data.version;
geometry.touch('foo');
geometry.update();
expect(geometry.getAttribute('position').version, 'position').toBe(position_serial);
expect(geometry.getAttribute('color').version, 'color').toBe(color_serial);
expect(geometry.getAttribute('impact').version, 'impact').toBeGreaterThan(impact_serial);
expect(geometry.getAttribute('foo').data.version, 'foo').toBeGreaterThan(foo_serial);
expect(geometry.getAttribute('bar').data.version, 'bar').toBeGreaterThan(bar_serial);
});
test('createVO', () => {
const [geometry, pool] = makeInstancedGeometry();
geometry.update();
const position_serial = geometry.getAttribute('position').version;
const color_serial = geometry.getAttribute('color').version;
const impact_serial = geometry.getAttribute('impact').version;
const foo_serial = geometry.getAttribute('foo').data.version;
const bar_serial = geometry.getAttribute('bar').data.version;
pool.createVO();
geometry.update();
expect(geometry.getAttribute('position').version, 'position').toBe(position_serial);
expect(geometry.getAttribute('color').version, 'color').toBeGreaterThan(color_serial);
expect(geometry.getAttribute('impact').version, 'impact').toBeGreaterThan(impact_serial);
expect(geometry.getAttribute('foo').data.version, 'foo').toBeGreaterThan(foo_serial);
expect(geometry.getAttribute('bar').data.version, 'bar').toBeGreaterThan(bar_serial);
expect(geometry.instanceCount).toEqual(4);
});
test('freeVO:last', () => {
const [geometry, pool, , , vo2] = makeInstancedGeometry();
geometry.update();
const position_serial = geometry.getAttribute('position').version;
const color_serial = geometry.getAttribute('color').version;
const impact_serial = geometry.getAttribute('impact').version;
const foo_serial = geometry.getAttribute('foo').data.version;
const bar_serial = geometry.getAttribute('bar').data.version;
pool.freeVO(vo2);
geometry.update();
expect(geometry.getAttribute('position').version, 'position').toBe(position_serial);
expect(geometry.getAttribute('color').version, 'color').toBe(color_serial);
expect(geometry.getAttribute('impact').version, 'impact').toBeGreaterThan(impact_serial);
expect(geometry.getAttribute('foo').data.version, 'foo').toBe(foo_serial);
expect(geometry.getAttribute('bar').data.version, 'bar').toBe(bar_serial);
expect(pool.buffer.toAttributeArrays(['color', 'foo', 'bar', 'impact'], 0, pool.usedCount)).toEqual({
color: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]),
foo: new Float32Array([100, 103]),
bar: new Float32Array([101, 102, 104, 105]),
impact: new Uint32Array([1000, 1001]),
});
expect(geometry.instanceCount).toEqual(2);
});
test('freeVO:not(last)', () => {
const [geometry, pool, , vo1] = makeInstancedGeometry();
geometry.update();
const position_serial = geometry.getAttribute('position').version;
const color_serial = geometry.getAttribute('color').version;
const impact_serial = geometry.getAttribute('impact').version;
const foo_serial = geometry.getAttribute('foo').data.version;
const bar_serial = geometry.getAttribute('bar').data.version;
pool.freeVO(vo1);
geometry.update();
expect(geometry.getAttribute('position').version, 'position').toBe(position_serial);
expect(geometry.getAttribute('color').version, 'color').toBeGreaterThan(color_serial);
expect(geometry.getAttribute('impact').version, 'impact').toBeGreaterThan(impact_serial);
expect(geometry.getAttribute('foo').data.version, 'foo').toBeGreaterThan(foo_serial);
expect(geometry.getAttribute('bar').data.version, 'bar').toBeGreaterThan(bar_serial);
expect(pool.buffer.toAttributeArrays(['color', 'foo', 'bar', 'impact'], 0, pool.usedCount)).toEqual({
color: new Uint8Array([1, 2, 3, 4, 9, 10, 11, 12]),
foo: new Float32Array([100, 106]),
bar: new Float32Array([101, 102, 107, 108]),
impact: new Uint32Array([1000, 1002]),
});
expect(geometry.instanceCount).toEqual(2);
});
});
});
//# sourceMappingURL=vertex-buffers-geometry-updates.spec.js.map