Source: rendering/batcher/shared/DefaultBatcher.ts

import { ExtensionType } from '../../../extensions/Extensions';
import { Batcher } from './Batcher';
import { BatchGeometry } from './BatchGeometry';
import { DefaultShader } from './DefaultShader';

import type { Matrix } from '../../../maths/matrix/Matrix';
import type { Shader } from '../../renderers/shared/shader/Shader';
import type { BatchableMeshElement, BatchableQuadElement } from './Batcher';

let defaultShader: Shader = null;

/**
 * Represents the common elements for default batch rendering.
 * This interface defines the properties that are used by the DefaultBatcher
 * to render elements efficiently in a batch.
 * @memberof rendering
 */
export interface DefaultBatchElements
{
    /**
     * The color of the element that will be multiplied with the texture color.
     * This is typically represented as a 32-bit integer in RGBA format.
     */
    color: number;

    /**
     * Determines whether the element should be rounded to the nearest pixel.
     * - 0: No rounding (default)
     * - 1: Round to nearest pixel
     * This can help with visual consistency, especially for pixel art styles.
     */
    roundPixels: 0 | 1;

    /**
     * The transform matrix of the element.
     * This matrix represents the position, scale, rotation, and skew of the element.
     */
    transform: Matrix;
}

/**
 * Represents a batchable quad element with default batch properties.
 * @memberof rendering
 */
export interface DefaultBatchableQuadElement extends BatchableQuadElement, DefaultBatchElements {}

/**
 * Represents a batchable mesh element with default batch properties.
 * @memberof rendering
 */
export interface DefaultBatchableMeshElement extends BatchableMeshElement, DefaultBatchElements {}

/**
 * The default batcher is used to batch quads and meshes. This batcher will batch the following elements:
 * - tints
 * - roundPixels
 * - texture
 * - transform
 * @memberof rendering
 */
export class DefaultBatcher extends Batcher
{
    /** @ignore */
    public static extension = {
        type: [
            ExtensionType.Batcher,
        ],
        name: 'default',
    } as const;

    public geometry = new BatchGeometry();
    public shader = defaultShader || (defaultShader = new DefaultShader(this.maxTextures));

    public name = DefaultBatcher.extension.name;

    /** The size of one attribute. 1 = 32 bit. x, y, u, v, color, textureIdAndRound -> total = 6 */
    public vertexSize = 6;

    /**
     * Packs the attributes of a DefaultBatchableMeshElement into the provided views.
     * @param element - The DefaultBatchableMeshElement to pack.
     * @param float32View - The Float32Array view to pack into.
     * @param uint32View - The Uint32Array view to pack into.
     * @param index - The starting index in the views.
     * @param textureId - The texture ID to use.
     */
    public packAttributes(
        element: DefaultBatchableMeshElement,
        float32View: Float32Array,
        uint32View: Uint32Array,
        index: number,
        textureId: number
    )
    {
        const textureIdAndRound = (textureId << 16) | (element.roundPixels & 0xFFFF);

        const wt = element.transform;

        const a = wt.a;
        const b = wt.b;
        const c = wt.c;
        const d = wt.d;
        const tx = wt.tx;
        const ty = wt.ty;

        const { positions, uvs } = element;

        const argb = element.color;

        const offset = element.attributeOffset;
        const end = offset + element.attributeSize;

        for (let i = offset; i < end; i++)
        {
            const i2 = i * 2;

            const x = positions[i2];
            const y = positions[(i2) + 1];

            float32View[index++] = (a * x) + (c * y) + tx;
            float32View[index++] = (d * y) + (b * x) + ty;

            float32View[index++] = uvs[i2];
            float32View[index++] = uvs[(i2) + 1];

            uint32View[index++] = argb;
            uint32View[index++] = textureIdAndRound;
        }
    }

    /**
     * Packs the attributes of a DefaultBatchableQuadElement into the provided views.
     * @param element - The DefaultBatchableQuadElement to pack.
     * @param float32View - The Float32Array view to pack into.
     * @param uint32View - The Uint32Array view to pack into.
     * @param index - The starting index in the views.
     * @param textureId - The texture ID to use.
     */
    public packQuadAttributes(
        element: DefaultBatchableQuadElement,
        float32View: Float32Array,
        uint32View: Uint32Array,
        index: number,
        textureId: number
    )
    {
        const texture = element.texture;

        const wt = element.transform;

        const a = wt.a;
        const b = wt.b;
        const c = wt.c;
        const d = wt.d;
        const tx = wt.tx;
        const ty = wt.ty;

        const bounds = element.bounds;

        const w0 = bounds.maxX;
        const w1 = bounds.minX;
        const h0 = bounds.maxY;
        const h1 = bounds.minY;

        const uvs = texture.uvs;

        // _ _ _ _
        // a b g r
        const argb = element.color;

        const textureIdAndRound = (textureId << 16) | (element.roundPixels & 0xFFFF);

        float32View[index + 0] = (a * w1) + (c * h1) + tx;
        float32View[index + 1] = (d * h1) + (b * w1) + ty;

        float32View[index + 2] = uvs.x0;
        float32View[index + 3] = uvs.y0;

        uint32View[index + 4] = argb;
        uint32View[index + 5] = textureIdAndRound;

        // xy
        float32View[index + 6] = (a * w0) + (c * h1) + tx;
        float32View[index + 7] = (d * h1) + (b * w0) + ty;

        float32View[index + 8] = uvs.x1;
        float32View[index + 9] = uvs.y1;

        uint32View[index + 10] = argb;
        uint32View[index + 11] = textureIdAndRound;

        // xy
        float32View[index + 12] = (a * w0) + (c * h0) + tx;
        float32View[index + 13] = (d * h0) + (b * w0) + ty;

        float32View[index + 14] = uvs.x2;
        float32View[index + 15] = uvs.y2;

        uint32View[index + 16] = argb;
        uint32View[index + 17] = textureIdAndRound;

        // xy
        float32View[index + 18] = (a * w1) + (c * h0) + tx;
        float32View[index + 19] = (d * h0) + (b * w1) + ty;

        float32View[index + 20] = uvs.x3;
        float32View[index + 21] = uvs.y3;

        uint32View[index + 22] = argb;
        uint32View[index + 23] = textureIdAndRound;
    }
}