UNPKG

shaku

Version:

A simple and effective JavaScript game development framework that knows its place!

189 lines (168 loc) 7.3 kB
/** * Implement the gfx sprite batch renderer. * * |-- copyright and license --| * @module Shaku * @file shaku\src\gfx\draw_batches\sprite_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 TextureAssetBase = require('../../assets/texture_asset_base'); const { Rectangle } = require('../../utils'); const Vector2 = require('../../utils/vector2'); const Vector3 = require('../../utils/vector3'); const DrawBatch = require('./draw_batch'); const SpriteBatchBase = require('./sprite_batch_base'); const _logger = require('../../logger.js').getLogger('gfx-sprite-batch'); /** * Sprite batch renderer. * Responsible to drawing a batch of sprites with as little draw calls as possible. */ class SpriteBatch extends SpriteBatchBase { /** * Create the sprites batch. * @param {Number=} batchSpritesCount Internal buffers size, in sprites count (sprite = 4 vertices). Bigger value = faster rendering but more RAM. * @param {Boolean=} enableVertexColor If true (default) will support vertex color. * @param {Boolean=} enableNormals If true (not default) will support vertex normals. */ constructor(batchSpritesCount, enableVertexColor, enableNormals) { // init draw batch super(batchSpritesCount, enableVertexColor, enableNormals); } /** * Get the gfx manager. * @private */ get #_gfx() { return DrawBatch._gfx; } /** * Get the web gl instance. * @private */ get #_gl() { return DrawBatch._gfx._internal.gl; } /** * @inheritdoc */ get defaultEffect() { return this.supportVertexColor ? this.#_gfx.builtinEffects.Sprites : this.#_gfx.builtinEffects.SpritesNoVertexColor; } /** * Push vertices to drawing batch. * @param {TextureAssetBase} texture Texture to draw. * @param {Array<Vertex>} vertices Vertices to push. Vertices count must be dividable by 4 to keep the batch consistent of quads. */ drawVertices(texture, vertices) { // sanity this.__validateDrawing(true); // update texture this._updateTexture(texture); // sanity check if ((vertices.length % 4) !== 0) { _logger.warn("Tried to push vertices that are not multiplication of 4!"); return; } // push vertices let i = 0; let colors = this._buffers.colorsArray; let uvs = this._buffers.textureArray; let positions = this._buffers.positionArray; let normals = this._buffers.normalsArray; for (let vertex of vertices) { // push color if (this.__currDrawingParams.hasVertexColor) { colors[colors._index++] = (vertex.color.r || 0); colors[colors._index++] = (vertex.color.g || 0); colors[colors._index++] = (vertex.color.b || 0); colors[colors._index++] = (vertex.color.a || 0); } // push normals if (normals) { normals[normals._index++] = vertex.normal.x; normals[normals._index++] = vertex.normal.y; normals[normals._index++] = vertex.normal.z; } // push texture coords uvs[uvs._index++] = (vertex.textureCoord.x || 0); uvs[uvs._index++] = (vertex.textureCoord.y || 0); // push position positions[positions._index++] = (vertex.position.x || 0); positions[positions._index++] = (vertex.position.y || 0); positions[positions._index++] = (vertex.position.z || 0); // every 4 vertices.. if (i++ === 3) { // update quads count this.__quadsCount++; // check if full if (this.__quadsCount >= this.__maxQuadsCount) { this._handleFullBuffer(); } // reset count i = 0; } } // mark as dirty this.__dirty = true; } /** * Add a quad to draw. * @param {TextureAssetBase} texture Texture to draw. * @param {Vector2|Vector3} position Drawing position (at origin). If vector3 is provided, will pass z value to the shader code position attribute. * @param {Vector2|Vector3|Number} size Drawing size. If vector3 is provided, will pass z value to the shader code position attribute for the bottom vertices, as position.z + size.z. * @param {Rectangle} sourceRectangle Source rectangle, or undefined to use the entire texture. * @param {Color|Array<Color>|undefined} color Tint color, or undefined to not change color. If array is set, will assign each color to different vertex, starting from top-left. * @param {Number=} rotation Rotate sprite. * @param {Vector2=} origin Drawing origin. This will be the point at 'position' and rotation origin. * @param {Vector2=} skew Skew the drawing corners on X and Y axis, around the origin point. */ drawQuad(texture, position, size, sourceRectangle, color, rotation, origin, skew) { let sprite = this.#_gfx.Sprite.build(texture, position, size, sourceRectangle, color, rotation, origin, skew); this.drawSprite(sprite); } /** * Add sprites group to this batch. * @param {SpritesGroup} group Sprite group to draw. * @param {Boolean=} cullOutOfScreen If true, will cull sprites that are not visible in currently set rendering region. */ drawSpriteGroup(group, cullOutOfScreen) { let transform = group.getTransform(); this.drawSprite(group._sprites, transform, cullOutOfScreen); } /** * Add a quad that covers a given destination rectangle. * @param {TextureAssetBase} texture Texture to draw. * @param {Rectangle|Vector2} destRect Destination rectangle to draw on. If vector is provided, will draw from 0,0 with vector as size. * @param {Rectangle=} sourceRect Source rectangle, or undefined to use the entire texture. * @param {Color|Array<Color>|undefined} color Tint color, or undefined to not change color. If array is set, will assign each color to different vertex, starting from top-left. * @param {Vector2=} origin Drawing origin. This will be the point at 'position' and rotation origin. */ drawRectangle(texture, destRect, sourceRect, color, origin) { if ((destRect.isVector2) || (destRect.isVector3)) { destRect = new Rectangle(0, 0, destRect.x, destRect.y); } let position = origin ? destRect.getPosition().addSelf(size.mul(origin)) : destRect.getCenter(); origin = origin || Vector2.halfReadonly; let size = destRect.getSize(); this.drawQuad(texture, position, size, sourceRect, color); } } // export the sprite batch class module.exports = SpriteBatch;