shaku
Version:
A simple and effective JavaScript game development framework that knows its place!
316 lines (272 loc) • 9.35 kB
JavaScript
/**
* An interface for a batch renderer.
*
* |-- copyright and license --|
* @module Shaku
* @file shaku\src\gfx\draw_batches\draw_batch.js
* @author Ronen Ness (ronenness@gmail.com | http://ronenness.com)
* @copyright (c) 2021 Ronen Ness
* @license MIT
* |-- end copyright and license --|
*
*/
'use strict';
const { BlendModes } = require('../blend_modes');
const { BuffersUsage } = require('./buffers_usage');
const Matrix = require('../../utils/matrix.js');
const _logger = require('../../logger.js').getLogger('gfx-draw-batch');
/**
* Base class for a drawing batch, used to draw a collection of sprites or shapes.
*/
class DrawBatch
{
/**
* Create the draw batch.
*/
constructor()
{
// set default usage mode
this.setBuffersUsage(BuffersUsage.StreamDraw);
// will have values after calling 'begin()'
this.__currDrawingParams = null;
// if true, it means the buffers have been frozen and can't be changed
this.__staticBuffers = false;
// are we currently between 'begin()' and 'end()' calls
this.__drawing = false;
}
/**
* Make the batch buffers static.
* This means you won't be able to change the drawings in this batch once end() is called, but you'll be able to redraw
* the batch with different effects and transformations without copying any data, and much faster.
* This also free up some internal arrays, thus reducing the memory used for this batch.
* Note: must be called after 'begin()' and right before the 'end()' call.
*/
makeStatic()
{
this.__validateBatch();
if (!this.isDrawing) { throw new Error("Must call 'makeStatic()' between 'begin()' and 'end()'."); }
this.setBuffersUsage(this.BuffersUsage.StaticDraw);
this.__staticBuffers = true;
}
/**
* Get the default effect to use for this drawing batch type.
* @returns {Effect} Default effect to use for this drawing batch.
*/
get defaultEffect()
{
return null;
}
/**
* Get the BuffersUsage enum.
* @see BuffersUsage
*/
get BuffersUsage()
{
return BuffersUsage;
}
/**
* Destroy the batch and free any resources assigned to it.
*/
destroy()
{
}
/**
* Return if the batch was destroyed.
* @returns {Boolean} True if batch was destoryed.
*/
get isDestroyed()
{
return false;
}
/**
* Throw exception if batch was destoryed.
* @private
*/
__validateBatch()
{
if (this.isDestroyed) {
throw new Error("Can't perform this action after the batch was destroyed!");
}
}
/**
* Set the way we mark the buffers we pass to the GPU based on expected behavior.
* Use StreamDraw if you want to set buffers once, and use them in GPU few times.
* Use DynamicDraw if you want to set buffers many times, and use them in GPU many times.
* Use StaticDraw if you want to set buffers once, and use them in GPU many times.
* @param {BuffersUsage} usage Buffers usage.
*/
setBuffersUsage(usage)
{
switch (usage)
{
case BuffersUsage.DynamicDraw:
this.__buffersUsage = DrawBatch._gfx._internal.gl.DYNAMIC_DRAW;
break;
case BuffersUsage.StreamDraw:
this.__buffersUsage = DrawBatch._gfx._internal.gl.STREAM_DRAW;
break;
case BuffersUsage.StaticDraw:
this.__buffersUsage = DrawBatch._gfx._internal.gl.STATIC_DRAW;
break;
default:
this.__buffersUsage = DrawBatch._gfx._internal.gl.DYNAMIC_DRAW;
_logger.warn("Illegal buffers usage value: " + usage);
break;
}
}
/**
* Return if the batch is currently drawing.
* @returns {Boolean} If the batch began drawing.
*/
get isDrawing()
{
return this.__drawing;
}
/**
* Throw exception if batch is not currently drawing.
* @private
*/
__validateDrawing(validateNotStatic)
{
if (!this.isDrawing) {
throw new Error("Can't perform this action without calling 'begin()' first!");
}
if (validateNotStatic && this.__staticBuffers) {
throw new Error("Can't perform this action after batch has turned static!");
}
}
/**
* Start drawing this batch.
* You must call this before doing any draw calls.
* @param {BlendModes=} blendMode Blend mode to draw this batch in.
* @param {Effect=} effect Effect to use. If not defined will use this batch type default effect.
* @param {Matrix=} transform Optional transformations to apply on all sprites.
* @param {*=} overrideEffectFlags Optional flags to override effect's defaults. Possible keys: {enableDepthTest, enableFaceCulling, enableStencilTest, enableDithering}.
*/
begin(blendMode, effect, transform, overrideEffectFlags)
{
// sanity - not already drawing
if (this.isDrawing) {
throw new Error("Can't call Drawing Batch 'begin' twice without calling 'end()' first!");
}
// sanity - batch is not destoryed
this.__validateBatch();
// we might still have values in this.__currDrawingParams if 'preserve buffers' is true.
// if so, we extract last texture from it
let lastTexture = this.__currDrawingParams ? (this.__currDrawingParams.texture || null) : null;
// set new drawing params
effect = effect || this.defaultEffect;
blendMode = blendMode || this.defaultBlendMode;
this.__currDrawingParams = {
blendMode: blendMode,
effect: effect,
transform: transform || Matrix.identity,
overrideEffectFlags: overrideEffectFlags,
hasVertexColor: effect.hasVertexColor,
texture: lastTexture
};
// we are now drawing
this.__drawing = true;
}
/**
* Finish drawing without presenting on screen.
*/
endWithoutDraw()
{
// sanity
this.__validateBatch();
this.__validateDrawing(false);
// clear buffers and drawing params
if (!this.__staticBuffers) {
this.clear();
this.__currDrawingParams = null;
}
// no longer drawing
this.__drawing = false;
}
/**
* End drawing and present whatever left in buffers on screen.
*/
end()
{
// sanity
this.__validateBatch();
this.__validateDrawing(false);
// draw current batch data
this._drawBatch();
// clear buffers and drawing params
if (!this.__staticBuffers) {
this.clear();
this.__currDrawingParams = null;
}
// no longer drawing
this.__drawing = false;
}
/**
* Draw whatever is currently in buffers without ending the draw batch.
*/
present()
{
this._drawBatch();
}
/**
* Clear this buffer from any drawings in it.
* Called internally if 'preserveBuffers' is not true.
*/
clear()
{
if (this.__staticBuffers) {
throw new Error("Can't clear batch after it was turned static. You can only destroy it.");
}
}
/**
* Return if this batch was turned static.
* @returns {Boolean} True if its a static batch you can no longer change.
*/
get isStatic()
{
return this.__staticBuffers;
}
/**
* Get the default blend mode to use for this drawing batch.
*/
get defaultBlendMode()
{
return BlendModes.AlphaBlend;
}
/**
* Draw current batch with set drawing params.
* @private
*/
_drawBatch()
{
// sanity
this.__validateBatch();
this.__validateDrawing(false);
// get default effect
let effect = this.__currDrawingParams.effect;
// get the gfx manager
let gfx = DrawBatch._gfx;
// set effect
gfx._internal.useEffect(effect, this.__currDrawingParams.overrideEffectFlags);
// set blend mode if needed
gfx._internal.setBlendMode(this.__currDrawingParams.blendMode);
// set world matrix
effect.setWorldMatrix(this.__currDrawingParams.transform);
// set active texture
gfx._internal.setActiveTexture(this.__currDrawingParams.texture);
// trigger on set effect
this._onSetEffect(effect, this.__currDrawingParams.texture);
}
/**
* Called internally after we set the effect and texture and before we start rendering batch.
* @private
*/
_onSetEffect(effect, texture)
{
}
}
// will be set by the Gfx manager during init
DrawBatch._gfx = null;
// export the draw batch class
module.exports = DrawBatch;