@spearwolf/twopoint5d
Version:
Create 2.5D realtime graphics and pixelart with WebGL and three.js
280 lines • 11.8 kB
JavaScript
import { BufferGeometry, InstancedBufferGeometry } from 'three/webgpu';
import { VOBufferPool } from './VOBufferPool.js';
import { VertexObjectDescriptor } from './VertexObjectDescriptor.js';
import { VertexObjectPool } from './VertexObjectPool.js';
import { initializeAttributes } from './initializeAttributes.js';
import { initializeInstancedAttributes } from './initializeInstancedAttributes.js';
import { selectAttributes } from './selectAttributes.js';
import { selectBuffers } from './selectBuffers.js';
import { updateUpdateRange } from './updateUpdateRange.js';
export class InstancedVOBufferGeometry extends InstancedBufferGeometry {
#extraInstancedPoolAutoDispose;
constructor(...args) {
super();
this.baseBufferSerials = new Map();
this.instancedBufferSerials = new Map();
this.instancedBuffers = new Map();
this.extraInstancedPools = new Map();
this.extraInstancedBuffers = new Map();
this.extraInstancedBufferSerials = new Map();
this.#extraInstancedPoolAutoDispose = new Map();
this.#serials = new Map();
this.#updateAttributes = new Map();
this.#firstAutoTouch = true;
this.#autoTouchAttributes = () => {
if (this.instanceCount === 0)
return;
if (this.#firstAutoTouch) {
this.touchBuffers({ static: true });
this.#firstAutoTouch = false;
}
const autoTouchAttrs = this.#getAutoTouchAttributeNames();
if (autoTouchAttrs.length) {
this.touchAttributes(...autoTouchAttrs);
}
};
this.#getAutoTouchAttributeNames = () => {
if (!this.#autoTouchAttrNames) {
const attrNames = [...Array.from(this.instancedPool.descriptor.attributes.values())];
if (this.basePool) {
attrNames.push(...Array.from(this.basePool.descriptor.attributes.values()));
}
if (this.extraInstancedPools.size) {
for (const [, pool] of this.extraInstancedPools) {
attrNames.push(...Array.from(pool.descriptor.attributes.values()));
}
}
this.#autoTouchAttrNames = attrNames.filter((attr) => attr.autoTouch).map((attr) => attr.name);
}
return this.#autoTouchAttrNames;
};
this.name = 'InstancedVOBufferGeometry';
const [instancedSource, instancedCapacity] = args;
this.instancedPool =
instancedSource instanceof VOBufferPool ? instancedSource : new VOBufferPool(instancedSource, instancedCapacity);
if (args[2] instanceof BufferGeometry) {
this.copy(args[2]);
}
else {
const baseSource = args[2];
const baseCapacity = args[3] ?? 1;
this.basePool = baseSource instanceof VOBufferPool ? baseSource : new VOBufferPool(baseSource, baseCapacity);
this.baseBuffers = new Map();
initializeAttributes(this, this.basePool, this.baseBuffers, this.baseBufferSerials);
}
initializeInstancedAttributes(this, this.instancedPool, this.instancedBuffers, this.instancedBufferSerials);
}
attachInstancedPool(name, pool, options) {
if (!(pool instanceof VertexObjectPool)) {
const descriptor = pool instanceof VertexObjectDescriptor ? pool : new VertexObjectDescriptor(pool);
pool = new VertexObjectPool(descriptor, 1);
}
this.extraInstancedPools.set(name, pool);
const buffers = new Map();
this.extraInstancedBuffers.set(name, buffers);
const bufferSerials = new Map();
this.extraInstancedBufferSerials.set(name, bufferSerials);
this.#extraInstancedPoolAutoDispose.set(name, options?.autoDispose ?? true);
initializeInstancedAttributes(this, pool, buffers, bufferSerials);
this.#autoTouchAttrNames = undefined;
this.#firstAutoTouch = true;
return pool;
}
detachInstancedPool(name) {
const pool = this.extraInstancedPools.get(name);
this.extraInstancedPools.delete(name);
this.extraInstancedBuffers.delete(name);
this.extraInstancedBufferSerials.delete(name);
this.#extraInstancedPoolAutoDispose.delete(name);
this.#autoTouchAttrNames = undefined;
return pool;
}
dispose() {
this.basePool?.clear();
this.instancedPool.clear();
for (const [name, pool] of this.extraInstancedPools) {
if (this.#extraInstancedPoolAutoDispose.get(name) ?? true) {
pool.clear();
}
}
this.extraInstancedPools.clear();
this.extraInstancedBuffers.clear();
this.extraInstancedBufferSerials.clear();
this.#extraInstancedPoolAutoDispose.clear();
super.dispose();
}
touchAttributes(...attrNames) {
if (this.basePool) {
selectAttributes(this.basePool, this.baseBuffers, attrNames).forEach((buffer) => {
buffer.needsUpdate = true;
});
}
selectAttributes(this.instancedPool, this.instancedBuffers, attrNames).forEach((buffer) => {
buffer.needsUpdate = true;
});
for (const [name, pool] of this.extraInstancedPools) {
const buffers = this.extraInstancedBuffers.get(name);
if (buffers) {
selectAttributes(pool, buffers, attrNames).forEach((buffer) => {
buffer.needsUpdate = true;
});
}
}
}
touchBuffers(bufferTypes) {
if ('base' in bufferTypes || 'instanced' in bufferTypes) {
if (bufferTypes.base && this.baseBuffers) {
selectBuffers(this.baseBuffers, bufferTypes.base).forEach((buffer) => {
buffer.needsUpdate = true;
});
}
if (bufferTypes.instanced) {
selectBuffers(this.instancedBuffers, bufferTypes.instanced).forEach((buffer) => {
buffer.needsUpdate = true;
});
for (const buffers of this.extraInstancedBuffers.values()) {
selectBuffers(buffers, bufferTypes.instanced).forEach((buffer) => {
buffer.needsUpdate = true;
});
}
}
}
else {
if (this.baseBuffers) {
selectBuffers(this.baseBuffers, bufferTypes).forEach((buffer) => {
buffer.needsUpdate = true;
});
}
selectBuffers(this.instancedBuffers, bufferTypes).forEach((buffer) => {
buffer.needsUpdate = true;
});
for (const buffers of this.extraInstancedBuffers.values()) {
selectBuffers(buffers, bufferTypes).forEach((buffer) => {
buffer.needsUpdate = true;
});
}
}
}
touch(...args) {
const attrNames = [];
let buffers;
args.forEach((arg) => {
if (typeof arg === 'string') {
attrNames.push(arg);
}
else {
buffers = { ...buffers, ...arg };
}
});
if (attrNames.length) {
this.touchAttributes(...attrNames);
}
if (buffers) {
this.touchBuffers(buffers);
}
}
update() {
this.instanceCount = this.instancedPool.usedCount;
this.#updateDrawRange();
this.#checkBufferSerials();
this.#updateBuffersUpdateRange();
this.#autoTouchAttributes();
this.#syncAttributeArrays();
}
#serials;
#updateAttributes;
#syncAttributeArrays() {
this.#updateAttributes.clear();
for (const [attrName, attr] of Object.entries(this.attributes)) {
const bufAttr = attr.isInterleavedBufferAttribute
? attr.data
: attr;
const version = bufAttr.version;
if (this.#serials.has(attrName)) {
if (this.#serials.get(attrName) !== version) {
this.#updateAttributes.set(attrName, bufAttr);
this.#serials.set(attrName, version);
}
}
else {
this.#updateAttributes.set(attrName, bufAttr);
this.#serials.set(attrName, version);
}
}
for (const [attrName, bufAttr] of this.#updateAttributes) {
let poolBufInfo = this.instancedPool.buffer.bufferAttributes.get(attrName);
if (poolBufInfo) {
const poolBuf = this.instancedPool.buffer.buffers.get(poolBufInfo.bufferName);
if (poolBuf) {
bufAttr.array = poolBuf.typedArray;
}
}
else {
poolBufInfo = this.basePool.buffer.bufferAttributes.get(attrName);
if (poolBufInfo) {
const poolBuf = this.basePool.buffer.buffers.get(poolBufInfo.bufferName);
if (poolBuf) {
bufAttr.array = poolBuf.typedArray;
}
}
else {
for (const [, pool] of this.extraInstancedPools) {
const poolBufInfo = pool.buffer.bufferAttributes.get(attrName);
if (poolBufInfo) {
const poolBuf = pool.buffer.buffers.get(poolBufInfo.bufferName);
if (poolBuf) {
bufAttr.array = poolBuf.typedArray;
}
}
}
}
}
}
}
#checkBufferSerials() {
const checkBufferSerials = (pool, buffers, bufferSerials) => {
for (const [bufferName, buffer] of buffers) {
const serial = bufferSerials.get(bufferName);
const bufSerial = pool.buffer.buffers.get(bufferName).serial;
if (serial !== bufSerial) {
buffer.needsUpdate = true;
bufferSerials.set(bufferName, bufSerial);
}
}
};
if (this.basePool) {
checkBufferSerials(this.basePool, this.baseBuffers, this.baseBufferSerials);
}
if (this.instancedPool) {
checkBufferSerials(this.instancedPool, this.instancedBuffers, this.instancedBufferSerials);
}
for (const [name, pool] of this.extraInstancedPools) {
const buffers = this.extraInstancedBuffers.get(name);
const bufferSerials = this.extraInstancedBufferSerials.get(name);
checkBufferSerials(pool, buffers, bufferSerials);
}
}
#updateBuffersUpdateRange() {
updateUpdateRange(this.basePool, this.baseBuffers);
updateUpdateRange(this.instancedPool, this.instancedBuffers);
for (const [name, pool] of this.extraInstancedPools) {
const buffers = this.extraInstancedBuffers.get(name);
updateUpdateRange(pool, buffers);
}
}
#updateDrawRange() {
if (this.basePool) {
this.setDrawRange(0, this.basePool.descriptor.hasIndices
? this.basePool.usedCount * this.basePool.descriptor.indices.length
: this.basePool.usedCount * this.basePool.descriptor.vertexCount);
}
else {
this.setDrawRange(0, Infinity);
}
}
#firstAutoTouch;
#autoTouchAttributes;
#autoTouchAttrNames;
#getAutoTouchAttributeNames;
}
//# sourceMappingURL=InstancedVOBufferGeometry.js.map