123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- package h2d.impl;
- import h3d.Indexes;
- import h3d.Buffer;
- /**
- Automates buffer segmentation when rendering 2D geometry with multiple unique textures.
- Primary use-case is to allow usage of multiple textures without the need to manually manage them.
- Causes extra draw call each time a texture is swapped.
- Due to that, for production it is recommended to combine assets in atlases for optimal performance.
- Depending on geometry type, vertex count should be in groups of 4 vertices per quad or 3 indices per triangle.
- **/
- class BatchDrawState {
- /**
- Current active texture of the BatchDrawState.
- Represents the most recent texture that was set with `setTile` or `setTexture`.
- Always null after state initialization or after `clear` call.
- **/
- public var currentTexture(get, never) : h3d.mat.Texture;
- /**
- A total amount of vertices added to the BatchDrawState.
- **/
- public var totalCount(default, null) : Int;
- var head : StateEntry;
- var tail : StateEntry;
- /**
- Create a new BatchDrawState instance.
- **/
- public function new() {
- this.head = this.tail = new StateEntry(null);
- this.totalCount = 0;
- }
- /**
- Switches currently active texture to one in the given `tile` if it differs and splits the render state.
- @param tile A Tile containing a texture that should be used for the next set of vertices. Does nothing if `null`.
- **/
- public inline function setTile( tile : h2d.Tile ) {
- if ( tile != null ) setTexture(tile.getTexture());
- }
- /**
- Switches currently active texture to the given `texture` if it differs and splits the render state.
- @param texture The texture that should be used for the next set of vertices. Does nothing if `null`.
- **/
- public function setTexture( texture : h3d.mat.Texture ) {
- if ( texture != null ) {
- if ( tail.texture == null ) tail.texture = texture;
- else if ( tail.texture != texture ) {
- var cur = tail;
- if ( cur.count == 0 ) cur.set(texture);
- else if ( cur.next == null ) cur.next = tail = new StateEntry(texture);
- else tail = cur.next.set(texture);
- }
- }
- }
- /**
- Add vertices to the state using currently active texture.
- Should be called when rendering buffers add more data in order to properly render the geometry.
- @param count The amount of vertices to add.
- **/
- public inline function add( count : Int ) {
- tail.count += count;
- totalCount += count;
- }
- /**
- Resets the BatchDrawState by removing all texture references and zeroing vertex counter.
- **/
- public function clear() {
- var state = head;
- do {
- state.texture = null;
- state = state.next;
- } while ( state != null );
- tail = head;
- tail.count = 0;
- totalCount = 0;
- }
- /**
- Renders given buffer as a set of quads. Buffer data should be in groups of 4 vertices per quad.
- @param ctx The render context which performs the rendering. Rendering object should call `h2d.RenderContext.beginDrawBatchState` before calling `drawQuads`.
- @param buffer The quad buffer used to render the state.
- @param offset An optional starting offset of the buffer to render in triangles (2 per quad).
- @param length An optional maximum limit of triangles to render.
- When `offset` and `length` are not provided or are default values, slightly faster rendering routine is used.
- **/
- public function drawQuads( ctx : RenderContext, buffer : Buffer, offset = 0, length = -1 ) {
- var state = head;
- var last = tail.next;
- var engine = ctx.engine;
- var stateLen : Int;
- inline function toQuads( count : Int ) return count >> 1;
- if ( offset == 0 && length == -1 ) {
- // Skip extra logic when not restraining rendering
- do {
- ctx.swapTexture(state.texture);
- stateLen = toQuads(state.count);
- engine.renderQuadBuffer(buffer, offset, stateLen);
- offset += stateLen;
- state = state.next;
- } while ( state != last );
- } else {
- if ( length == -1 ) length = toQuads(totalCount) - offset;
- var caret = 0;
- do {
- stateLen = toQuads(state.count);
- if ( caret + stateLen >= offset ) {
- var stateMin = offset >= caret ? offset : caret;
- var stateLen = length > stateLen ? stateLen : length;
- ctx.swapTexture(state.texture);
- engine.renderQuadBuffer(buffer, stateMin, stateLen);
- length -= stateLen;
- if ( length == 0 ) break;
- }
- caret += stateLen;
- state = state.next;
- } while ( state != last );
- }
- }
- /**
- Renders given indices as a set of triangles. Index data should be in groups of 3 vertices per quad.
- @param ctx The render context which performs the rendering. Rendering object should call `h2d.RenderContext.beginDrawBatchState` before calling `drawQuads`.
- @param buffer The vertex buffer used to render the state.
- @param indices Vertex indices used to render the state.
- @param offset An optional starting offset of the buffer to render in triangles.
- @param length An optional maximum limit of triangles to render.
- When `offset` and `length` are not provided or are default values, slightly faster rendering routine is used.
- **/
- public function drawIndexed( ctx : RenderContext, buffer : Buffer, indices : Indexes, offset : Int = 0, length : Int = -1 ) {
- var state = head;
- var last = tail.next;
- var engine = ctx.engine;
- var stateLen : Int;
- inline function toTris( count : Int ) return Std.int(count / 3);
- if ( offset == 0 && length == -1 ) {
- // Skip extra logic when not restraining rendering
- do {
- ctx.swapTexture(state.texture);
- stateLen = toTris(state.count);
- engine.renderIndexed(buffer, indices, offset, stateLen);
- offset += stateLen;
- state = state.next;
- } while ( state != last );
- } else {
- if ( length == -1 ) length = toTris(totalCount);
- var caret = 0;
- do {
- stateLen = toTris(state.count);
- if ( caret + stateLen >= offset ) {
- var stateMin = offset >= caret ? offset : caret;
- var stateLen = length > stateLen ? stateLen : length;
- ctx.swapTexture(state.texture);
- engine.renderIndexed(buffer, indices, stateMin, stateLen);
- length -= stateLen;
- if ( length == 0 ) break;
- }
- caret += stateLen;
- state = state.next;
- } while ( state != last );
- }
- }
- inline function get_currentTexture() return tail.texture;
- }
- private class StateEntry {
- /**
- Texture associated with draw state instance.
- **/
- public var texture : h3d.mat.Texture;
- /**
- A size of batch state.
- **/
- public var count : Int;
- public var next:StateEntry;
- public function new( texture : h3d.mat.Texture ) {
- this.texture = texture;
- this.count = 0;
- }
- public function set( texture : h3d.mat.Texture ) : StateEntry {
- this.texture = texture;
- this.count = 0;
- return this;
- }
- }
|