drawQuads and atlases

Let’s pretend we have a BitmapData atlas with two graphics in it:

[  A  |  B  ]

Assuming each side is 100 pixels all around, you should be able to use drawQuads in order to draw both, like this:

graphics.beginBitmapFill (bitmapData);
graphics.drawQuads (Vector.ofArray ([
    0, 0, 100, 100,
    100, 0, 100, 100
]));

This draws both A and B from the source BitmapData, using the proper UV coordinates for each rectangle. You could use drawRect with the full-size of the BitmapData again and get the same effect. However, drawQuads supports the ability to transform the target draw position. The source UV remains the same, but we can override the rectangle position and define our own (x, y) pair, abcd, or full (a, b, c, d, tx, ty) transform.

graphics.beginBitmapFill (bitmapData);
graphics.drawQuads (Vector.ofArray ([
    0, 0, 100, 100,
    100, 0, 100, 100
]), null, Vector.ofArray ([
    100, 200,
    300, 100
]));

The second parameter can be used to reference rectangles (from the first parameter) by index. You can define the rect data as in the above examples, or another way is the Tileset class (for example) exposes the data for each rectangle in the tileset as an array that can be used with drawQuads.

var tileset = new Tileset (bitmapData);
tileset.addRect (new Rectangle (0, 0, 100, 100));
tileset.addRect (new Rectangle (100, 0, 100, 100));

graphics.beginBitmapFill (bitmapData);
graphics.drawQuads (tileset.rectData, Vector.ofArray ([ 0, 1 ]));

It’s not currently possible to add alpha without using a shader fill. I think a shader with alpha only would look something like this:

import openfl.display.GraphicsShader;

class AlphaGraphicsShader extends GraphicsShader {
	
	@:glVertexSource(
		"#pragma header
		attribute float alpha;
		varying float alphav;
		
		void main(void) {
			
			alphav = alpha;
			#pragma body
			
		}"
	)
	
	@:glFragmentSource(
		"#pragma header
		varying float alphav;
		
		void main(void) {
			
			#pragma body
			gl_FragColor = gl_FragColor * alphav;
			
		}"
	)
	
	public function new () {
		super ();
	}
	
}

Then rendering would look more like:

var shader = new AlphaGraphicsShader ();
shader.bitmap.input = bitmapData;
shader.alpha.value = [ 0.5 ];

graphics.beginShaderFill (shader);
graphics.drawQuads (tileset.rectData, new Vector ([ 0, 1 ]));

This would set 0.5 as a constant value for all vertices. The set the alpha for each vertex (and ultimately, quad) separately, you would need to define enough alpha values to cover the render. In the latest OpenFL release, you need 6 values per quad. In the next release, I think this will be dropping down to 4 since we’ll be using an index buffer instead.

var shader = new AlphaGraphicsShader ();
shader.alpha = [];

for (i in 0...6) shader.alpha.push (0.5);
for (i in 0...6) shader.alpha.push (0.25);

graphics.beginShaderFill (shader);
graphics.drawQuads (tileset.rectData, new Vector ([ 0, 1 ]));

You are not trying to abuse the API, though (depending on your use case) you might also consider Tilemap, which is easier to use

var tilemap = new Tilemap (stage.stageWidth, stage.stageHeight);

var tile = new Tile (0);
tile.tileset = tileset;
tile.alpha = 0.5;
tile.x = 200;
tilemap.addTile (tile);

var tile = new Tile (1);
tile.tileset = tileset;
tile.alpha = 0.25;
tile.x = 300;
tile.y = 100;
tilemap.addTile (tile);

Tilemap lets you set a default Tileset for the whole map, but you also can set/override the tileset on each Tile, making it pretty flexible.

You can do alpha, x, y, scale, color transform, custom shaders and other properties on each Tile, or group using TileContainer, so it’s sort of a mix of drawQuads and the display list rendering. Some features (like color transform or shaders) don’t work on software (cairo, canvas, DOM) targets, but otherwise Tilemap (like drawQuads) should work on these renderers

4 Likes