@spearwolf/twopoint5d
Version:
a library to create 2.5d realtime graphics and pixelart with three.js
90 lines • 3.81 kB
JavaScript
import { DataTexture, FloatType, RGBAFormat } from 'three';
import { findNextPowerOf2 } from '../utils/findNextPowerOf2.js';
import { TextureAtlas } from './TextureAtlas.js';
import { TextureCoords } from './TextureCoords.js';
import { TileSet } from './TileSet.js';
const getBufferSize = (animationsMap, sizePerTexture = 1, maxTextureSize = 16384) => {
const anims = Array.from(animationsMap.values());
const totalFramesCount = anims.reduce((sum, anim) => sum + anim.frames.length, 0);
const minBufSize = anims.length + totalFramesCount * sizePerTexture;
const bufSize = findNextPowerOf2(minBufSize);
if (bufSize > maxTextureSize) {
throw new Error('TODO too many animation frames - we need better way here to calculate a corresponding buffer size!');
}
return bufSize;
};
const renderFloatsBuffer = (floatsBuffer, names, animations, includeTextureSize) => {
let curOffset = names.length;
floatsBuffer.set(names.flatMap((name) => {
const { frames, duration } = animations.get(name);
const offset = curOffset;
curOffset += frames.length * (includeTextureSize ? 2 : 1);
return [frames.length, duration, offset, 0];
}));
floatsBuffer.set(includeTextureSize
? names.flatMap((name) => animations.get(name).frames.flatMap(({ s, t, u, v, width, height }) => [s, t, u, v, width, height, 0, 0]))
: names.flatMap((name) => animations.get(name).frames.flatMap(({ s, t, u, v }) => [s, t, u, v])), names.length * 4);
return floatsBuffer;
};
export class FrameBasedAnimations {
static { this.MaxTextureSize = 16384; }
#animations = new Map();
#names = [];
add(...args) {
let [name] = args;
if (name) {
if (this.#animations.has(name)) {
throw new Error(`name='${name.toString()}' must be unique!`);
}
}
else {
name = Symbol('n/a');
}
let frames;
if (Array.isArray(args[2])) {
frames = args[2];
}
else if (args[2] instanceof TextureAtlas) {
const atlas = args[2];
const frameNames = atlas.frameNames(args[3]).sort();
frames = frameNames.map((frameName) => atlas.frame(frameName).coords);
}
else if (args[2] instanceof TileSet) {
const tileSet = args[2];
if (Array.isArray(args[3])) {
const tileIds = args[3];
frames = tileIds.map((tileId) => tileSet.frame(tileId).coords);
}
else {
const firstTileId = args[3] ?? tileSet.firstId;
const tileCount = args[4] ?? tileSet.tileCount;
frames = [];
for (let tileId = firstTileId; tileId < firstTileId + tileCount; tileId++) {
frames.push(tileSet.frame(tileId).coords);
}
}
}
const id = this.#names.length;
const [, duration] = args;
this.#names.push(name);
this.#animations.set(name, {
id,
name,
frames,
duration,
});
return id;
}
animId(name) {
return this.#animations.get(name).id;
}
bakeDataTexture(options) {
const includeTextureSize = Boolean(options?.includeTextureSize);
const bufSize = getBufferSize(this.#animations, includeTextureSize ? 2 : 1, FrameBasedAnimations.MaxTextureSize);
const floatsBuffer = renderFloatsBuffer(new Float32Array(bufSize * 4), this.#names, this.#animations, includeTextureSize);
const dataTexture = new DataTexture(floatsBuffer, bufSize, 1, RGBAFormat, FloatType);
dataTexture.needsUpdate = true;
return dataTexture;
}
}
//# sourceMappingURL=FrameBasedAnimations.js.map