UNPKG

@tolokoban/tgd

Version:

ToloGameDev library for WebGL2

229 lines 17.5 kB
import { TgdMaterialFaceOrientation } from "./../../material/index.js"; import { TgdTransfo } from "./../../math/index.js"; import { TgdPainter } from "./../painter.js"; import { TgdProgram } from "./../../program/index.js"; import { TgdShaderFragment, TgdShaderVertex } from "./../../shader/index.js"; import { TgdTexture2D } from "./../../texture/index.js"; import { tgdCanvasCreatePalette } from "./../../utils/index.js"; import { TgdVertexArray } from "./../../vao/index.js"; import { makeCapsule } from "./capsule.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 TgdPainterSegmentsMorphing extends TgdPainter { // private readonly painter: TgdPainter constructor(context, options) { super(); this.context = context; this.transfo = new TgdTransfo(); this.minRadius = 1; this.radiusMultiplier = 1; this.radiusConstant = 1; this.radiusSwitch = 0; this.instanceCount = 0; /** * Mix between two datasets of a pair. * 0.0 for the first one, 1.0 for the second one. */ this.mix = 0; this.name = `TgdPainterSegments#${this.id}`; this.radiusMultiplier = options.radiusMultiplier ?? 1; const { roundness = 3, minRadius = 1, datasetsPairs } = options; if (datasetsPairs.length === 0) { throw new Error('[TgdPainterSegmentsMorphing] "datasetsPairs" must contain at least one pair of datasets!'); } const geometry = makeCapsule(roundness); const material = options.material ?? new TgdMaterialFaceOrientation(); this.material = material; material.attPosition = geometry.attPosition; material.attNormal = "normal"; material.attUV = "((mix(attUV0_A, attUV0_B, uniMix) + mix(attUV1_A, attUV1_B, uniMix)) * .5)"; this.minRadius = minRadius; if (roundness > 127) { throw new Error("[TgdPainterSegments] Max roundness is 127!"); } 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(["#f44", "#ff4", "#4f4", "#4ff", "#44f"])); const vert = new TgdShaderVertex({ uniforms: { uniMix: "float", uniTransfoMatrix: "mat4", uniModelViewMatrix: "mat4", uniProjectionMatrix: "mat4", uniPixelPerScreenUnit: "float", uniMinRadiusInPixel: "float", uniRadiusMultiplier: "float", ...material.uniforms, }, attributes: { [geometry.attPosition]: "vec4", [geometry.attNormal]: "vec3", attTip: "float", attXYZR0_A: "vec4", attXYZR0_B: "vec4", attXYZR1_A: "vec4", attXYZR1_B: "vec4", attUV0_A: "vec2", attUV0_B: "vec2", attUV1_A: "vec2", attUV1_B: "vec2", }, varying: { ...material.varyings, }, functions: { ...material.extraVertexShaderFunctions, getPosition: [ "vec4 getPosition(vec4 pos) {", [material.vertexShaderCodeForGetPosition ?? "return pos;"], "}", ], applyMaterial: [ "void applyMaterial(vec3 position, vec3 normal, vec2 uv) {", [material.vertexShaderCode], "}", ], }, mainCode: [ "vec4 attXYZR0 = mix(attXYZR0_A, attXYZR0_B, uniMix);", "vec4 attXYZR1 = mix(attXYZR1_A, attXYZR1_B, uniMix);", "vec2 attUV0 = mix(attUV0_A, attUV0_B, uniMix);", "vec2 attUV1 = mix(attUV1_A, attUV1_B, uniMix);", `vec3 normal = ${geometry.attNormal};`, `vec3 pos = getPosition(${geometry.attPosition}).xyz;`, "vec4 xyzr = mix(attXYZR0, attXYZR1, attTip);", "vec3 center = xyzr.xyz;", "vec4 centerInCameraSpace = uniModelViewMatrix * uniTransfoMatrix * vec4(center, 1);", "vec4 centerInScreenSpace = uniProjectionMatrix * centerInCameraSpace;", "float screenUnitPerCameraUnit = (uniProjectionMatrix * vec4(0, 1, centerInCameraSpace.z, 1)).y / centerInScreenSpace.w;", "float minRadiusInCameraUnit = uniMinRadiusInPixel / (uniPixelPerScreenUnit * screenUnitPerCameraUnit);", "float radius = max(", ["xyzr.w * uniRadiusMultiplier,", "minRadiusInCameraUnit"], ");", "vec3 dir = attXYZR1.xyz - attXYZR0.xyz;", "float len = length(dir);", "if (len == 0.0) {", ["// Just a sphere", "pos *= radius;", "pos += center.xyz;"], "} else {", [ "// Full capsule", "vec3 Z = dir / len;", "bool yUp = abs(Z.y) < 0.9;", "vec3 fakeY = yUp ? vec3(0,1,0) : vec3(0,0,1);", "vec3 X = cross(fakeY, Z);", "vec3 Y = cross(Z, X);", "mat3 mat = mat3(X, Y, Z);", "pos *= radius;", "pos = mat * pos + center.xyz;", "normal = mat * normal;", ], "}", "gl_Position = uniProjectionMatrix * uniModelViewMatrix * uniTransfoMatrix * vec4(pos, 1);", `applyMaterial(pos, normal, ${material.attUV}.xy);`, ], }).code; const frag = new TgdShaderFragment({ header: material.fragmentShaderHeader, uniforms: material.uniforms, outputs: { FragColor: "vec4" }, varying: material.varyings, functions: { ...material.extraFragmentShaderFunctions, applyMaterial: ["vec4 applyMaterial() {", [material.fragmentShaderCode], "}"], }, mainCode: ["FragColor = applyMaterial();"], }).code; const prg = new TgdProgram(context.gl, { name: `TgdPainterSegments/TgdProgram#${this.id}`, vert, frag, }); this.prg = prg; this.vaos = datasetsPairs.map(([A, B]) => { const datasetA = extract(A).renameAttributes([ "attXYZR0_A", "attUV0_A", "attInfluence0_A", "attXYZR1_A", "attUV1_A", "attInfluence1_A", ]); const datasetB = extract(B).renameAttributes([ "attXYZR0_B", "attUV0_B", "attInfluence0_B", "attXYZR1_B", "attUV1_B", "attInfluence1_B", ]); if (datasetA.count !== datasetB.count) { throw new Error(`[TgdPainterSegmentsMorphing] Datasets of a pair must have the same count, but we got ${datasetA.count} ≠ ${datasetB.count}!`); } const vao = new TgdVertexArray(context.gl, prg, [geometry.dataset, datasetA, datasetB], geometry.elements); this.instanceCount = datasetA.count; return vao; }); this.vertexCount = geometry.elements?.length ?? 0; } delete() { for (const vao of this.vaos) vao.delete(); this.prg.delete(); } paint(time, delta) { const { context, prg, vao, vertexCount, instanceCount, material } = this; const { gl, camera } = context; gl.disable(gl.DITHER); prg.use(); this.material.setUniforms?.({ program: prg, context, time, delta }); prg.uniform1f("uniMix", this.mix); prg.uniform1f("uniPixelPerScreenUnit", gl.drawingBufferHeight); prg.uniform1f("uniMinRadiusInPixel", this.minRadius); prg.uniform1f("uniRadiusMultiplier", this.radiusMultiplier); prg.uniformMatrix4fv("uniTransfoMatrix", this.transfo.matrix); prg.uniformMatrix4fv("uniModelViewMatrix", camera.matrixModelView); prg.uniformMatrix4fv("uniProjectionMatrix", camera.matrixProjection); material.applyState(this.context, () => { vao.bind(); gl.drawElementsInstanced(gl.TRIANGLES, vertexCount, gl.UNSIGNED_SHORT, 0, instanceCount); vao.unbind(); }); } get vao() { return this.vaos[0]; } } function extract(arg) { if (typeof arg === "function") return arg(); return arg; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VnbWVudHMtbW9ycGhpbmcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvcGFpbnRlci9zZWdtZW50cy9zZWdtZW50cy1tb3JwaGluZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQW9CLDBCQUEwQixFQUFFLE1BQU0sZUFBZSxDQUFBO0FBQzVFLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxXQUFXLENBQUE7QUFDdEMsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHNCQUFzQixDQUFBO0FBQ2pELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxjQUFjLENBQUE7QUFDekMsT0FBTyxFQUFFLGlCQUFpQixFQUFFLGVBQWUsRUFBRSxNQUFNLGFBQWEsQ0FBQTtBQUNoRSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sY0FBYyxDQUFBO0FBQzNDLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLFlBQVksQ0FBQTtBQUNuRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sVUFBVSxDQUFBO0FBQ3pDLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxXQUFXLENBQUE7QUFpQ3ZDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW9CRztBQUNILE1BQU0sT0FBTywwQkFBMkIsU0FBUSxVQUFVO0lBbUJ0RCx1Q0FBdUM7SUFFdkMsWUFDdUIsT0FBbUIsRUFDdEMsT0FBMEM7UUFFMUMsS0FBSyxFQUFFLENBQUE7UUFIWSxZQUFPLEdBQVAsT0FBTyxDQUFZO1FBckIxQixZQUFPLEdBQUcsSUFBSSxVQUFVLEVBQUUsQ0FBQTtRQUVuQyxjQUFTLEdBQUcsQ0FBQyxDQUFBO1FBQ2IscUJBQWdCLEdBQUcsQ0FBQyxDQUFBO1FBQ3BCLG1CQUFjLEdBQUcsQ0FBQyxDQUFBO1FBQ2xCLGlCQUFZLEdBQUcsQ0FBQyxDQUFBO1FBQ2hCLGtCQUFhLEdBQUcsQ0FBQyxDQUFBO1FBQ3hCOzs7V0FHRztRQUNJLFFBQUcsR0FBRyxDQUFDLENBQUE7UUFj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