UNPKG

@tolokoban/tgd

Version:

ToloGameDev library for WebGL2

196 lines 14.7 kB
import { TgdDataset } from "./../../dataset/index.js"; import { TgdPainter } from "./../painter.js"; import { TgdVertexArray } from "./../../vao/index.js"; import FRAG from "./segments.frag"; import VERT from "./segments.vert"; import { TgdTexture2D } from "./../../texture/index.js"; import { TgdProgram } from "./../../program/index.js"; import { tgdCanvasCreatePalette } from "./../../utils/index.js"; /** * @example * ``` * const factory = new TgdPainterSegmentsData() * factory.add( * [0, 0, 0, .2], * [1, 0, 0, .1], * ) * factory.add( * [0, 0, 0, .2], * [0, 1, 0, .1], * ) * factory.add( * [0, 0, 0, .2], * [0, 0, 1, .1], * ) * const segments = new TgdPainterSegments( * segment, factory * ) * ``` */ export class TgdPainterSegments extends TgdPainter { constructor(context, factory, { roundness = 3, minRadius = 0, } = {}) { super(); this.context = context; this.minRadius = 0; this.radiusMultiplier = 1; this.radiusConstant = 1; this.radiusSwitch = 0; this.light = 1; this.shiftZ = 0; this.minRadius = minRadius; if (roundness > 125) { throw new Error("[TgdPainterSegments] Max roundness is 125!"); } if (roundness < 0) { throw new Error("[TgdPainterSegments] Min roundness is 0!"); } this.colorTexture = new TgdTexture2D(context) .setParams({ magFilter: "NEAREST", minFilter: "NEAREST", wrapR: "CLAMP_TO_EDGE", wrapS: "CLAMP_TO_EDGE", wrapT: "CLAMP_TO_EDGE", }) .loadBitmap(tgdCanvasCreatePalette(["#f00", "#0f0", "#00f"])); const prg = new TgdProgram(context.gl, { vert: VERT, frag: FRAG, }); this.prg = prg; const { capsule, elements } = makeCapsule(roundness); const instance = factory.makeDataset(); this.vao = new TgdVertexArray(context.gl, prg, [capsule, instance], elements); this.vertexCount = elements.length; this.instanceCount = instance.count; } delete() { this.vao.delete(); } paint(_time, _delay) { const { context, prg, vao, colorTexture, vertexCount, instanceCount, light, radiusMultiplier, radiusConstant, radiusSwitch, shiftZ, } = this; const { gl, camera } = context; prg.use(); const minRadius = (this.minRadius * camera.spaceHeightAtTarget) / (camera.zoom * camera.screenHeight); prg.uniform1f("uniMinRadius", minRadius); prg.uniform1f("uniLight", light); prg.uniform1f("uniShiftZ", shiftZ); prg.uniform1f("uniRadiusMultiplier", radiusMultiplier); prg.uniform1f("uniRadiusConstant", radiusConstant); prg.uniform1f("uniRadiusSwitch", radiusSwitch); colorTexture.activate(0, prg, "uniTexture"); prg.uniformMatrix4fv("uniModelViewMatrix", camera.matrixModelView); prg.uniformMatrix4fv("uniProjectionMatrix", camera.matrixProjection); vao.bind(); gl.drawElementsInstanced(gl.TRIANGLES, vertexCount, gl.UNSIGNED_BYTE, 0, instanceCount); } } export class TgdPainterSegmentsData { constructor() { this._count = 0; this.attAxyzr = []; this.attAuv = []; this.attAinfluence = []; this.attBxyzr = []; this.attBuv = []; this.attBinfluence = []; } get count() { return this._count; } /** * @param Axyzr (x,y,z) and radius of point A. * @param Bxyzr (x,y,z) and radius of point B. * @param Auv * @param Buv * @param radiusMultiplierInfluenceA * @param radiusMultiplierInfluenceB */ add(Axyzr, Bxyzr, Auv = [0, 0], Buv = [0, 0], radiusMultiplierInfluenceA = 1, radiusMultiplierInfluenceB = 1) { this.attAxyzr.push(...Axyzr); this.attAuv.push(...Auv); this.attAinfluence.push(radiusMultiplierInfluenceA); this.attBxyzr.push(...Bxyzr); this.attBuv.push(...Buv); this.attBinfluence.push(radiusMultiplierInfluenceB); this._count++; } makeDataset() { const dataset = new TgdDataset({ attAxyzr: "vec4", attAuv: "vec2", attAinfluence: "float", attBxyzr: "vec4", attBuv: "vec2", attBinfluence: "float", }, { divisor: 1, }); dataset.set("attAxyzr", new Float32Array(this.attAxyzr)); dataset.set("attAuv", new Float32Array(this.attAuv)); dataset.set("attAinfluence", new Float32Array(this.attAinfluence)); dataset.set("attBxyzr", new Float32Array(this.attBxyzr)); dataset.set("attBuv", new Float32Array(this.attBuv)); dataset.set("attBinfluence", new Float32Array(this.attBinfluence)); return dataset; } } /** * The capsule is a 2D shape (x,y) that will be uses * as a pattern for the segment. * The segment will expand this template along Y axis. * The tip pointing toward +Y is called A. * The tip pointing toward -Y is called B. * The z coodinates indicates to which tip the point * is attached: 0 for A and 1 for B. */ function makeCapsule(roundness) { // prettier-ignore const offset = [ 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 1, 1, 0, 1, -1, 0, 1, ]; // prettier-ignore const elements = [ 0, 3, 1, 3, 4, 1, 0, 2, 5, 3, 0, 5, ]; if (roundness > 0) { let oldIndexA = 1; let oldIndexB = 4; let elementIndex = 6; for (let roundnessStep = 0; roundnessStep < roundness; roundnessStep++) { const ang = (Math.PI * (roundnessStep + 1)) / (roundness + 1); const x = Math.cos(ang); const y = Math.sin(ang); // We set z to 0 because it's related to tip A. offset.push(x, y, 0); elements.push(0, oldIndexA, elementIndex); oldIndexA = elementIndex; elementIndex++; // We set z to 1 because it's related to tip B. offset.push(x, -y, 1); elements.push(3, elementIndex, oldIndexB); oldIndexB = elementIndex; elementIndex++; } elements.push(0, oldIndexA, 2); elements.push(3, 5, oldIndexB); } const capsule = new TgdDataset({ attOffset: "vec3", }); capsule.set("attOffset", new Float32Array(offset)); return { capsule, elements: new Uint8Array(elements), }; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VnbWVudHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvcGFpbnRlci9zZWdtZW50cy9zZWdtZW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sY0FBYyxDQUFBO0FBQ3pDLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQTtBQUVqRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sVUFBVSxDQUFBO0FBRXpDLE9BQU8sSUFBSSxNQUFNLGlCQUFpQixDQUFBO0FBQ2xDLE9BQU8sSUFBSSxNQUFNLGlCQUFpQixDQUFBO0FBQ2xDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxjQUFjLENBQUE7QUFDM0MsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGNBQWMsQ0FBQTtBQUN6QyxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxZQUFZLENBQUE7QUFzQm5EOzs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW9CRztBQUNILE1BQU0sT0FBTyxrQkFBbUIsU0FBUSxVQUFVO0lBYzlDLFlBQ3VCLE9BR2xCLEVBQ0QsT0FBdUUsRUFDdkUsRUFDSSxTQUFTLEdBQUcsQ0FBQyxFQUNiLFNBQVMsR0FBRyxDQUFDLE1BQ3VCLEVBQUU7UUFFMUMsS0FBSyxFQUFFLENBQUE7UUFWWSxZQUFPLEdBQVAsT0FBTyxDQUd6QjtRQWhCRSxjQUFTLEdBQVcsQ0FBQyxDQUFBO1FBQ3JCLHFCQUFnQixHQUFHLENBQUMsQ0FBQTtRQUNwQixtQkFBYyxHQUFHLENBQUMsQ0FBQTtRQUNsQixpQkFBWSxHQUFHLENBQUMsQ0FBQTtRQUNoQixVQUFLLEdBQUcsQ0FBQyxDQUFBO1FBQ1QsV0FBTSxHQUFHLENBQUMsQ0FBQTtRQW1CYixJQUFJLENBQUMsU0FBUyxHQUFHLFNBQVMsQ0FBQTtRQUMxQixJQUFJLFNBQVMsR0FBRyxHQUFHLEVBQUUsQ0FBQztZQUNsQixNQUFNLElBQUksS0FBSyxDQUFDLDRD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