Pour ceux que la programmation intéresse (bon je rêve pas trop on est sur avn) voilà comment on affiche des hapistes:
// -----------------------------------------------------------------------------
// CLASS DECLARATION
// -----------------------------------------------------------------------------
function PPU_SpritesLayer( tilesheetTexture, numSprites )
{
this.build( tilesheetTexture, numSprites );
return this;
}
// -----------------------------------------------------------------------------
// STATIC PROPS
// -----------------------------------------------------------------------------
// context
PPU_SpritesLayer.CONTEXT3D = null;
PPU_SpritesLayer.CRASHED = false;
PPU_SpritesLayer.SCREEN_WIDTH = 1;
PPU_SpritesLayer.SCREEN_HEIGHT = 1;
// static initialized
PPU_SpritesLayer.INITIALIZED = false;
// instance index
PPU_SpritesLayer.NUM_INSTANCES = 0;
// tilesheet dimensions
// for the moment we use fixed dimensions, GBA format.
// multi-resolution will be a later work and should impact only tilesheets
PPU_SpritesLayer.TILESHEET_WIDTH = 128, PPU_SpritesLayer.TILESHEET_HEIGHT = 128;
// vertex shader (for the moment, the same as tilemapLayer, but will be modified)
PPU_SpritesLayer.VERTEX_SHADER_SOURCE = `
attribute vec4 aVertexPosition;
attribute vec2 aTilesheetTextureCoord;
attribute vec2 aSpritesmapTextureCoord;
varying highp vec2 vTilesheetTextureCoord;
varying highp vec2 vSpritesmapTextureCoord;
void main(void) {
gl_Position = aVertexPosition;
vTilesheetTextureCoord = aTilesheetTextureCoord;
vSpritesmapTextureCoord = aSpritesmapTextureCoord;
}
`;
PPU_SpritesLayer.VERTEX_SHADER_ID = 0;
// fragment shader
PPU_SpritesLayer.FRAGMENT_SHADER_SOURCE = `
varying highp vec2 vTilesheetTextureCoord;
varying highp vec2 vSpritesmapTextureCoord;
uniform sampler2D uTilesheetSampler;
uniform sampler2D uTilemapSampler;
uniform sampler2D uPaletteSampler;
const highp float c05 = 0.5;
const highp float c255 = 255.0 ;
const highp float c256 = 256.0 ;
highp vec4 tile;
highp float sheetTexCoordX;
highp float sheetTexCoordY;
highp vec4 colorIndex;
void main(void) {
// get cell info in 64*32 tilemap
tile = texture2D( uTilemapSampler, vSpritesmapTextureCoord );
// get the color index in 16*16 tileSheet
sheetTexCoordX = vTilesheetTextureCoord.x + (tile.r*c255+c05)/c256 ;
sheetTexCoordY = vTilesheetTextureCoord.y + (tile.g*c255+c05)/c256 ;
colorIndex = texture2D( uTilesheetSampler, vec2( ( -2.0 * tile.a + 1.0 )* sheetTexCoordX , sheetTexCoordY ) );
// get the color in the 256x256 palette
gl_FragColor = texture2D( uPaletteSampler, vec2( (colorIndex.a*c255+c05)/c256, (tile.b*c255+c05)/c256 ) );
}
`;
PPU_SpritesLayer.FRAGMENT_SHADER_ID = 0;
// shader program
PPU_SpritesLayer.SHADER_PROGRAM_ID = 0;
// shader locations
PPU_SpritesLayer.XY_ATTRIB_LOCATION = 0;
PPU_SpritesLayer.TILESHEET_UV_ATTRIB_LOCATION = 0;
PPU_SpritesLayer.SPRITESMAP_UV_ATTRIB_LOCATION = 0;
PPU_SpritesLayer.TILESHEET_TEXTURE_UNIFORM_LOCATION = 0;
PPU_SpritesLayer.SPRITESMAP_TEXTURE_UNIFORM_LOCATION = 0;
PPU_SpritesLayer.PALETTE_TEXTURE_UNIFORM_LOCATION = 0;
// -----------------------------------------------------------------------------
// STATIC METHODS
// -----------------------------------------------------------------------------
PPU_SpritesLayer.Init = function (gl, screenWidth, screenHeight)
{
PPU_SpritesLayer.CONTEXT3D = gl;
PPU_SpritesLayer.SCREEN_WIDTH = screenWidth; // avoid spaghetti, use modular classes
PPU_SpritesLayer.SCREEN_HEIGHT = screenHeight;
console.log('PPU_SpritesLayer static shader compilation -----------------');
// compile vertex shader
PPU_SpritesLayer.VERTEX_SHADER_ID = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(PPU_SpritesLayer.VERTEX_SHADER_ID, PPU_SpritesLayer.VERTEX_SHADER_SOURCE);
gl.compileShader(PPU_SpritesLayer.VERTEX_SHADER_ID);
// compile error
if (!gl.getShaderParameter(PPU_SpritesLayer.VERTEX_SHADER_ID, gl.COMPILE_STATUS)) {
alert('PPU_SpritesLayer compile vertex shader error: ' + gl.getShaderInfoLog(PPU_SpritesLayer.VERTEX_SHADER_ID));
gl.deleteShader(PPU_SpritesLayer.VERTEX_SHADER_ID); // AJOUTER CA DANS LE CPP !!!
PPU_SpritesLayer.CRASHED = true;
return false;
} else
{
console.log("PPU_SpritesLayer vertex shader compiled.");
}
// compile fragment shader
PPU_SpritesLayer.FRAGMENT_SHADER_ID = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(PPU_SpritesLayer.FRAGMENT_SHADER_ID, PPU_SpritesLayer.FRAGMENT_SHADER_SOURCE);
gl.compileShader(PPU_SpritesLayer.FRAGMENT_SHADER_ID);
// compile error
if (!gl.getShaderParameter(PPU_SpritesLayer.FRAGMENT_SHADER_ID, gl.COMPILE_STATUS)) {
alert('PPU_SpritesLayer compile fragment shader error: ' + gl.getShaderInfoLog(PPU_SpritesLayer.FRAGMENT_SHADER_ID));
gl.deleteShader(PPU_SpritesLayer.FRAGMENT_SHADER_ID); // AJOUTER CA DANS LE CPP !!!
PPU_SpritesLayer.CRASHED = true;
return false;
} else
{
console.log("PPU_SpritesLayer fragment shader compiled");
}
// link the program shader
PPU_SpritesLayer.SHADER_PROGRAM_ID = gl.createProgram();
gl.attachShader(PPU_SpritesLayer.SHADER_PROGRAM_ID, PPU_SpritesLayer.VERTEX_SHADER_ID);
gl.attachShader(PPU_SpritesLayer.SHADER_PROGRAM_ID, PPU_SpritesLayer.FRAGMENT_SHADER_ID);
gl.linkProgram(PPU_SpritesLayer.SHADER_PROGRAM_ID);
// compile error
if (!gl.getProgramParameter(PPU_SpritesLayer.SHADER_PROGRAM_ID, gl.LINK_STATUS)) {
alert('PPU_SpritesLayer link shaders program error: ' + gl.getProgramInfoLog(PPU_SpritesLayer.SHADER_PROGRAM_ID));
gl.deleteShader(PPU_SpritesLayer.VERTEX_SHADER_ID); // AJOUTER CA DANS LE CPP !!!
gl.deleteShader(PPU_SpritesLayer.FRAGMENT_SHADER_ID); // AJOUTER CA DANS LE CPP !!!
PPU_SpritesLayer.CRASHED = true;
return false;
} else
{
console.log("PPU_SpritesLayer shader programs linked");
}
// shader locations
PPU_SpritesLayer.XY_ATTRIB_LOCATION = gl.getAttribLocation(PPU_SpritesLayer.SHADER_PROGRAM_ID, 'aVertexPosition');
PPU_SpritesLayer.TILESHEET_UV_ATTRIB_LOCATION = gl.getAttribLocation(PPU_SpritesLayer.SHADER_PROGRAM_ID, 'aTilesheetTextureCoord');
PPU_SpritesLayer.SPRITESMAP_UV_ATTRIB_LOCATION = gl.getAttribLocation(PPU_SpritesLayer.SHADER_PROGRAM_ID, 'aSpritesmapTextureCoord');
PPU_SpritesLayer.TILESHEET_TEXTURE_UNIFORM_LOCATION = gl.getUniformLocation(PPU_SpritesLayer.SHADER_PROGRAM_ID, 'uTilesheetSampler');
PPU_SpritesLayer.SPRITESMAP_TEXTURE_UNIFORM_LOCATION = gl.getUniformLocation(PPU_SpritesLayer.SHADER_PROGRAM_ID, 'uTilemapSampler');
PPU_SpritesLayer.PALETTE_TEXTURE_UNIFORM_LOCATION = gl.getUniformLocation(PPU_SpritesLayer.SHADER_PROGRAM_ID, 'uPaletteSampler');
// bool
PPU_SpritesLayer.INITIALIZED = true;
};
PPU_SpritesLayer.ShutDown = function ()
{
if (!PPU_SpritesLayer.CRASHED && PPU_SpritesLayer.INITIALIZED)
{
const gl = PPU_SpritesLayer.CONTEXT3D;
gl.detachShader(PPU_SpritesLayer.SHADER_PROGRAM_ID,PPU_SpritesLayer.VERTEX_SHADER_ID);
gl.detachShader(PPU_SpritesLayer.SHADER_PROGRAM_ID,PPU_SpritesLayer.FRAGMENT_SHADER_ID);
gl.deleteShader(PPU_SpritesLayer.VERTEX_SHADER_ID);
gl.deleteShader(PPU_SpritesLayer.FRAGMENT_SHADER_ID);
console.log("PPU_SpritesLayer shaders deleted");
}
// bool
PPU_SpritesLayer.INITIALIZED = false;
};
// -----------------------------------------------------------------------------
// INSTANCE PROPS
// -----------------------------------------------------------------------------
// instance index
PPU_SpritesLayer.prototype.m_instanceIndex = 0;
// num sprites
PPU_SpritesLayer.prototype.m_numSprites = 0;
// vertex arrays & buffers
PPU_SpritesLayer.prototype.m_sprites_YX = null; //new Float32Array( 8 * this.m_numSprites ); // updated every frame, from uv * matrix
PPU_SpritesLayer.prototype.m_sprites_tilesheet_UV = null; //new Float32Array( 8 * this.m_numSprites ); // updated at init. default 0.0-1.0
PPU_SpritesLayer.prototype.m_spritemap_UV = null; //new Float32Array( 8 * this.m_numSprites ); // theorically updated at init
PPU_SpritesLayer.prototype.m_sprites_indexes = null; //new Uint16Array( 6 * this.m_numSprites );
PPU_SpritesLayer.prototype.m_sprites_XY_buffer = null;
PPU_SpritesLayer.prototype.m_sprites_tileSheet_UV_buffer = null;
PPU_SpritesLayer.prototype.m_spritemap_UV_buffer = null; // all at 0.5 centered on pixels
PPU_SpritesLayer.prototype.m_sprites_indexes_buffer = null;
// arrays & textures for spritemap, palette and scanlines
PPU_SpritesLayer.prototype.m_palette_array = null ; //new Uint8Array( 4 * 16 * 16 );
PPU_SpritesLayer.prototype.m_spritemap_array = null ; //new Uint8Array( 4 * this.m_numSprites );
PPU_SpritesLayer.prototype.m_tileSheet_texture = null; // managed elsewhere
PPU_SpritesLayer.prototype.m_palette_texture = null;
PPU_SpritesLayer.prototype.m_spritemap_texture = null;
// -----------------------------------------------------------------------------
// INSTANCE METHODS
// -----------------------------------------------------------------------------
// build-delete
PPU_SpritesLayer.prototype.build = function( tilesheetTexture, numSprites )
{
// do nothing if not init
if (!PPU_SpritesLayer.INITIALIZED)
{
return null;
}
// instance index
this.m_instanceIndex = PPU_SpritesLayer.NUM_INSTANCES;
PPU_SpritesLayer.NUM_INSTANCES++;
// cache context
const gl = PPU_SpritesLayer.CONTEXT3D;
// args
this.m_numSprites = numSprites;
// alloc CPU arrays and GPU vertex buffers ---------------------------------
// dimension sprites vertex arrays
this.m_sprites_YX = new Float32Array( 8 * this.m_numSprites ); // updated every frame, from uv * matrix
this.m_sprites_tilesheet_UV = new Float32Array( 8 * this.m_numSprites ); // updated at init. default 0.0-1.0
this.m_spritemap_UV = new Float32Array( 8 * this.m_numSprites ); // theorically updated at init, col-row offsets are in spritemap texture
this.m_sprites_indexes = new Uint16Array( 6 * this.m_numSprites );
// cache arrays
var tshUV = this.m_sprites_tilesheet_UV;
var mapUV = this.m_spritemap_UV;
var indxs = this.m_sprites_indexes;
// compute initial values
for ( var iSp = 0; iSp < this.m_numSprites; iSp++ )
{
// uv coords in tileSheet
var it = iSp*8;
// 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0
tshUV[ it + 0 ] = 0.0/16.0 ; tshUV[ it + 1 ] = 1.0/16.0 ;
tshUV[ it + 2 ] = 1.0/16.0 ; tshUV[ it + 3 ] = 1.0/16.0 ;
tshUV[ it + 4 ] = 1.0/16.0 ; tshUV[ it + 5 ] = 0.0/16.0 ;
tshUV[ it + 6 ] = 0.0/16.0 ; tshUV[ it + 7 ] = 0.0/16.0 ;
// uv coords in spriteemap 1-dimension
var im = iSp * 8;
for ( var i=0; i<4; i++ )
{
mapUV[ im + i*2 + 0 ] = ( iSp + 0.5 ) / this.m_numSprites ;
mapUV[ im + i*2 + 1 ] = 0.5 ;
}
// indexes
var ii = iSp * 6;
indxs[ ii + 0 ] = 0 + 4*iSp;
indxs[ ii + 1 ] = 1 + 4*iSp;
indxs[ ii + 2 ] = 2 + 4*iSp;
indxs[ ii + 3 ] = 0 + 4*iSp;
indxs[ ii + 4 ] = 2 + 4*iSp;
indxs[ ii + 5 ] = 3 + 4*iSp;
}
// sprites GPU verts buffers
this.m_sprites_XY_buffer = gl.createBuffer(); // updated at runtime only
this.m_sprites_tileSheet_UV_buffer = gl.createBuffer(); // defaut 0.0 - 1.0
gl.bindBuffer(gl.ARRAY_BUFFER, this.m_sprites_tileSheet_UV_buffer );
gl.bufferData(gl.ARRAY_BUFFER, this.m_sprites_tilesheet_UV, gl.STATIC_DRAW); // will be updated at init later
// for these two ones i could discard array member, we'll see
this.m_spritemap_UV_buffer = gl.createBuffer(); // .5 everywhere, never updated
gl.bindBuffer(gl.ARRAY_BUFFER, this.m_spritemap_UV_buffer );
gl.bufferData(gl.ARRAY_BUFFER, this.m_spritemap_UV, gl.STATIC_DRAW);
this.m_sprites_indexes_buffer = gl.createBuffer(); // never updated
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.m_sprites_indexes_buffer );
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.m_sprites_indexes, gl.STATIC_DRAW); // static buffer, won't be streamed
console.log(" PPU_SpritesLayer instance "+this.m_instanceIndex+" vertices buffers created");
// alloc CPU arrays and GPU textures ---------------------------------------
this.m_tileSheet_texture = tilesheetTexture; // managed elsewhere
this.m_palette_array = new Uint8Array( 4 * 16 * 16 );
this.m_palette_texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.m_palette_texture);
// oldschool rendering
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
// initialize data (may be removed)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 16 , 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, this.m_palette_array);
this.m_spritemap_array = new Uint8Array( 4 * this.m_numSprites );
this.m_spritemap_texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.m_spritemap_texture);
// oldschool rendering
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
// initialize data (may be removed)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.m_numSprites, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, this.m_spritemap_array);
console.log(" PPU_SpritesLayer instance "+this.m_instanceIndex+" textures created");
};
PPU_SpritesLayer.prototype.delete = function()
{
// do nothing if not init
if (!PPU_SpritesLayer.INITIALIZED)
{
console.log('TilesheetTester not initialized');
return null;
}
// cache context
const gl = PPU_SpritesLayer.CONTEXT3D;
// delete arrays and textures ----------------------------------------------
gl.deleteTexture(this.m_palette_texture);
this.m_palette_array = null;
gl.deleteTexture(this.m_spritemap_texture);
this.m_spritemap_array = null;
console.log(" PPU_SpritesLayer instance "+this.m_instanceIndex+" textures deleted");
// delete verts buffers ----------------------------------------------------
gl.deleteBuffer(this.m_sprites_XY_buffer);
this.m_sprites_YX = null;
gl.deleteBuffer(this.m_sprites_tileSheet_UV_buffer);
this.m_sprites_tilesheet_UV = null;
gl.deleteBuffer(this.m_spritemap_UV_buffer);
this.m_spritemap_UV = null;
gl.deleteBuffer(this.m_sprites_indexes_buffer);
this.m_sprites_indexes = null;
console.log(" PPU_SpritesLayer instance "+this.m_instanceIndex+" vertices buffers deleted");
};
// update sprites and palette
PPU_SpritesLayer.prototype.setColor = function ( y, x, r, g, b, a)
{
// voir si ce bordel là est optimisable avec des array 32 (là y'a dla doc webgl à fouiller)
const addr = ( (x&15) | ((y&15)<<4) ) * 4;
let pa = this.m_palette_array;
pa[ addr + 0 ] = r ;
pa[ addr + 1 ] = g ;
pa[ addr + 2 ] = b ;
pa[ addr + 3 ] = a ;
};
PPU_SpritesLayer.prototype.setSprite = function ( index, visible, x, y, indexAscii, paletteY, flipX )
{
x = Math.floor(x); // round coords
y = Math.floor(y);
let xys = this.m_sprites_YX;
const uvs = this.m_sprites_tilesheet_UV;
const iF = index * 8; // index first double
// hide unactive sprite and return
if ( ! visible )
{
for ( let i=0; i<8; i++ )
{
xys[ iF + i ] = -1024 ; // x/y outField
}
return;
}
// compute the pseudo-matrix
const imgW = 128.0, imgH = 128.0; // x y coordinates based on sheet dimensions
const matw = imgW / PPU_SpritesLayer.SCREEN_WIDTH * 2 ;
const math = -imgH / PPU_SpritesLayer.SCREEN_HEIGHT * 2 ;
const matx = x * matw / imgW;
const maty = y * math / imgH;
// tileSheet UV coords are converted to XY coords with "matrix"
for ( let i=0; i<4; i++ )
{
const ix = iF + i*2;
xys[ ix+0 ] = uvs[ ix+0 ] * matw + matx ; // x
xys[ ix+1 ] = uvs[ ix+1 ] * math + maty ; // y
}
// update pixel data in cellList
let colInSheet = indexAscii & 15 ;
if ( flipX )
{
colInSheet = 127 - colInSheet;
}
let rowInSheet = Math.floor( (indexAscii & 255) / 16 );
let colOffset = colInSheet & 15;
let rowOffset = rowInSheet & 15;
let map = this.m_spritemap_array;
const c = index % this.m_numSprites;
map[ 4*c + 0 ] = colOffset << 4 ; // 255->1.0, so 16*16
map[ 4*c + 1 ] = rowOffset << 4 ;
map[ 4*c + 2 ] = paletteY << 4 ;
map[ 4*c + 3 ] = flipX * 255 ; // glsl reads 0.0 / 1.0
};
// drawcall render
PPU_SpritesLayer.prototype.drawCall = function()
{
// cache context
const gl = PPU_SpritesLayer.CONTEXT3D;
// CPU->GPU STREAM PHASE ----------------------------------------------------
// update textures
gl.bindTexture(gl.TEXTURE_2D, this.m_spritemap_texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.m_numSprites, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, this.m_spritemap_array);
gl.bindTexture(gl.TEXTURE_2D, this.m_palette_texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, this.m_palette_array);
// update vertices
gl.bindBuffer(gl.ARRAY_BUFFER, this.m_sprites_XY_buffer);
gl.bufferData(gl.ARRAY_BUFFER, this.m_sprites_YX, gl.STATIC_DRAW);
// DRAW PHASE --------------------------------------------------------------
// bind vertex buffers
gl.bindBuffer(gl.ARRAY_BUFFER, this.m_sprites_XY_buffer);
gl.vertexAttribPointer(PPU_SpritesLayer.XY_ATTRIB_LOCATION, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(PPU_SpritesLayer.XY_ATTRIB_LOCATION);
gl.bindBuffer(gl.ARRAY_BUFFER, this.m_sprites_tileSheet_UV_buffer);
gl.vertexAttribPointer(PPU_SpritesLayer.TILESHEET_UV_ATTRIB_LOCATION, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(PPU_SpritesLayer.TILESHEET_UV_ATTRIB_LOCATION);
gl.bindBuffer(gl.ARRAY_BUFFER, this.m_spritemap_UV_buffer);
gl.vertexAttribPointer(PPU_SpritesLayer.SPRITESMAP_UV_ATTRIB_LOCATION, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(PPU_SpritesLayer.SPRITESMAP_UV_ATTRIB_LOCATION);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.m_sprites_indexes_buffer);
// select shader program (revoir si c'est dans le bon ordre)
gl.useProgram(PPU_SpritesLayer.SHADER_PROGRAM_ID);
// bind textures
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this.m_tileSheet_texture);
gl.uniform1i(PPU_SpritesLayer.TILESHEET_TEXTURE_UNIFORM_LOCATION, 0);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, this.m_spritemap_texture);
gl.uniform1i(PPU_SpritesLayer.SPRITESMAP_TEXTURE_UNIFORM_LOCATION, 1);
gl.activeTexture(gl.TEXTURE2);
gl.bindTexture(gl.TEXTURE_2D, this.m_palette_texture);
gl.uniform1i(PPU_SpritesLayer.PALETTE_TEXTURE_UNIFORM_LOCATION, 2);
// draw triangles
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.drawElements(gl.TRIANGLES, 6 * this.m_numSprites, gl.UNSIGNED_SHORT, 0);
gl.disable(gl.BLEND);
};
// -----------------------------------------------------------------------------
// CLASS DECLARATION
// -----------------------------------------------------------------------------
function PPU_SpritesLayer( tilesheetTexture, numSprites )
{
this.build( tilesheetTexture, numSprites );
return this;
}
// -----------------------------------------------------------------------------
// STATIC PROPS
// -----------------------------------------------------------------------------
// context
PPU_SpritesLayer.CONTEXT3D = null;
PPU_SpritesLayer.CRASHED = false;
PPU_SpritesLayer.SCREEN_WIDTH = 1;
PPU_SpritesLayer.SCREEN_HEIGHT = 1;
// static initialized
PPU_SpritesLayer.INITIALIZED = false;
// instance index
PPU_SpritesLayer.NUM_INSTANCES = 0;
// tilesheet dimensions
// for the moment we use fixed dimensions, GBA format.
// multi-resolution will be a later work and should impact only tilesheets
PPU_SpritesLayer.TILESHEET_WIDTH = 128, PPU_SpritesLayer.TILESHEET_HEIGHT = 128;
// vertex shader (for the moment, the same as tilemapLayer, but will be modified)
PPU_SpritesLayer.VERTEX_SHADER_SOURCE = `
attribute vec4 aVertexPosition;
attribute vec2 aTilesheetTextureCoord;
attribute vec2 aSpritesmapTextureCoord;
varying highp vec2 vTilesheetTextureCoord;
varying highp vec2 vSpritesmapTextureCoord;
void main(void) {
gl_Position = aVertexPosition;
vTilesheetTextureCoord = aTilesheetTextureCoord;
vSpritesmapTextureCoord = aSpritesmapTextureCoord;
}
`;
PPU_SpritesLayer.VERTEX_SHADER_ID = 0;
// fragment shader
PPU_SpritesLayer.FRAGMENT_SHADER_SOURCE = `
varying highp vec2 vTilesheetTextureCoord;
varying highp vec2 vSpritesmapTextureCoord;
uniform sampler2D uTilesheetSampler;
uniform sampler2D uTilemapSampler;
uniform sampler2D uPaletteSampler;
const highp float c05 = 0.5;
const highp float c255 = 255.0 ;
const highp float c256 = 256.0 ;
highp vec4 tile;
highp float sheetTexCoordX;
highp float sheetTexCoordY;
highp vec4 colorIndex;
void main(void) {
// get cell info in 64*32 tilemap
tile = texture2D( uTilemapSampler, vSpritesmapTextureCoord );
// get the color index in 16*16 tileSheet
sheetTexCoordX = vTilesheetTextureCoord.x + (tile.r*c255+c05)/c256 ;
sheetTexCoordY = vTilesheetTextureCoord.y + (tile.g*c255+c05)/c256 ;
colorIndex = texture2D( uTilesheetSampler, vec2( ( -2.0 * tile.a + 1.0 )* sheetTexCoordX , sheetTexCoordY ) );
// get the color in the 256x256 palette
gl_FragColor = texture2D( uPaletteSampler, vec2( (colorIndex.a*c255+c05)/c256, (tile.b*c255+c05)/c256 ) );
}
`;
PPU_SpritesLayer.FRAGMENT_SHADER_ID = 0;
// shader program
PPU_SpritesLayer.SHADER_PROGRAM_ID = 0;
// shader locations
PPU_SpritesLayer.XY_ATTRIB_LOCATION = 0;
PPU_SpritesLayer.TILESHEET_UV_ATTRIB_LOCATION = 0;
PPU_SpritesLayer.SPRITESMAP_UV_ATTRIB_LOCATION = 0;
PPU_SpritesLayer.TILESHEET_TEXTURE_UNIFORM_LOCATION = 0;
PPU_SpritesLayer.SPRITESMAP_TEXTURE_UNIFORM_LOCATION = 0;
PPU_SpritesLayer.PALETTE_TEXTURE_UNIFORM_LOCATION = 0;
// -----------------------------------------------------------------------------
// STATIC METHODS
// -----------------------------------------------------------------------------
PPU_SpritesLayer.Init = function (gl, screenWidth, screenHeight)
{
PPU_SpritesLayer.CONTEXT3D = gl;
PPU_SpritesLayer.SCREEN_WIDTH = screenWidth; // avoid spaghetti, use modular classes
PPU_SpritesLayer.SCREEN_HEIGHT = screenHeight;
console.log('PPU_SpritesLayer static shader compilation -----------------');
// compile vertex shader
PPU_SpritesLayer.VERTEX_SHADER_ID = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(PPU_SpritesLayer.VERTEX_SHADER_ID, PPU_SpritesLayer.VERTEX_SHADER_SOURCE);
gl.compileShader(PPU_SpritesLayer.VERTEX_SHADER_ID);
// compile error
if (!gl.getShaderParameter(PPU_SpritesLayer.VERTEX_SHADER_ID, gl.COMPILE_STATUS)) {
alert('PPU_SpritesLayer compile vertex shader error: ' + gl.getShaderInfoLog(PPU_SpritesLayer.VERTEX_SHADER_ID));
gl.deleteShader(PPU_SpritesLayer.VERTEX_SHADER_ID); // AJOUTER CA DANS LE CPP !!!
PPU_SpritesLayer.CRASHED = true;
return false;
} else
{
console.log("PPU_SpritesLayer vertex shader compiled.");
}
// compile fragment shader
PPU_SpritesLayer.FRAGMENT_SHADER_ID = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(PPU_SpritesLayer.FRAGMENT_SHADER_ID, PPU_SpritesLayer.FRAGMENT_SHADER_SOURCE);
gl.compileShader(PPU_SpritesLayer.FRAGMENT_SHADER_ID);
// compile error
if (!gl.getShaderParameter(PPU_SpritesLayer.FRAGMENT_SHADER_ID, gl.COMPILE_STATUS)) {
alert('PPU_SpritesLayer compile fragment shader error: ' + gl.getShaderInfoLog(PPU_SpritesLayer.FRAGMENT_SHADER_ID));
gl.deleteShader(PPU_SpritesLayer.FRAGMENT_SHADER_ID); // AJOUTER CA DANS LE CPP !!!
PPU_SpritesLayer.CRASHED = true;
return false;
} else
{
console.log("PPU_SpritesLayer fragment shader compiled");
}
// link the program shader
PPU_SpritesLayer.SHADER_PROGRAM_ID = gl.createProgram();
gl.attachShader(PPU_SpritesLayer.SHADER_PROGRAM_ID, PPU_SpritesLayer.VERTEX_SHADER_ID);
gl.attachShader(PPU_SpritesLayer.SHADER_PROGRAM_ID, PPU_SpritesLayer.FRAGMENT_SHADER_ID);
gl.linkProgram(PPU_SpritesLayer.SHADER_PROGRAM_ID);
// compile error
if (!gl.getProgramParameter(PPU_SpritesLayer.SHADER_PROGRAM_ID, gl.LINK_STATUS)) {
alert('PPU_SpritesLayer link shaders program error: ' + gl.getProgramInfoLog(PPU_SpritesLayer.SHADER_PROGRAM_ID));
gl.deleteShader(PPU_SpritesLayer.VERTEX_SHADER_ID); // AJOUTER CA DANS LE CPP !!!
gl.deleteShader(PPU_SpritesLayer.FRAGMENT_SHADER_ID); // AJOUTER CA DANS LE CPP !!!
PPU_SpritesLayer.CRASHED = true;
return false;
} else
{
console.log("PPU_SpritesLayer shader programs linked");
}
// shader locations
PPU_SpritesLayer.XY_ATTRIB_LOCATION = gl.getAttribLocation(PPU_SpritesLayer.SHADER_PROGRAM_ID, 'aVertexPosition');
PPU_SpritesLayer.TILESHEET_UV_ATTRIB_LOCATION = gl.getAttribLocation(PPU_SpritesLayer.SHADER_PROGRAM_ID, 'aTilesheetTextureCoord');
PPU_SpritesLayer.SPRITESMAP_UV_ATTRIB_LOCATION = gl.getAttribLocation(PPU_SpritesLayer.SHADER_PROGRAM_ID, 'aSpritesmapTextureCoord');
PPU_SpritesLayer.TILESHEET_TEXTURE_UNIFORM_LOCATION = gl.getUniformLocation(PPU_SpritesLayer.SHADER_PROGRAM_ID, 'uTilesheetSampler');
PPU_SpritesLayer.SPRITESMAP_TEXTURE_UNIFORM_LOCATION = gl.getUniformLocation(PPU_SpritesLayer.SHADER_PROGRAM_ID, 'uTilemapSampler');
PPU_SpritesLayer.PALETTE_TEXTURE_UNIFORM_LOCATION = gl.getUniformLocation(PPU_SpritesLayer.SHADER_PROGRAM_ID, 'uPaletteSampler');
// bool
PPU_SpritesLayer.INITIALIZED = true;
};
PPU_SpritesLayer.ShutDown = function ()
{
if (!PPU_SpritesLayer.CRASHED && PPU_SpritesLayer.INITIALIZED)
{
const gl = PPU_SpritesLayer.CONTEXT3D;
gl.detachShader(PPU_SpritesLayer.SHADER_PROGRAM_ID,PPU_SpritesLayer.VERTEX_SHADER_ID);
gl.detachShader(PPU_SpritesLayer.SHADER_PROGRAM_ID,PPU_SpritesLayer.FRAGMENT_SHADER_ID);
gl.deleteShader(PPU_SpritesLayer.VERTEX_SHADER_ID);
gl.deleteShader(PPU_SpritesLayer.FRAGMENT_SHADER_ID);
console.log("PPU_SpritesLayer shaders deleted");
}
// bool
PPU_SpritesLayer.INITIALIZED = false;
};
// -----------------------------------------------------------------------------
// INSTANCE PROPS
// -----------------------------------------------------------------------------
// instance index
PPU_SpritesLayer.prototype.m_instanceIndex = 0;
// num sprites
PPU_SpritesLayer.prototype.m_numSprites = 0;
// vertex arrays & buffers
PPU_SpritesLayer.prototype.m_sprites_YX = null; //new Float32Array( 8 * this.m_numSprites ); // updated every frame, from uv * matrix
PPU_SpritesLayer.prototype.m_sprites_tilesheet_UV = null; //new Float32Array( 8 * this.m_numSprites ); // updated at init. default 0.0-1.0
PPU_SpritesLayer.prototype.m_spritemap_UV = null; //new Float32Array( 8 * this.m_numSprites ); // theorically updated at init
PPU_SpritesLayer.prototype.m_sprites_indexes = null; //new Uint16Array( 6 * this.m_numSprites );
PPU_SpritesLayer.prototype.m_sprites_XY_buffer = null;
PPU_SpritesLayer.prototype.m_sprites_tileSheet_UV_buffer = null;
PPU_SpritesLayer.prototype.m_spritemap_UV_buffer = null; // all at 0.5 centered on pixels
PPU_SpritesLayer.prototype.m_sprites_indexes_buffer = null;
// arrays & textures for spritemap, palette and scanlines
PPU_SpritesLayer.prototype.m_palette_array = null ; //new Uint8Array( 4 * 16 * 16 );
PPU_SpritesLayer.prototype.m_spritemap_array = null ; //new Uint8Array( 4 * this.m_numSprites );
PPU_SpritesLayer.prototype.m_tileSheet_texture = null; // managed elsewhere
PPU_SpritesLayer.prototype.m_palette_texture = null;
PPU_SpritesLayer.prototype.m_spritemap_texture = null;
// -----------------------------------------------------------------------------
// INSTANCE METHODS
// -----------------------------------------------------------------------------
// build-delete
PPU_SpritesLayer.prototype.build = function( tilesheetTexture, numSprites )
{
// do nothing if not init
if (!PPU_SpritesLayer.INITIALIZED)
{
return null;
}
// instance index
this.m_instanceIndex = PPU_SpritesLayer.NUM_INSTANCES;
PPU_SpritesLayer.NUM_INSTANCES++;
// cache context
const gl = PPU_SpritesLayer.CONTEXT3D;
// args
this.m_numSprites = numSprites;
// alloc CPU arrays and GPU vertex buffers ---------------------------------
// dimension sprites vertex arrays
this.m_sprites_YX = new Float32Array( 8 * this.m_numSprites ); // updated every frame, from uv * matrix
this.m_sprites_tilesheet_UV = new Float32Array( 8 * this.m_numSprites ); // updated at init. default 0.0-1.0
this.m_spritemap_UV = new Float32Array( 8 * this.m_numSprites ); // theorically updated at init, col-row offsets are in spritemap texture
this.m_sprites_indexes = new Uint16Array( 6 * this.m_numSprites );
// cache arrays
var tshUV = this.m_sprites_tilesheet_UV;
var mapUV = this.m_spritemap_UV;
var indxs = this.m_sprites_indexes;
// compute initial values
for ( var iSp = 0; iSp < this.m_numSprites; iSp++ )
{
// uv coords in tileSheet
var it = iSp*8;
// 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0
tshUV[ it + 0 ] = 0.0/16.0 ; tshUV[ it + 1 ] = 1.0/16.0 ;
tshUV[ it + 2 ] = 1.0/16.0 ; tshUV[ it + 3 ] = 1.0/16.0 ;
tshUV[ it + 4 ] = 1.0/16.0 ; tshUV[ it + 5 ] = 0.0/16.0 ;
tshUV[ it + 6 ] = 0.0/16.0 ; tshUV[ it + 7 ] = 0.0/16.0 ;
// uv coords in spriteemap 1-dimension
var im = iSp * 8;
for ( var i=0; i<4; i++ )
{
mapUV[ im + i*2 + 0 ] = ( iSp + 0.5 ) / this.m_numSprites ;
mapUV[ im + i*2 + 1 ] = 0.5 ;
}
// indexes
var ii = iSp * 6;
indxs[ ii + 0 ] = 0 + 4*iSp;
indxs[ ii + 1 ] = 1 + 4*iSp;
indxs[ ii + 2 ] = 2 + 4*iSp;
indxs[ ii + 3 ] = 0 + 4*iSp;
indxs[ ii + 4 ] = 2 + 4*iSp;
indxs[ ii + 5 ] = 3 + 4*iSp;
}
// sprites GPU verts buffers
this.m_sprites_XY_buffer = gl.createBuffer(); // updated at runtime only
this.m_sprites_tileSheet_UV_buffer = gl.createBuffer(); // defaut 0.0 - 1.0
gl.bindBuffer(gl.ARRAY_BUFFER, this.m_sprites_tileSheet_UV_buffer );
gl.bufferData(gl.ARRAY_BUFFER, this.m_sprites_tilesheet_UV, gl.STATIC_DRAW); // will be updated at init later
// for these two ones i could discard array member, we'll see
this.m_spritemap_UV_buffer = gl.createBuffer(); // .5 everywhere, never updated
gl.bindBuffer(gl.ARRAY_BUFFER, this.m_spritemap_UV_buffer );
gl.bufferData(gl.ARRAY_BUFFER, this.m_spritemap_UV, gl.STATIC_DRAW);
this.m_sprites_indexes_buffer = gl.createBuffer(); // never updated
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.m_sprites_indexes_buffer );
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.m_sprites_indexes, gl.STATIC_DRAW); // static buffer, won't be streamed
console.log(" PPU_SpritesLayer instance "+this.m_instanceIndex+" vertices buffers created");
// alloc CPU arrays and GPU textures ---------------------------------------
this.m_tileSheet_texture = tilesheetTexture; // managed elsewhere
this.m_palette_array = new Uint8Array( 4 * 16 * 16 );
this.m_palette_texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.m_palette_texture);
// oldschool rendering
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
// initialize data (may be removed)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 16 , 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, this.m_palette_array);
this.m_spritemap_array = new Uint8Array( 4 * this.m_numSprites );
this.m_spritemap_texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.m_spritemap_texture);
// oldschool rendering
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
// initialize data (may be removed)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.m_numSprites, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, this.m_spritemap_array);
console.log(" PPU_SpritesLayer instance "+this.m_instanceIndex+" textures created");
};
PPU_SpritesLayer.prototype.delete = function()
{
// do nothing if not init
if (!PPU_SpritesLayer.INITIALIZED)
{
console.log('TilesheetTester not initialized');
return null;
}
// cache context
const gl = PPU_SpritesLayer.CONTEXT3D;
// delete arrays and textures ----------------------------------------------
gl.deleteTexture(this.m_palette_texture);
this.m_palette_array = null;
gl.deleteTexture(this.m_spritemap_texture);
this.m_spritemap_array = null;
console.log(" PPU_SpritesLayer instance "+this.m_instanceIndex+" textures deleted");
// delete verts buffers ----------------------------------------------------
gl.deleteBuffer(this.m_sprites_XY_buffer);
this.m_sprites_YX = null;
gl.deleteBuffer(this.m_sprites_tileSheet_UV_buffer);
this.m_sprites_tilesheet_UV = null;
gl.deleteBuffer(this.m_spritemap_UV_buffer);
this.m_spritemap_UV = null;
gl.deleteBuffer(this.m_sprites_indexes_buffer);
this.m_sprites_indexes = null;
console.log(" PPU_SpritesLayer instance "+this.m_instanceIndex+" vertices buffers deleted");
};
// update sprites and palette
PPU_SpritesLayer.prototype.setColor = function ( y, x, r, g, b, a)
{
// voir si ce bordel là est optimisable avec des array 32 (là y'a dla doc webgl à fouiller)
const addr = ( (x&15) | ((y&15)<<4) ) * 4;
let pa = this.m_palette_array;
pa[ addr + 0 ] = r ;
pa[ addr + 1 ] = g ;
pa[ addr + 2 ] = b ;
pa[ addr + 3 ] = a ;
};
PPU_SpritesLayer.prototype.setSprite = function ( index, visible, x, y, indexAscii, paletteY, flipX )
{
x = Math.floor(x); // round coords
y = Math.floor(y);
let xys = this.m_sprites_YX;
const uvs = this.m_sprites_tilesheet_UV;
const iF = index * 8; // index first double
// hide unactive sprite and return
if ( ! visible )
{
for ( let i=0; i<8; i++ )
{
xys[ iF + i ] = -1024 ; // x/y outField
}
return;
}
// compute the pseudo-matrix
const imgW = 128.0, imgH = 128.0; // x y coordinates based on sheet dimensions
const matw = imgW / PPU_SpritesLayer.SCREEN_WIDTH * 2 ;
const math = -imgH / PPU_SpritesLayer.SCREEN_HEIGHT * 2 ;
const matx = x * matw / imgW;
const maty = y * math / imgH;
// tileSheet UV coords are converted to XY coords with "matrix"
for ( let i=0; i<4; i++ )
{
const ix = iF + i*2;
xys[ ix+0 ] = uvs[ ix+0 ] * matw + matx ; // x
xys[ ix+1 ] = uvs[ ix+1 ] * math + maty ; // y
}
// update pixel data in cellList
let colInSheet = indexAscii & 15 ;
if ( flipX )
{
colInSheet = 127 - colInSheet;
}
let rowInSheet = Math.floor( (indexAscii & 255) / 16 );
let colOffset = colInSheet & 15;
let rowOffset = rowInSheet & 15;
let map = this.m_spritemap_array;
const c = index % this.m_numSprites;
map[ 4*c + 0 ] = colOffset << 4 ; // 255->1.0, so 16*16
map[ 4*c + 1 ] = rowOffset << 4 ;
map[ 4*c + 2 ] = paletteY << 4 ;
map[ 4*c + 3 ] = flipX * 255 ; // glsl reads 0.0 / 1.0
};
// drawcall render
PPU_SpritesLayer.prototype.drawCall = function()
{
// cache context
const gl = PPU_SpritesLayer.CONTEXT3D;
// CPU->GPU STREAM PHASE ----------------------------------------------------
// update textures
gl.bindTexture(gl.TEXTURE_2D, this.m_spritemap_texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.m_numSprites, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, this.m_spritemap_array);
gl.bindTexture(gl.TEXTURE_2D, this.m_palette_texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, this.m_palette_array);
// update vertices
gl.bindBuffer(gl.ARRAY_BUFFER, this.m_sprites_XY_buffer);
gl.bufferData(gl.ARRAY_BUFFER, this.m_sprites_YX, gl.STATIC_DRAW);
// DRAW PHASE --------------------------------------------------------------
// bind vertex buffers
gl.bindBuffer(gl.ARRAY_BUFFER, this.m_sprites_XY_buffer);
gl.vertexAttribPointer(PPU_SpritesLayer.XY_ATTRIB_LOCATION, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(PPU_SpritesLayer.XY_ATTRIB_LOCATION);
gl.bindBuffer(gl.ARRAY_BUFFER, this.m_sprites_tileSheet_UV_buffer);
gl.vertexAttribPointer(PPU_SpritesLayer.TILESHEET_UV_ATTRIB_LOCATION, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(PPU_SpritesLayer.TILESHEET_UV_ATTRIB_LOCATION);
gl.bindBuffer(gl.ARRAY_BUFFER, this.m_spritemap_UV_buffer);
gl.vertexAttribPointer(PPU_SpritesLayer.SPRITESMAP_UV_ATTRIB_LOCATION, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(PPU_SpritesLayer.SPRITESMAP_UV_ATTRIB_LOCATION);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.m_sprites_indexes_buffer);
// select shader program (revoir si c'est dans le bon ordre)
gl.useProgram(PPU_SpritesLayer.SHADER_PROGRAM_ID);
// bind textures
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this.m_tileSheet_texture);
gl.uniform1i(PPU_SpritesLayer.TILESHEET_TEXTURE_UNIFORM_LOCATION, 0);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, this.m_spritemap_texture);
gl.uniform1i(PPU_SpritesLayer.SPRITESMAP_TEXTURE_UNIFORM_LOCATION, 1);
gl.activeTexture(gl.TEXTURE2);
gl.bindTexture(gl.TEXTURE_2D, this.m_palette_texture);
gl.uniform1i(PPU_SpritesLayer.PALETTE_TEXTURE_UNIFORM_LOCATION, 2);
// draw triangles
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.drawElements(gl.TRIANGLES, 6 * this.m_numSprites, gl.UNSIGNED_SHORT, 0);
gl.disable(gl.BLEND);
};