UNPKG

@spearwolf/twopoint5d

Version:

a library to create 2.5d realtime graphics and pixelart with three.js

268 lines 11.3 kB
import { BufferAttribute, BufferGeometry, InstancedBufferGeometry, InterleavedBuffer, InterleavedBufferAttribute } from 'three'; 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 { 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.#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) { 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); 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.#autoTouchAttrNames = undefined; return pool; } dispose() { this.basePool?.clear(); this.instancedPool.clear(); this.extraInstancedPools.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