HaxeFlixel/flixel

View on GitHub
flixel/graphics/tile/FlxDrawTrianglesItem.hx

Summary

Maintainability
Test Coverage
package flixel.graphics.tile;

import flixel.FlxCamera;
import flixel.graphics.frames.FlxFrame;
import flixel.graphics.tile.FlxDrawBaseItem.FlxDrawItemType;
import flixel.math.FlxMatrix;
import flixel.math.FlxPoint;
import flixel.math.FlxRect;
import flixel.system.FlxAssets.FlxShader;
import flixel.util.FlxColor;
import openfl.display.Graphics;
import openfl.display.ShaderParameter;
import openfl.display.TriangleCulling;
import openfl.geom.ColorTransform;

typedef DrawData<T> = openfl.Vector<T>;

/**
 * @author Zaphod
 */
class FlxDrawTrianglesItem extends FlxDrawBaseItem<FlxDrawTrianglesItem>
{
    static var point:FlxPoint = FlxPoint.get();
    static var rect:FlxRect = FlxRect.get();

    #if !flash
    public var shader:FlxShader;
    var alphas:Array<Float>;
    var colorMultipliers:Array<Float>;
    var colorOffsets:Array<Float>;
    #end

    public var vertices:DrawData<Float> = new DrawData<Float>();
    public var indices:DrawData<Int> = new DrawData<Int>();
    public var uvtData:DrawData<Float> = new DrawData<Float>();
    public var colors:DrawData<Int> = new DrawData<Int>();

    public var verticesPosition:Int = 0;
    public var indicesPosition:Int = 0;
    public var colorsPosition:Int = 0;

    var bounds:FlxRect = FlxRect.get();

    public function new()
    {
        super();
        type = FlxDrawItemType.TRIANGLES;
        #if !flash
        alphas = [];
        #end
    }

    override public function render(camera:FlxCamera):Void
    {
        if (!FlxG.renderTile)
            return;

        if (numTriangles <= 0)
            return;

        #if !flash
        var shader = shader != null ? shader : graphics.shader;
        shader.bitmap.input = graphics.bitmap;
        shader.bitmap.filter = (camera.antialiasing || antialiasing) ? LINEAR : NEAREST;
        shader.bitmap.wrap = REPEAT; // in order to prevent breaking tiling behaviour in classes that use drawTriangles
        shader.alpha.value = alphas;

        if (colored || hasColorOffsets)
        {
            shader.colorMultiplier.value = colorMultipliers;
            shader.colorOffset.value = colorOffsets;
        }

        setParameterValue(shader.hasTransform, true);
        setParameterValue(shader.hasColorTransform, colored || hasColorOffsets);

        #if (openfl > "8.7.0")
        camera.canvas.graphics.overrideBlendMode(blend);
        #end

        camera.canvas.graphics.beginShaderFill(shader);
        #else
        camera.canvas.graphics.beginBitmapFill(graphics.bitmap, null, true, (camera.antialiasing || antialiasing));
        #end

        camera.canvas.graphics.drawTriangles(vertices, indices, uvtData, TriangleCulling.NONE);
        camera.canvas.graphics.endFill();

        #if FLX_DEBUG
        if (FlxG.debugger.drawDebug)
        {
            var gfx:Graphics = camera.debugLayer.graphics;
            gfx.lineStyle(1, FlxColor.BLUE, 0.5);
            gfx.drawTriangles(vertices, indices, uvtData);
        }
        #end

        super.render(camera);
    }

    override public function reset():Void
    {
        super.reset();
        vertices.length = 0;
        indices.length = 0;
        uvtData.length = 0;
        colors.length = 0;

        verticesPosition = 0;
        indicesPosition = 0;
        colorsPosition = 0;
        #if !flash
        alphas.splice(0, alphas.length);
        if (colorMultipliers != null)
            colorMultipliers.splice(0, colorMultipliers.length);
        if (colorOffsets != null)
            colorOffsets.splice(0, colorOffsets.length);
        #end
    }

    override public function dispose():Void
    {
        super.dispose();

        vertices = null;
        indices = null;
        uvtData = null;
        colors = null;
        bounds = null;
        #if !flash
        alphas = null;
        colorMultipliers = null;
        colorOffsets = null;
        #end
    }

    public function addTriangles(vertices:DrawData<Float>, indices:DrawData<Int>, uvtData:DrawData<Float>, ?colors:DrawData<Int>, ?position:FlxPoint,
            ?cameraBounds:FlxRect #if !flash , ?transform:ColorTransform #end):Void
    {
        if (position == null)
            position = point.set();

        if (cameraBounds == null)
            cameraBounds = rect.set(0, 0, FlxG.width, FlxG.height);

        var verticesLength:Int = vertices.length;
        var prevVerticesLength:Int = this.vertices.length;
        var numberOfVertices:Int = Std.int(verticesLength / 2);
        var prevIndicesLength:Int = this.indices.length;
        var prevUVTDataLength:Int = this.uvtData.length;
        var prevColorsLength:Int = this.colors.length;
        var prevNumberOfVertices:Int = this.numVertices;

        var tempX:Float, tempY:Float;
        var i:Int = 0;
        var currentVertexPosition:Int = prevVerticesLength;

        while (i < verticesLength)
        {
            tempX = position.x + vertices[i];
            tempY = position.y + vertices[i + 1];

            this.vertices[currentVertexPosition++] = tempX;
            this.vertices[currentVertexPosition++] = tempY;

            if (i == 0)
            {
                bounds.set(tempX, tempY, 0, 0);
            }
            else
            {
                inflateBounds(bounds, tempX, tempY);
            }

            i += 2;
        }

        if (!cameraBounds.overlaps(bounds))
        {
            this.vertices.splice(this.vertices.length - verticesLength, verticesLength);
        }
        else
        {
            var uvtDataLength:Int = uvtData.length;
            for (i in 0...uvtDataLength)
            {
                this.uvtData[prevUVTDataLength + i] = uvtData[i];
            }

            var indicesLength:Int = indices.length;
            for (i in 0...indicesLength)
            {
                this.indices[prevIndicesLength + i] = indices[i] + prevNumberOfVertices;
            }

            if (colored)
            {
                for (i in 0...numberOfVertices)
                {
                    this.colors[prevColorsLength + i] = colors[i];
                }

                colorsPosition += numberOfVertices;
            }

            verticesPosition += verticesLength;
            indicesPosition += indicesLength;
        }

        position.putWeak();
        cameraBounds.putWeak();

        #if !flash
        for (_ in 0...numTriangles)
        {
            alphas.push(transform != null ? transform.alphaMultiplier : 1.0);
            alphas.push(transform != null ? transform.alphaMultiplier : 1.0);
            alphas.push(transform != null ? transform.alphaMultiplier : 1.0);
        }

        if (colored || hasColorOffsets)
        {
            if (colorMultipliers == null)
                colorMultipliers = [];

            if (colorOffsets == null)
                colorOffsets = [];

            for (_ in 0...(numTriangles * 3))
            {
                if(transform != null)
                {
                    colorMultipliers.push(transform.redMultiplier);
                    colorMultipliers.push(transform.greenMultiplier);
                    colorMultipliers.push(transform.blueMultiplier);

                    colorOffsets.push(transform.redOffset);
                    colorOffsets.push(transform.greenOffset);
                    colorOffsets.push(transform.blueOffset);
                    colorOffsets.push(transform.alphaOffset);
                }
                else
                {
                    colorMultipliers.push(1);
                    colorMultipliers.push(1);
                    colorMultipliers.push(1);
    
                    colorOffsets.push(0);
                    colorOffsets.push(0);
                    colorOffsets.push(0);
                    colorOffsets.push(0);
                }

                colorMultipliers.push(1);
            }
        }
        #end
    }

    inline function setParameterValue(parameter:ShaderParameter<Bool>, value:Bool):Void
    {
        if (parameter.value == null)
            parameter.value = [];
        parameter.value[0] = value;
    }

    public static inline function inflateBounds(bounds:FlxRect, x:Float, y:Float):FlxRect
    {
        if (x < bounds.x)
        {
            bounds.width += bounds.x - x;
            bounds.x = x;
        }

        if (y < bounds.y)
        {
            bounds.height += bounds.y - y;
            bounds.y = y;
        }

        if (x > bounds.x + bounds.width)
        {
            bounds.width = x - bounds.x;
        }

        if (y > bounds.y + bounds.height)
        {
            bounds.height = y - bounds.y;
        }

        return bounds;
    }

    override public function addQuad(frame:FlxFrame, matrix:FlxMatrix, ?transform:ColorTransform):Void
    {
        var prevVerticesPos:Int = verticesPosition;
        var prevIndicesPos:Int = indicesPosition;
        var prevColorsPos:Int = colorsPosition;
        var prevNumberOfVertices:Int = numVertices;

        var point = FlxPoint.get();
        point.transform(matrix);

        vertices[prevVerticesPos] = point.x;
        vertices[prevVerticesPos + 1] = point.y;

        uvtData[prevVerticesPos] = frame.uv.x;
        uvtData[prevVerticesPos + 1] = frame.uv.y;

        point.set(frame.frame.width, 0);
        point.transform(matrix);

        vertices[prevVerticesPos + 2] = point.x;
        vertices[prevVerticesPos + 3] = point.y;

        uvtData[prevVerticesPos + 2] = frame.uv.width;
        uvtData[prevVerticesPos + 3] = frame.uv.y;

        point.set(frame.frame.width, frame.frame.height);
        point.transform(matrix);

        vertices[prevVerticesPos + 4] = point.x;
        vertices[prevVerticesPos + 5] = point.y;

        uvtData[prevVerticesPos + 4] = frame.uv.width;
        uvtData[prevVerticesPos + 5] = frame.uv.height;

        point.set(0, frame.frame.height);
        point.transform(matrix);

        vertices[prevVerticesPos + 6] = point.x;
        vertices[prevVerticesPos + 7] = point.y;

        point.put();

        uvtData[prevVerticesPos + 6] = frame.uv.x;
        uvtData[prevVerticesPos + 7] = frame.uv.height;

        indices[prevIndicesPos] = prevNumberOfVertices;
        indices[prevIndicesPos + 1] = prevNumberOfVertices + 1;
        indices[prevIndicesPos + 2] = prevNumberOfVertices + 2;
        indices[prevIndicesPos + 3] = prevNumberOfVertices + 2;
        indices[prevIndicesPos + 4] = prevNumberOfVertices + 3;
        indices[prevIndicesPos + 5] = prevNumberOfVertices;

        if (colored)
        {
            var red = 1.0;
            var green = 1.0;
            var blue = 1.0;
            var alpha = 1.0;

            if (transform != null)
            {
                red = transform.redMultiplier;
                green = transform.greenMultiplier;
                blue = transform.blueMultiplier;

                #if !neko
                alpha = transform.alphaMultiplier;
                #end
            }

            var color = FlxColor.fromRGBFloat(red, green, blue, alpha);

            colors[prevColorsPos] = color;
            colors[prevColorsPos + 1] = color;
            colors[prevColorsPos + 2] = color;
            colors[prevColorsPos + 3] = color;

            colorsPosition += 4;
        }

        verticesPosition += 8;
        indicesPosition += 6;
    }

    override function get_numVertices():Int
    {
        return Std.int(vertices.length / 2);
    }

    override function get_numTriangles():Int
    {
        return Std.int(indices.length / 3);
    }
}