mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-19 00:06:42 +08:00
2381 lines
72 KiB
JavaScript
2381 lines
72 KiB
JavaScript
// Copyright (c) 2012 Turbulenz Limited
|
|
|
|
/*global
|
|
Draw2D: false
|
|
Float32Array: false
|
|
*/
|
|
|
|
//
|
|
// Draw2DGroup. Wraps vertex buffer data with pairings of indices and textures
|
|
// representing subsets of buffer relating to a set of equal-texture quads.
|
|
//
|
|
// [ sprite1 sprite2 sprite3 sprite4 sprite5 ]
|
|
// \---------------/ \------/ \--------------/
|
|
// texture 1 texture 2 texture 3
|
|
// 12 indices 6 indices 12 indices
|
|
//
|
|
function Draw2DGroup() {}
|
|
Draw2DGroup.create = function draw2DGroupFn()
|
|
{
|
|
var group = new Draw2DGroup();
|
|
|
|
// pairs of index counts + associated texture for subset of group.
|
|
group.indices = [];
|
|
group.textures = [];
|
|
group.numSets = 0;
|
|
|
|
// vertex buffer for group.
|
|
group.vertexBufferData = new Draw2D.prototype.floatArray(1024);
|
|
group.numVertices = 0;
|
|
|
|
return group;
|
|
};
|
|
|
|
function Draw2DSprite() {}
|
|
Draw2DSprite.prototype = {
|
|
|
|
version : 1,
|
|
|
|
//
|
|
// Assumption is that user will not be performing these actions frequently.
|
|
// To that end, we provide a function which performs the ssary side effects
|
|
// on call, to prevent an overhead for lazy evaluation.
|
|
//
|
|
getTextureRectangle : function getTextureRectangleFn(dst)
|
|
{
|
|
if (dst === undefined)
|
|
{
|
|
dst = new Draw2D.prototype.floatArray(4);
|
|
}
|
|
var data = this.data;
|
|
var texture = this._texture;
|
|
if (texture)
|
|
{
|
|
dst[0] = data[12] * texture.width;
|
|
dst[1] = data[13] * texture.height;
|
|
dst[2] = data[14] * texture.width;
|
|
dst[3] = data[15] * texture.height;
|
|
}
|
|
else
|
|
{
|
|
dst[0] = data[12];
|
|
dst[1] = data[13];
|
|
dst[2] = data[14];
|
|
dst[3] = data[15];
|
|
}
|
|
return dst;
|
|
},
|
|
setTextureRectangle : function setTextureRectangleFn(uvRect)
|
|
{
|
|
var data = this.data;
|
|
var texture = this._texture;
|
|
if (texture)
|
|
{
|
|
var iwidth = 1 / texture.width;
|
|
var iheight = 1 / texture.height;
|
|
data[12] = uvRect[0] * iwidth;
|
|
data[13] = uvRect[1] * iheight;
|
|
data[14] = uvRect[2] * iwidth;
|
|
data[15] = uvRect[3] * iheight;
|
|
}
|
|
else
|
|
{
|
|
data[12] = uvRect[0];
|
|
data[13] = uvRect[1];
|
|
data[14] = uvRect[2];
|
|
data[15] = uvRect[3];
|
|
}
|
|
},
|
|
|
|
getColor : function getColorFn(dst)
|
|
{
|
|
if (dst === undefined)
|
|
{
|
|
dst = new Draw2D.prototype.floatArray(4);
|
|
}
|
|
var data = this.data;
|
|
dst[0] = data[8];
|
|
dst[1] = data[9];
|
|
dst[2] = data[10];
|
|
dst[3] = data[11];
|
|
return dst;
|
|
},
|
|
setColor : function setColorFn(color)
|
|
{
|
|
var data = this.data;
|
|
data[8] = color[0];
|
|
data[9] = color[1];
|
|
data[10] = color[2];
|
|
data[11] = color[3];
|
|
},
|
|
|
|
getTexture : function getTextureFn()
|
|
{
|
|
return this._texture;
|
|
},
|
|
setTexture : function setTextureFn(texture)
|
|
{
|
|
if (this._texture !== texture)
|
|
{
|
|
var su = (this._texture ? this._texture.width : 1.0) / (texture ? texture.width : 1.0);
|
|
var sv = (this._texture ? this._texture.height : 1.0) / (texture ? texture.height : 1.0);
|
|
this._texture = texture || null;
|
|
|
|
// re-normalise texture coordinates.
|
|
var data = this.data;
|
|
data[12] *= su;
|
|
data[13] *= sv;
|
|
data[14] *= su;
|
|
data[15] *= sv;
|
|
}
|
|
},
|
|
|
|
getWidth : function getWidthFn()
|
|
{
|
|
return this.data[17] * 2;
|
|
},
|
|
setWidth : function setWidthFn(width)
|
|
{
|
|
width *= 0.5;
|
|
var data = this.data;
|
|
if (data[17] !== width)
|
|
{
|
|
data[17] = width;
|
|
this._invalidate();
|
|
}
|
|
},
|
|
|
|
getHeight : function getHeightFn()
|
|
{
|
|
return this.data[18] * 2;
|
|
},
|
|
setHeight : function setHeightFn(height)
|
|
{
|
|
height *= 0.5;
|
|
var data = this.data;
|
|
if (data[18] !== height)
|
|
{
|
|
data[18] = height;
|
|
this._invalidate();
|
|
}
|
|
},
|
|
|
|
getScale : function getScaleFn(dst)
|
|
{
|
|
if (dst === undefined)
|
|
{
|
|
dst = new Draw2D.prototype.floatArray(2);
|
|
}
|
|
var data = this.data;
|
|
dst[0] = data[19];
|
|
dst[1] = data[20];
|
|
return dst;
|
|
},
|
|
setScale : function setScaleFn(scale)
|
|
{
|
|
var scaleX = scale[0];
|
|
var scaleY = scale[1];
|
|
var data = this.data;
|
|
if (data[19] !== scaleX || data[20] !== scaleY)
|
|
{
|
|
data[19] = scaleX;
|
|
data[20] = scaleY;
|
|
this._invalidate();
|
|
}
|
|
},
|
|
|
|
getShear : function getShearFn(dst)
|
|
{
|
|
if (dst === undefined)
|
|
{
|
|
dst = new Draw2D.prototype.floatArray(2);
|
|
}
|
|
var data = this.data;
|
|
dst[0] = data[21];
|
|
dst[1] = data[22];
|
|
return dst;
|
|
},
|
|
setShear : function setShearFn(shear)
|
|
{
|
|
var shearX = shear[0];
|
|
var shearY = shear[1];
|
|
var data = this.data;
|
|
if (data[21] !== shearX || data[22] !== shearY)
|
|
{
|
|
data[21] = shearX;
|
|
data[22] = shearY;
|
|
this._invalidate();
|
|
}
|
|
},
|
|
|
|
getOrigin : function getOriginFn(dst)
|
|
{
|
|
if (dst === undefined)
|
|
{
|
|
dst = new Draw2D.prototype.floatArray(2);
|
|
}
|
|
var data = this.data;
|
|
dst[0] = data[23];
|
|
dst[1] = data[24];
|
|
return dst;
|
|
},
|
|
setOrigin : function setOriginFn(origin)
|
|
{
|
|
var originX = origin[0];
|
|
var originY = origin[1];
|
|
var data = this.data;
|
|
if (data[23] !== originX || data[24] !== originY)
|
|
{
|
|
data[23] = originX;
|
|
data[24] = originY;
|
|
this._invalidate();
|
|
}
|
|
},
|
|
|
|
// Method for internal use only.
|
|
//
|
|
// Recompute locally defined vectors.
|
|
_invalidate : function invalidateFn()
|
|
{
|
|
var data = this.data;
|
|
// [ T1 T2 ] = [ scaleX 0 ] [ 1 shearX ]
|
|
// [ T3 T4 ] [ 0 scaleY ] [ shearY 1 ]
|
|
var T1 = data[19];
|
|
var T2 = data[19] * data[21];
|
|
var T3 = data[20] * data[22];
|
|
var T4 = data[20];
|
|
|
|
// Recompute locally defined position of true center of sprite.
|
|
var x = data[17] - data[23]; // x = width/2 - originX
|
|
var y = data[18] - data[24]; // y = height/2 - originY
|
|
var cx = data[25] = (T1 * x + T2 * y); // (cx) = T (x)
|
|
var cy = data[26] = (T3 * x + T4 * y); // (cy) (y)
|
|
|
|
// Recompute locally defined position of top-left vertex relative to center of sprite.
|
|
x = -data[17]; // x = -width/2
|
|
y = -data[18]; // y = -height/2
|
|
var ux = data[27] = (T1 * x + T2 * y); // (ux) = T (x)
|
|
var uy = data[28] = (T3 * x + T4 * y); // (uy) (y)
|
|
|
|
// Recompute locally defined position of top-right vertex relative to center of sprite.
|
|
x = -x; // x = width / 2
|
|
var vx = data[29] = (T1 * x + T2 * y); // (vx) = T (x)
|
|
var vy = data[30] = (T3 * x + T4 * y); // (vy) (y)
|
|
|
|
// Rotate vectors to screen space so that in the case that rotation is not performed
|
|
// These vectors are still valid.
|
|
var rotation = data[16] = this.rotation;
|
|
var cos = Math.cos(rotation);
|
|
var sin = Math.sin(rotation);
|
|
|
|
data[31] = ((cos * cx) - (sin * cy));
|
|
data[32] = ((sin * cx) + (cos * cy));
|
|
data[33] = ((cos * ux) - (sin * uy));
|
|
data[34] = ((sin * ux) + (cos * uy));
|
|
data[35] = ((cos * vx) - (sin * vy));
|
|
data[36] = ((sin * vx) + (cos * vy));
|
|
|
|
// Compute suitable epsilon to consider rotations equals.
|
|
// We do this by finding the vertex furthest from defined center of rotation.
|
|
// And using its distance to compute what rotation constitutes a 'visible' rotation.
|
|
//
|
|
// Positions of vertices relative to origin are given by:
|
|
// v1 = c + u, v2 = c + v, v3 = c - v, v4 = c - u.
|
|
// |v1|^2 = |c|^2 + |u|^2 + 2c.u
|
|
// |v4|^2 = |c|^2 + |u|^2 - 2c.u
|
|
// |v2|^2 = |c|^2 + |v|^2 + 2c.v
|
|
// |v3|^2 = |c|^2 + |v|^2 - 2c.v
|
|
//
|
|
// Compute r1 = |u|^2 + abs(2c.u)
|
|
// Compute r2 = |v|^2 + abs(2c.v)
|
|
//
|
|
// Finally max(|vi|^2) = |c|^2 + max(r1, r2)
|
|
//
|
|
var dot = 2 * ((cx * ux) + (cy * uy));
|
|
if (dot < 0)
|
|
{
|
|
dot = -dot;
|
|
}
|
|
var r1 = (ux * ux) + (uy * uy) + dot;
|
|
|
|
dot = 2 * ((cx * vx) + (cy * vy));
|
|
if (dot < 0)
|
|
{
|
|
dot = -dot;
|
|
}
|
|
var r2 = (vx * vx) + (vy * vy) + dot;
|
|
|
|
if (r2 > r1)
|
|
{
|
|
r1 = r2;
|
|
}
|
|
|
|
r1 += ((cx * cx) + (cy * cy));
|
|
// r1 is the squared distance to furthest vertex.
|
|
//
|
|
// We permit a half pixel movement to be considered a 'true' movement.
|
|
// Squared rotation required to impart this movement on furthest vertex is
|
|
data[37] = (0.25 / r1); // squared epsilon
|
|
},
|
|
|
|
// Method for internal use only.
|
|
//
|
|
// Recompute draw2d coordinate space vertices and vectors.
|
|
_update : function _updateFn(angleScaleFactor)
|
|
{
|
|
var data = this.data;
|
|
var x, y, u, v;
|
|
|
|
// Check if rotation has been modified
|
|
x = this.rotation;
|
|
y = x - data[16]; // y = rotation - previousRotation
|
|
if ((y * y) > (data[37] * angleScaleFactor)) // if |y| > epsilon
|
|
{
|
|
data[16] = x; //previousRotation = rotation
|
|
u = Math.cos(x);
|
|
v = Math.sin(x);
|
|
|
|
// rotate locally defined vectors.
|
|
x = data[25];
|
|
y = data[26];
|
|
data[31] = (u * x - v * y); // (px) = [cos -sin] (cx)
|
|
data[32] = (v * x + u * y); // (py) = [sin cos] (cy)
|
|
|
|
x = data[27];
|
|
y = data[28];
|
|
data[33] = (u * x - v * y); // (x1) = [cos -sin] (ux)
|
|
data[34] = (v * x + u * y); // (y1) = [sin cos] (uy)
|
|
|
|
x = data[29];
|
|
y = data[30];
|
|
data[35] = (u * x - v * y); // (x2) = [cos -sin] (vx)
|
|
data[36] = (v * x + u * y); // (y2) = [sin cos] (vy)
|
|
}
|
|
|
|
// Compute center of this sprite in screen space.
|
|
u = this.x + data[31]; // u = centerX = positionX + px
|
|
v = this.y + data[32]; // v = centerY = positionY + py
|
|
|
|
// Compute vertex positions in screen space.
|
|
x = data[33];
|
|
y = data[34];
|
|
data[0] = u + x; // v1x = centerX + x1
|
|
data[1] = v + y; // v1y = centerY + y1
|
|
data[6] = u - x; // v4x = centerX - x1
|
|
data[7] = v - y; // v4y = centerY - y1
|
|
|
|
x = data[35];
|
|
y = data[36];
|
|
data[2] = u + x; // v2x = centerX + x2
|
|
data[3] = v + y; // v2y = centerY + y2
|
|
data[4] = u - x; // v3x = centerX - x2
|
|
data[5] = v - y; // v3y = centerY - y2
|
|
}
|
|
};
|
|
|
|
Draw2DSprite.create = function draw2DSpriteCreateFn(params)
|
|
{
|
|
if ((params.width === undefined || params.height === undefined) && !params.texture)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
// data:
|
|
// ---
|
|
// First 16 values reserved for Draw2DSpriteData.
|
|
// includes colour and texture coordinates.
|
|
//
|
|
// 16 : old_rotation (for lazy evaluation)
|
|
// 17,18 : width/2, height/2 (changed by user via function)
|
|
// 19,20 : scaleX, scaleY (changed by user via function)
|
|
// 21,22 : shearX, shearY (changed by user via function)
|
|
// 23,24 : originX, originY (changed by user via function)
|
|
// 25,26 : cx, cy // locally defined position of true center of sprite relative to origin
|
|
// (dependant on scale/shear/center/dimension)
|
|
// 27,28 : u1, v1 // locally defined position of top-left vertex relative to center of sprite.
|
|
// (dependant on scale/shear/dimension)
|
|
// 29,30 : u2, v2 // locally defined position of top-right vertex relative to center of sprite.
|
|
// (dependant on scale/shear/dimension)
|
|
// 31,32 : px, py // relative defined position of true center of sprite relative to origin
|
|
// (dependant on rotation and cx,cy)
|
|
// 33,34 : x1, y1 // relative defined position of top-left vertex relative to center of sprite.
|
|
// (dependant on rotation and u1,v1)
|
|
// 35,36 : x2, y2 // relative defined position of top-right vertex relative to center of sprite.
|
|
// (dependant on rotation and u2,v2)
|
|
// 37 : Squared epsilon to consider rotations equal based on dimensions.
|
|
var s = new Draw2DSprite();
|
|
var data = s.data = new Draw2D.prototype.floatArray(38);
|
|
|
|
// texture (not optional)
|
|
var texture = s._texture = params.texture || null;
|
|
|
|
// position (optional, default 0,0)
|
|
s.x = (params.x || 0.0);
|
|
s.y = (params.y || 0.0);
|
|
|
|
// rotation (optional, default 0)
|
|
s.rotation = data[16] = (params.rotation || 0.0);
|
|
|
|
// colour (optional, default [1,1,1,1])
|
|
var color = params.color;
|
|
data[8] = (color ? color[0] : 1.0);
|
|
data[9] = (color ? color[1] : 1.0);
|
|
data[10] = (color ? color[2] : 1.0);
|
|
data[11] = (color ? color[3] : 1.0);
|
|
|
|
// uvRect (optional, default texture rectangle)
|
|
var uvRect = params.textureRectangle;
|
|
var iwidth = (texture ? 1 / texture.width : 1);
|
|
var iheight = (texture ? 1 / texture.height : 1);
|
|
data[12] = (uvRect ? (uvRect[0] * iwidth) : 0.0);
|
|
data[13] = (uvRect ? (uvRect[1] * iheight) : 0.0);
|
|
data[14] = (uvRect ? (uvRect[2] * iwidth) : 1.0);
|
|
data[15] = (uvRect ? (uvRect[3] * iheight) : 1.0);
|
|
|
|
// dimensions / 2 (default texture dimensions)
|
|
data[17] = ((params.width !== undefined) ? params.width : texture.width) * 0.5;
|
|
data[18] = ((params.height !== undefined) ? params.height : texture.height) * 0.5;
|
|
|
|
// scale (default [1,1])
|
|
var scale = params.scale;
|
|
data[19] = (scale ? scale[0] : 1.0);
|
|
data[20] = (scale ? scale[1] : 1.0);
|
|
|
|
// shear (default [0,0])
|
|
var shear = params.shear;
|
|
data[21] = (shear ? shear[0] : 0.0);
|
|
data[22] = (shear ? shear[1] : 0.0);
|
|
|
|
// origin (default dimensions / 2)
|
|
var origin = params.origin;
|
|
data[23] = (origin ? origin[0] : data[17]);
|
|
data[24] = (origin ? origin[1] : data[18]);
|
|
|
|
s._invalidate();
|
|
return s;
|
|
};
|
|
|
|
//
|
|
// Used in rectangle draw routines to compute data to be pushed into vertex buffers.
|
|
//
|
|
function Draw2DSpriteData() {}
|
|
Draw2DSpriteData.setFromRotatedRectangle = function setFromRotatedRectangleFn(sprite, texture, rect, uvrect, color, rotation, origin)
|
|
{
|
|
var x1 = rect[0];
|
|
var y1 = rect[1];
|
|
var x2 = rect[2];
|
|
var y2 = rect[3];
|
|
|
|
if (!rotation)
|
|
{
|
|
sprite[0] = x1;
|
|
sprite[1] = y1;
|
|
sprite[2] = x2;
|
|
sprite[3] = y1;
|
|
sprite[4] = x1;
|
|
sprite[5] = y2;
|
|
sprite[6] = x2;
|
|
sprite[7] = y2;
|
|
}
|
|
else
|
|
{
|
|
var cx, cy;
|
|
if (origin)
|
|
{
|
|
cx = x1 + origin[0];
|
|
cy = y1 + origin[1];
|
|
}
|
|
else
|
|
{
|
|
cx = 0.5 * (x1 + x2);
|
|
cy = 0.5 * (y1 + y2);
|
|
}
|
|
|
|
var dx = x1 - cx;
|
|
var dy = y1 - cy;
|
|
|
|
var cos = Math.cos(rotation);
|
|
var sin = Math.sin(rotation);
|
|
var w = (x2 - x1);
|
|
var h = (y2 - y1);
|
|
|
|
sprite[0] = x1 = cx + (cos * dx - sin * dy);
|
|
sprite[1] = y1 = cy + (sin * dx + cos * dy);
|
|
sprite[2] = x1 + (cos * w);
|
|
sprite[3] = y1 + (sin * w);
|
|
sprite[4] = x1 - (sin * h);
|
|
sprite[5] = y1 + (cos * h);
|
|
sprite[6] = x1 + (cos * w - sin * h);
|
|
sprite[7] = y1 + (sin * w + cos * h);
|
|
}
|
|
|
|
if (color)
|
|
{
|
|
sprite[8] = color[0];
|
|
sprite[9] = color[1];
|
|
sprite[10] = color[2];
|
|
sprite[11] = color[3];
|
|
}
|
|
else
|
|
{
|
|
sprite[8] = sprite[9] = sprite[10] = sprite[11] = 1.0;
|
|
}
|
|
|
|
if (uvrect && texture)
|
|
{
|
|
var iwidth = 1 / texture.width;
|
|
var iheight = 1 / texture.height;
|
|
sprite[12] = uvrect[0] * iwidth;
|
|
sprite[13] = uvrect[1] * iheight;
|
|
sprite[14] = uvrect[2] * iwidth;
|
|
sprite[15] = uvrect[3] * iheight;
|
|
}
|
|
else
|
|
{
|
|
sprite[12] = sprite[13] = 0;
|
|
sprite[14] = sprite[15] = 1;
|
|
}
|
|
};
|
|
|
|
Draw2DSpriteData.create = function draw2DSpriteFn()
|
|
{
|
|
// x1 y1 x2 y2 x3 y3 x4 y4 - vertices [0,8)
|
|
// cr cg cb ca u1 v1 u2 v2 - normalized color + texture [8,16)
|
|
return new Draw2D.prototype.floatArray(16);
|
|
};
|
|
|
|
function Draw2D() {}
|
|
|
|
Draw2D.prototype = {
|
|
|
|
version : 7,
|
|
|
|
forceUpdate : false,
|
|
clearBackBuffer : false,
|
|
|
|
// supported sort modes.
|
|
sort : {
|
|
deferred : 'deferred',
|
|
immediate : 'immediate',
|
|
texture : 'texture'
|
|
},
|
|
|
|
// supported scale modes.
|
|
scale : {
|
|
scale : 'scale',
|
|
none : 'none'
|
|
},
|
|
|
|
drawStates: {
|
|
uninit: 0,
|
|
ready : 1,
|
|
draw : 2
|
|
},
|
|
|
|
defaultClearColor: [0, 0, 0, 1],
|
|
|
|
clear: function clearFn(clearColor)
|
|
{
|
|
if (this.state !== this.drawStates.ready)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var gd = this.graphicsDevice;
|
|
if (this.currentRenderTarget)
|
|
{
|
|
if (!gd.beginRenderTarget(this.currentRenderTarget.renderTarget))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
gd.clear(clearColor || this.defaultClearColor);
|
|
gd.endRenderTarget();
|
|
}
|
|
else
|
|
{
|
|
gd.clear(clearColor || this.defaultClearColor);
|
|
}
|
|
|
|
return true;
|
|
},
|
|
|
|
clearBatch: function clearFn()
|
|
{
|
|
for (var name in this.texLists)
|
|
{
|
|
if (this.texLists.hasOwnProperty(name))
|
|
{
|
|
delete this.texLists[name];
|
|
}
|
|
}
|
|
this.currentTextureGroup = undefined;
|
|
this.numGroups = 0;
|
|
},
|
|
|
|
bufferSprite : function bufferSpriteFn(buffer, sprite, index)
|
|
{
|
|
sprite._update(0);
|
|
/*jshint bitwise: false*/
|
|
index <<= 4;
|
|
/*jshint bitwise: true*/
|
|
|
|
var data = sprite.data;
|
|
buffer[index] = data[0];
|
|
buffer[index + 1] = data[1];
|
|
buffer[index + 2] = data[2];
|
|
buffer[index + 3] = data[3];
|
|
buffer[index + 4] = data[4];
|
|
buffer[index + 5] = data[5];
|
|
buffer[index + 6] = data[6];
|
|
buffer[index + 7] = data[7];
|
|
buffer[index + 8] = data[8];
|
|
buffer[index + 9] = data[9];
|
|
buffer[index + 10] = data[10];
|
|
buffer[index + 11] = data[11];
|
|
buffer[index + 12] = data[12];
|
|
buffer[index + 13] = data[13];
|
|
buffer[index + 14] = data[14];
|
|
buffer[index + 15] = data[15];
|
|
},
|
|
|
|
update: function updateFn()
|
|
{
|
|
var graphicsDevice = this.graphicsDevice;
|
|
var width = this.width;
|
|
var height = this.height;
|
|
|
|
var graphicsDeviceWidth = graphicsDevice.width;
|
|
var graphicsDeviceHeight = graphicsDevice.height;
|
|
|
|
if (width !== graphicsDeviceWidth || height !== graphicsDeviceHeight || this.forceUpdate)
|
|
{
|
|
var viewWidth, viewHeight, viewX, viewY;
|
|
var viewportRectangle = this.viewportRectangle;
|
|
|
|
if (viewportRectangle)
|
|
{
|
|
viewX = viewportRectangle[0];
|
|
viewY = viewportRectangle[1];
|
|
viewWidth = viewportRectangle[2] - viewX;
|
|
viewHeight = viewportRectangle[3] - viewY;
|
|
}
|
|
else
|
|
{
|
|
viewX = 0;
|
|
viewY = 0;
|
|
viewWidth = graphicsDeviceWidth;
|
|
viewHeight = graphicsDeviceHeight;
|
|
}
|
|
|
|
if ((viewWidth === graphicsDeviceWidth) && (viewHeight === graphicsDeviceHeight))
|
|
{
|
|
this.clearBackBuffer = false;
|
|
}
|
|
else
|
|
{
|
|
this.clearBackBuffer = true;
|
|
}
|
|
|
|
var target = this.currentRenderTarget;
|
|
|
|
if (this.scaleMode === 'scale')
|
|
{
|
|
var viewAspectRatio = viewWidth / viewHeight;
|
|
var graphicsDeviceAspectRatio = graphicsDeviceWidth / graphicsDeviceHeight;
|
|
var calcViewWidth, calcViewHeight, diffWidth, diffHeight, halfDiffWidth, halfDiffHeight;
|
|
|
|
if (graphicsDeviceAspectRatio > viewAspectRatio)
|
|
{
|
|
calcViewWidth = Math.ceil((graphicsDeviceHeight / viewHeight) * viewWidth);
|
|
diffWidth = graphicsDeviceWidth - calcViewWidth;
|
|
halfDiffWidth = Math.floor(diffWidth * 0.5);
|
|
|
|
this.scissorX = halfDiffWidth;
|
|
this.scissorY = 0;
|
|
this.scissorWidth = calcViewWidth;
|
|
this.scissorHeight = graphicsDeviceHeight;
|
|
|
|
this.viewScaleX = viewWidth / calcViewWidth;
|
|
this.viewScaleY = viewHeight / graphicsDeviceHeight;
|
|
|
|
if (!target)
|
|
{
|
|
this.clipOffsetX = (halfDiffWidth / graphicsDeviceWidth * 2.0) - 1.0;
|
|
this.clipOffsetY = 1;
|
|
this.clipScaleX = (calcViewWidth / graphicsDeviceWidth * 2.0) / viewWidth;
|
|
this.clipScaleY = -2.0 / viewHeight;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
calcViewHeight = Math.ceil((graphicsDeviceWidth / viewWidth) * viewHeight);
|
|
diffHeight = graphicsDeviceHeight - calcViewHeight;
|
|
halfDiffHeight = Math.floor(diffHeight * 0.5);
|
|
|
|
this.scissorX = 0;
|
|
this.scissorY = halfDiffHeight;
|
|
this.scissorWidth = graphicsDeviceWidth;
|
|
this.scissorHeight = calcViewHeight;
|
|
|
|
this.viewScaleX = viewWidth / graphicsDeviceWidth;
|
|
this.viewScaleY = viewHeight / calcViewHeight;
|
|
|
|
if (!target)
|
|
{
|
|
this.clipOffsetX = -1.0;
|
|
this.clipOffsetY = 1 - ((halfDiffHeight / graphicsDeviceHeight) * 2.0);
|
|
this.clipScaleX = 2.0 / viewWidth;
|
|
this.clipScaleY = ((calcViewHeight / graphicsDeviceHeight) * -2.0) / viewHeight;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.viewScaleX = 1;
|
|
this.viewScaleY = 1;
|
|
|
|
if (!target)
|
|
{
|
|
this.clipOffsetX = -1.0;
|
|
this.clipOffsetY = 1.0;
|
|
this.clipScaleX = 2.0 / graphicsDeviceWidth;
|
|
this.clipScaleY = -2.0 / graphicsDeviceHeight;
|
|
}
|
|
|
|
this.scissorX = 0;
|
|
this.scissorY = (graphicsDeviceHeight - viewHeight);
|
|
this.scissorWidth = viewWidth;
|
|
this.scissorHeight = viewHeight;
|
|
}
|
|
|
|
this.spriteAngleFactor = Math.min(this.viewScaleX, this.viewScaleY);
|
|
this.spriteAngleFactor *= this.spriteAngleFactor;
|
|
|
|
this.width = graphicsDeviceWidth;
|
|
this.height = graphicsDeviceHeight;
|
|
|
|
var i = 0;
|
|
var renderTargets = this.renderTargetStructs;
|
|
var limit = renderTargets.length;
|
|
for (i = 0; i < limit; i += 1)
|
|
{
|
|
this.validateTarget(renderTargets[i], this.scissorWidth, this.scissorHeight);
|
|
}
|
|
|
|
if (target)
|
|
{
|
|
this.clipOffsetX = -1.0;
|
|
this.clipOffsetY = -1.0;
|
|
this.clipScaleX = 2.0 * target.actualWidth / target.texture.width / viewWidth;
|
|
this.clipScaleY = 2.0 * target.actualHeight / target.texture.height / viewHeight;
|
|
}
|
|
|
|
// Deal with viewports that are not started at (0,0)
|
|
this.clipOffsetX -= viewX * this.clipScaleX;
|
|
this.clipOffsetY -= viewY * this.clipScaleY;
|
|
|
|
var clipSpace = this.techniqueParameters.clipSpace;
|
|
clipSpace[0] = this.clipScaleX;
|
|
clipSpace[1] = this.clipScaleY;
|
|
clipSpace[2] = this.clipOffsetX;
|
|
clipSpace[3] = this.clipOffsetY;
|
|
|
|
this.updateRenderTargetVbo(this.scissorX, this.scissorY, this.scissorWidth, this.scissorHeight);
|
|
this.forceUpdate = false;
|
|
}
|
|
},
|
|
|
|
getViewport: function getViewportFn(dst)
|
|
{
|
|
if (!dst)
|
|
{
|
|
dst = new Draw2D.prototype.floatArray(4);
|
|
}
|
|
var viewport = this.viewportRectangle;
|
|
if (viewport)
|
|
{
|
|
dst[0] = viewport[0];
|
|
dst[1] = viewport[1];
|
|
dst[2] = viewport[2];
|
|
dst[3] = viewport[3];
|
|
}
|
|
else
|
|
{
|
|
dst[0] = dst[1] = 0;
|
|
dst[2] = this.graphicsDevice.width;
|
|
dst[3] = this.graphicsDevice.height;
|
|
}
|
|
return dst;
|
|
},
|
|
getScreenSpaceViewport: function screenSpaceViewportFn(dst)
|
|
{
|
|
if (!dst)
|
|
{
|
|
dst = new Draw2D.prototype.floatArray(4);
|
|
}
|
|
// ensure mapping is correct.
|
|
this.update();
|
|
|
|
dst[0] = this.scissorX;
|
|
dst[1] = this.height - (this.scissorY + this.scissorHeight);
|
|
dst[2] = dst[0] + this.scissorWidth;
|
|
dst[3] = dst[1] + this.scissorHeight;
|
|
return dst;
|
|
},
|
|
|
|
viewportMap: function viewportMapFn(screenX, screenY, dst)
|
|
{
|
|
if (!dst)
|
|
{
|
|
dst = new Draw2D.prototype.floatArray(2);
|
|
}
|
|
// ensure mapping is correct.
|
|
this.update();
|
|
|
|
// webgl coordinates have flipped y.
|
|
var scissorY = (this.height - this.scissorHeight - this.scissorY);
|
|
|
|
dst[0] = (screenX - this.scissorX) * this.viewScaleX;
|
|
dst[1] = (screenY - scissorY) * this.viewScaleY;
|
|
|
|
var viewport = this.viewportRectangle;
|
|
if (viewport)
|
|
{
|
|
dst[0] += viewport[0];
|
|
dst[1] += viewport[1];
|
|
}
|
|
|
|
return dst;
|
|
},
|
|
viewportUnmap: function screenMapFn(x, y, dst)
|
|
{
|
|
if (!dst)
|
|
{
|
|
dst = new Draw2D.prototype.floatArray(2);
|
|
}
|
|
// ensure mapping is correct.
|
|
this.update();
|
|
|
|
var viewport = this.viewportRectangle;
|
|
if (viewport)
|
|
{
|
|
x -= viewport[0];
|
|
y -= viewport[1];
|
|
}
|
|
|
|
// webgl coordinates have flipped y.
|
|
var scissorY = (this.height - this.scissorHeight - this.scissorY);
|
|
|
|
dst[0] = (x / this.viewScaleX) + this.scissorX;
|
|
dst[1] = (y / this.viewScaleY) + scissorY;
|
|
return dst;
|
|
},
|
|
|
|
viewportClamp: function viewportClampFn(point)
|
|
{
|
|
if (point)
|
|
{
|
|
var x = point[0];
|
|
var y = point[1];
|
|
|
|
var minX, minY, maxX, maxY;
|
|
var viewport = this.viewportRectangle;
|
|
if (viewport)
|
|
{
|
|
minX = viewport[0];
|
|
minY = viewport[1];
|
|
maxX = viewport[2];
|
|
maxY = viewport[3];
|
|
}
|
|
else
|
|
{
|
|
minX = 0;
|
|
minY = 0;
|
|
maxX = this.graphicsDevice.width;
|
|
maxY = this.graphicsDevice.height;
|
|
}
|
|
|
|
if (x < minX)
|
|
{
|
|
x = minX;
|
|
}
|
|
else if (x > maxX)
|
|
{
|
|
x = maxX;
|
|
}
|
|
|
|
if (y < minY)
|
|
{
|
|
y = minY;
|
|
}
|
|
else if (y > maxY)
|
|
{
|
|
y = maxY;
|
|
}
|
|
|
|
point[0] = x;
|
|
point[1] = y;
|
|
}
|
|
|
|
return point;
|
|
},
|
|
|
|
configure: function configureFn(params)
|
|
{
|
|
if (this.state !== this.drawStates.ready)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var viewportRectangle = ("viewportRectangle" in params) ? params.viewportRectangle : this.viewportRectangle;
|
|
|
|
var scaleMode = params.scaleMode;
|
|
if (scaleMode !== undefined)
|
|
{
|
|
// check scaleMode is supported.
|
|
if (!(scaleMode in this.scale))
|
|
{
|
|
return false;
|
|
}
|
|
if (scaleMode === 'scale' && !viewportRectangle)
|
|
{
|
|
return false;
|
|
}
|
|
this.scaleMode = scaleMode;
|
|
}
|
|
|
|
this.viewportRectangle = viewportRectangle;
|
|
|
|
this.forceUpdate = true;
|
|
this.update();
|
|
|
|
return true;
|
|
},
|
|
|
|
destroy: function destroyFn()
|
|
{
|
|
this.texLists = null;
|
|
this.state = this.drawStates.uninit;
|
|
|
|
delete this.graphicsDevice;
|
|
|
|
if (this.vertexBuffer)
|
|
{
|
|
this.vertexBuffer.destroy();
|
|
}
|
|
if (this.indexBuffer)
|
|
{
|
|
this.indexBuffer.destroy();
|
|
}
|
|
|
|
this.copyVertexBuffer.destroy();
|
|
|
|
var renderTargets = this.renderTargetStructs;
|
|
while (renderTargets.length > 0)
|
|
{
|
|
var target = renderTargets.pop();
|
|
target.texture.destroy();
|
|
target.renderTarget.destroy();
|
|
delete target.texture;
|
|
delete target.renderTarget;
|
|
}
|
|
},
|
|
|
|
begin: function beginFn(blendMode, sortMode)
|
|
{
|
|
// Check sort mode is well defined (or undefined signifying default)
|
|
if (sortMode && !(sortMode in this.sort))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check blend mode is well defined (or undefined signifying default)
|
|
if (blendMode && !(blendMode in this.blend))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//if there are render states left in the stack
|
|
//and begin has been called without an end
|
|
//draw previous data with current render state
|
|
var firstTime = !this.sortMode;
|
|
if (this.dispatch())
|
|
{
|
|
this.clearBatch();
|
|
}
|
|
|
|
if (firstTime)
|
|
{
|
|
if (this.state !== this.drawStates.ready)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check the buffers are correct before we render
|
|
this.update();
|
|
|
|
if (!this.currentRenderTarget)
|
|
{
|
|
this.graphicsDevice.setScissor(this.scissorX, this.scissorY, this.scissorWidth, this.scissorHeight);
|
|
}
|
|
}
|
|
|
|
this.state = this.drawStates.draw;
|
|
|
|
sortMode = (sortMode) ? sortMode : (firstTime ? 'deferred' : this.sortMode);
|
|
blendMode = (blendMode) ? blendMode : (firstTime ? 'opaque' : this.blendMode);
|
|
|
|
|
|
if (!firstTime)
|
|
{
|
|
this.sortModeStack.push(this.sortMode);
|
|
this.blendModeStack.push(this.blendMode);
|
|
}
|
|
this.sortMode = sortMode;
|
|
this.blendMode = blendMode;
|
|
|
|
this.prepareSortMode(sortMode);
|
|
this.graphicsDevice.setTechnique(this.blendModeTechniques[blendMode]);
|
|
|
|
return true;
|
|
},
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// append sprite data to group buffer.
|
|
_bufferSprite : function bufferSpriteFn(group, sprite)
|
|
{
|
|
var vertexData = group.vertexBufferData;
|
|
var vertexBuffer = this.vertexBuffer;
|
|
|
|
var index = group.numVertices * vertexBuffer.stride;
|
|
var total = index + (4 * vertexBuffer.stride);
|
|
if (total >= vertexData.length)
|
|
{
|
|
// allocate new vertex buffer data array.
|
|
var size = this.bufferSizeAlgorithm(total, this.cpuStride);
|
|
var newData = new Draw2D.prototype.floatArray(size);
|
|
|
|
// copy data from existing buffer.
|
|
var i;
|
|
for (i = 0; i < index; i += 1)
|
|
{
|
|
newData[i] = vertexData[i];
|
|
}
|
|
|
|
group.vertexBufferData = vertexData = newData;
|
|
}
|
|
|
|
var c1 = sprite[8];
|
|
var c2 = sprite[9];
|
|
var c3 = sprite[10];
|
|
var c4 = sprite[11];
|
|
var u1 = sprite[12];
|
|
var v1 = sprite[13];
|
|
var u2 = sprite[14];
|
|
var v2 = sprite[15];
|
|
|
|
vertexData[index] = sprite[0];
|
|
vertexData[index + 1] = sprite[1];
|
|
vertexData[index + 2] = c1;
|
|
vertexData[index + 3] = c2;
|
|
vertexData[index + 4] = c3;
|
|
vertexData[index + 5] = c4;
|
|
vertexData[index + 6] = u1;
|
|
vertexData[index + 7] = v1;
|
|
|
|
vertexData[index + 8] = sprite[2];
|
|
vertexData[index + 9] = sprite[3];
|
|
vertexData[index + 10] = c1;
|
|
vertexData[index + 11] = c2;
|
|
vertexData[index + 12] = c3;
|
|
vertexData[index + 13] = c4;
|
|
vertexData[index + 14] = u2;
|
|
vertexData[index + 15] = v1;
|
|
|
|
vertexData[index + 16] = sprite[4];
|
|
vertexData[index + 17] = sprite[5];
|
|
vertexData[index + 18] = c1;
|
|
vertexData[index + 19] = c2;
|
|
vertexData[index + 20] = c3;
|
|
vertexData[index + 21] = c4;
|
|
vertexData[index + 22] = u1;
|
|
vertexData[index + 23] = v2;
|
|
|
|
vertexData[index + 24] = sprite[6];
|
|
vertexData[index + 25] = sprite[7];
|
|
vertexData[index + 26] = c1;
|
|
vertexData[index + 27] = c2;
|
|
vertexData[index + 28] = c3;
|
|
vertexData[index + 29] = c4;
|
|
vertexData[index + 30] = u2;
|
|
vertexData[index + 31] = v2;
|
|
|
|
group.numVertices += 4;
|
|
|
|
// increment number of indices in present subset.
|
|
group.indices[group.numSets - 1] += 6;
|
|
},
|
|
|
|
bufferMultiSprite : function bufferMultiSprite(group, buffer, count, offset)
|
|
{
|
|
var vertexData = group.vertexBufferData;
|
|
var vertexBuffer = this.vertexBuffer;
|
|
|
|
var numSprites = (count === undefined) ? Math.floor(buffer.length / 16) : count;
|
|
count = numSprites * 16;
|
|
|
|
offset = (offset !== undefined ? offset : 0) * 16;
|
|
|
|
var i;
|
|
var index = (group.numVertices * vertexBuffer.stride);
|
|
var total = index + (numSprites * 4 * vertexBuffer.stride);
|
|
if (total >= vertexData.length)
|
|
{
|
|
// allocate new vertex buffer data array.
|
|
var size = this.bufferSizeAlgorithm(total, this.cpuStride);
|
|
var newData = new Draw2D.prototype.floatArray(size);
|
|
|
|
// copy data from existing buffer.
|
|
for (i = 0; i < index; i += 1)
|
|
{
|
|
newData[i] = vertexData[i];
|
|
}
|
|
|
|
group.vertexBufferData = vertexData = newData;
|
|
}
|
|
|
|
var limit = offset + count;
|
|
for (i = offset; i < limit; i += 16)
|
|
{
|
|
var c1 = buffer[i + 8];
|
|
var c2 = buffer[i + 9];
|
|
var c3 = buffer[i + 10];
|
|
var c4 = buffer[i + 11];
|
|
var u1 = buffer[i + 12];
|
|
var v1 = buffer[i + 13];
|
|
var u2 = buffer[i + 14];
|
|
var v2 = buffer[i + 15];
|
|
|
|
vertexData[index] = buffer[i];
|
|
vertexData[index + 1] = buffer[i + 1];
|
|
vertexData[index + 2] = c1;
|
|
vertexData[index + 3] = c2;
|
|
vertexData[index + 4] = c3;
|
|
vertexData[index + 5] = c4;
|
|
vertexData[index + 6] = u1;
|
|
vertexData[index + 7] = v1;
|
|
|
|
vertexData[index + 8] = buffer[i + 2];
|
|
vertexData[index + 9] = buffer[i + 3];
|
|
vertexData[index + 10] = c1;
|
|
vertexData[index + 11] = c2;
|
|
vertexData[index + 12] = c3;
|
|
vertexData[index + 13] = c4;
|
|
vertexData[index + 14] = u2;
|
|
vertexData[index + 15] = v1;
|
|
|
|
vertexData[index + 16] = buffer[i + 4];
|
|
vertexData[index + 17] = buffer[i + 5];
|
|
vertexData[index + 18] = c1;
|
|
vertexData[index + 19] = c2;
|
|
vertexData[index + 20] = c3;
|
|
vertexData[index + 21] = c4;
|
|
vertexData[index + 22] = u1;
|
|
vertexData[index + 23] = v2;
|
|
|
|
vertexData[index + 24] = buffer[i + 6];
|
|
vertexData[index + 25] = buffer[i + 7];
|
|
vertexData[index + 26] = c1;
|
|
vertexData[index + 27] = c2;
|
|
vertexData[index + 28] = c3;
|
|
vertexData[index + 29] = c4;
|
|
vertexData[index + 30] = u2;
|
|
vertexData[index + 31] = v2;
|
|
|
|
index += 32;
|
|
}
|
|
|
|
group.numVertices += (numSprites * 4);
|
|
// increment number of indices in present subset.
|
|
group.indices[group.numSets - 1] += (numSprites * 6);
|
|
},
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
indexData : function indexDataFn(count)
|
|
{
|
|
var indexData = new Draw2D.prototype.uint16Array(count);
|
|
var i;
|
|
var vertexIndex = 0;
|
|
for (i = 0; i < count; i += 6)
|
|
{
|
|
indexData[i] = vertexIndex;
|
|
indexData[i + 1] = vertexIndex + 1;
|
|
indexData[i + 2] = vertexIndex + 2;
|
|
indexData[i + 3] = vertexIndex + 1;
|
|
indexData[i + 4] = vertexIndex + 2;
|
|
indexData[i + 5] = vertexIndex + 3;
|
|
vertexIndex += 4;
|
|
}
|
|
return indexData;
|
|
},
|
|
|
|
// upload group buffer to graphics device vertexBuffer.
|
|
uploadBuffer : function uploadBufferFn(group, count, offset)
|
|
{
|
|
var vertexBuffer = this.vertexBuffer;
|
|
var vertexBufferParameters = this.vertexBufferParameters;
|
|
var graphicsDevice = this.graphicsDevice;
|
|
var vertexData = group.vertexBufferData;
|
|
|
|
var performanceData = this.performanceData;
|
|
|
|
// Resize buffers.
|
|
if (count > vertexBufferParameters.numVertices)
|
|
{
|
|
var newSize = this.bufferSizeAlgorithm(count, this.gpuStride);
|
|
if (newSize > this.maxVertices)
|
|
{
|
|
newSize = this.maxVertices;
|
|
}
|
|
|
|
vertexBufferParameters.numVertices = newSize;
|
|
this.vertexBuffer.destroy();
|
|
this.vertexBuffer = vertexBuffer = graphicsDevice.createVertexBuffer(vertexBufferParameters);
|
|
|
|
// 32 bytes per vertex.
|
|
// 2 bytes per index, 1.5 indices per vertex.
|
|
performanceData.gpuMemoryUsage = newSize * 35; // 32 + (1.5 * 2)
|
|
|
|
newSize *= 1.5;
|
|
|
|
// Set indices.
|
|
var indexBufferParameters = this.indexBufferParameters;
|
|
indexBufferParameters.data = this.indexData(newSize);
|
|
indexBufferParameters.numIndices = newSize;
|
|
this.indexBuffer.destroy();
|
|
this.indexBuffer = graphicsDevice.createIndexBuffer(indexBufferParameters);
|
|
graphicsDevice.setIndexBuffer(this.indexBuffer);
|
|
}
|
|
|
|
performanceData.dataTransfers += 1;
|
|
|
|
// Upload data.
|
|
if (offset === 0)
|
|
{
|
|
vertexBuffer.setData(vertexData, 0, count);
|
|
}
|
|
else
|
|
{
|
|
var stride = vertexBuffer.stride;
|
|
vertexBuffer.setData(vertexData.subarray(offset * stride, (offset + count) * stride), 0, count);
|
|
}
|
|
},
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
drawRawImmediate : function drawRawImmediateFn(texture, multiSprite, count, offset)
|
|
{
|
|
var group = this.drawGroups[0];
|
|
group.textures[0] = texture || this.defaultTexture;
|
|
group.indices[0] = 0;
|
|
group.numSets = 1;
|
|
this.numGroups = 1;
|
|
|
|
this.bufferMultiSprite(group, multiSprite, count, offset);
|
|
|
|
// Draw render group immediately.
|
|
this.dispatch();
|
|
},
|
|
|
|
drawSpriteImmediate : function drawSpriteImmediateFn(sprite)
|
|
{
|
|
var group = this.drawGroups[0];
|
|
group.textures[0] = sprite._texture || this.defaultTexture;
|
|
group.indices[0] = 0;
|
|
group.numSets = 1;
|
|
this.numGroups = 1;
|
|
|
|
sprite._update(this.spriteAngleFactor);
|
|
this._bufferSprite(group, sprite.data);
|
|
|
|
// Draw render group immediately.
|
|
this.dispatch();
|
|
},
|
|
|
|
drawImmediate : function drawImmediateFn(params)
|
|
{
|
|
var texture = params.texture || this.defaultTexture;
|
|
var destRect = params.destinationRectangle;
|
|
var srcRect = params.sourceRectangle;
|
|
var color = params.color;
|
|
var rotation = params.rotation;
|
|
|
|
var group = this.drawGroups[0];
|
|
group.textures[0] = texture;
|
|
group.indices[0] = 0;
|
|
group.numSets = 1;
|
|
this.numGroups = 1;
|
|
|
|
var drawSprite = this.drawSprite;
|
|
Draw2DSpriteData.setFromRotatedRectangle(drawSprite, texture, destRect, srcRect, color, rotation, params.origin);
|
|
this._bufferSprite(group, drawSprite);
|
|
|
|
// Draw render group immediately.
|
|
this.dispatch();
|
|
},
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
drawRawDeferred : function drawRawDeferredFn(texture, multiSprite, count, offset)
|
|
{
|
|
texture = texture || this.defaultTexture;
|
|
var group = this.drawGroups[0];
|
|
this.numGroups = 1;
|
|
// If present group draw list uses a different texture
|
|
// We must start a new draw list.
|
|
var numSets = group.numSets;
|
|
if (numSets === 0 || group.textures[numSets - 1] !== texture)
|
|
{
|
|
group.textures[numSets] = texture;
|
|
group.indices[numSets] = 0;
|
|
group.numSets += 1;
|
|
}
|
|
|
|
this.bufferMultiSprite(group, multiSprite, count, offset);
|
|
},
|
|
|
|
drawSpriteDeferred : function drawSpriteDeferredFn(sprite)
|
|
{
|
|
var texture = sprite._texture || this.defaultTexture;
|
|
|
|
var group = this.drawGroups[0];
|
|
this.numGroups = 1;
|
|
// If present group draw list uses a different texture
|
|
// We must start a new draw list.
|
|
var numSets = group.numSets;
|
|
if (numSets === 0 || group.textures[numSets - 1] !== texture)
|
|
{
|
|
group.textures[numSets] = texture;
|
|
group.indices[numSets] = 0;
|
|
group.numSets += 1;
|
|
}
|
|
|
|
sprite._update(this.spriteAngleFactor);
|
|
this._bufferSprite(group, sprite.data);
|
|
},
|
|
|
|
drawDeferred : function drawDeferredFn(params)
|
|
{
|
|
var texture = params.texture || this.defaultTexture;
|
|
|
|
var group = this.drawGroups[0];
|
|
this.numGroups = 1;
|
|
// If present group draw list uses a different texture
|
|
// We must start a new draw list.
|
|
var numSets = group.numSets;
|
|
if (numSets === 0 || group.textures[numSets - 1] !== texture)
|
|
{
|
|
group.textures[numSets] = texture;
|
|
group.indices[numSets] = 0;
|
|
group.numSets += 1;
|
|
}
|
|
|
|
var destRect = params.destinationRectangle;
|
|
var srcRect = params.sourceRectangle;
|
|
var color = params.color;
|
|
var rotation = params.rotation;
|
|
|
|
var drawSprite = this.drawSprite;
|
|
Draw2DSpriteData.setFromRotatedRectangle(drawSprite, texture, destRect, srcRect, color, rotation, params.origin);
|
|
|
|
this._bufferSprite(group, drawSprite);
|
|
},
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
drawRawTextured : function drawRawTexturedFn(texture, multiSprite, count, offset)
|
|
{
|
|
texture = texture || this.defaultTexture;
|
|
var group;
|
|
// If last call to drawTextured used the same texture, then we need not look up render group.
|
|
if (this.currentTextureGroup !== undefined && this.currentTextureGroup.textures[0] === texture)
|
|
{
|
|
group = this.currentTextureGroup;
|
|
}
|
|
else
|
|
{
|
|
// Look up render group in texLists.
|
|
var name = texture.name;
|
|
var texLists = this.texLists;
|
|
group = texLists[name];
|
|
if (!group)
|
|
{
|
|
// Create new render group.
|
|
group = this.drawGroups[this.numGroups];
|
|
if (!group)
|
|
{
|
|
group = Draw2DGroup.create();
|
|
}
|
|
this.drawGroups[this.numGroups] = texLists[name] = group;
|
|
group.textures[0] = texture;
|
|
group.indices[0] = 0;
|
|
group.numSets = 1;
|
|
this.numGroups += 1;
|
|
}
|
|
this.currentTextureGroup = group;
|
|
}
|
|
|
|
this.bufferMultiSprite(group, multiSprite, count, offset);
|
|
},
|
|
|
|
drawSpriteTextured : function drawSpriteTexturedFn(sprite)
|
|
{
|
|
var texture = sprite._texture || this.defaultTexture;
|
|
|
|
var group;
|
|
// If last call to drawTextured used the same texture, then we need not look up render group.
|
|
if (this.currentTextureGroup !== undefined && this.currentTextureGroup.textures[0] === texture)
|
|
{
|
|
group = this.currentTextureGroup;
|
|
}
|
|
else
|
|
{
|
|
// Look up render group in texLists.
|
|
var name = texture.name;
|
|
var texLists = this.texLists;
|
|
group = texLists[name];
|
|
if (!group)
|
|
{
|
|
// Create new render group.
|
|
group = this.drawGroups[this.numGroups];
|
|
if (!group)
|
|
{
|
|
group = Draw2DGroup.create();
|
|
}
|
|
this.drawGroups[this.numGroups] = texLists[name] = group;
|
|
group.textures[0] = texture;
|
|
group.indices[0] = 0;
|
|
group.numSets = 1;
|
|
this.numGroups += 1;
|
|
}
|
|
this.currentTextureGroup = group;
|
|
}
|
|
|
|
sprite._update(this.spriteAngleFactor);
|
|
this._bufferSprite(group, sprite.data);
|
|
},
|
|
|
|
drawTextured : function drawTexturedFn(params)
|
|
{
|
|
var texture = params.texture || this.defaultTexture;
|
|
|
|
var group;
|
|
// If last call to drawTextured used the same texture, then we need not look up render group.
|
|
if (this.currentTextureGroup !== undefined && this.currentTextureGroup.textures[0] === texture)
|
|
{
|
|
group = this.currentTextureGroup;
|
|
}
|
|
else
|
|
{
|
|
// Look up render group in texLists.
|
|
var name = texture.name;
|
|
var texLists = this.texLists;
|
|
group = texLists[name];
|
|
if (!group)
|
|
{
|
|
// Create new render group.
|
|
group = this.drawGroups[this.numGroups];
|
|
if (!group)
|
|
{
|
|
group = Draw2DGroup.create();
|
|
}
|
|
this.drawGroups[this.numGroups] = texLists[name] = group;
|
|
group.textures[0] = texture;
|
|
group.indices[0] = 0;
|
|
group.numSets = 1;
|
|
this.numGroups += 1;
|
|
}
|
|
this.currentTextureGroup = group;
|
|
}
|
|
|
|
var destRect = params.destinationRectangle;
|
|
var srcRect = params.sourceRectangle;
|
|
var color = params.color;
|
|
var rotation = params.rotation;
|
|
|
|
var drawSprite = this.drawSprite;
|
|
Draw2DSpriteData.setFromRotatedRectangle(drawSprite, texture, destRect, srcRect, color, rotation, params.origin);
|
|
|
|
this._bufferSprite(group, drawSprite);
|
|
},
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
prepareSortMode : function refreshSortModeFn(sortMode)
|
|
{
|
|
if (sortMode === 'deferred')
|
|
{
|
|
this.draw = this.drawDeferred;
|
|
this.drawSprite = this.drawSpriteDeferred;
|
|
this.drawRaw = this.drawRawDeferred;
|
|
}
|
|
else if (sortMode === 'immediate')
|
|
{
|
|
this.draw = this.drawImmediate;
|
|
this.drawSprite = this.drawSpriteImmediate;
|
|
this.drawRaw = this.drawRawImmediate;
|
|
}
|
|
else
|
|
{
|
|
this.draw = this.drawTextured;
|
|
this.drawSprite = this.drawSpriteTextured;
|
|
this.drawRaw = this.drawRawTextured;
|
|
}
|
|
},
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
end: function endFn()
|
|
{
|
|
if (this.state !== this.drawStates.draw)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//dispatch objects to the graphics card
|
|
if (this.dispatch())
|
|
{
|
|
this.clearBatch();
|
|
}
|
|
|
|
if (this.blendModeStack.length !== 0)
|
|
{
|
|
this.blendMode = this.blendModeStack.pop();
|
|
this.sortMode = this.sortModeStack.pop();
|
|
this.prepareSortMode(this.sortMode);
|
|
this.graphicsDevice.setTechnique(this.blendModeTechniques[this.blendMode]);
|
|
}
|
|
else
|
|
{
|
|
this.blendMode = undefined;
|
|
this.sortMode = undefined;
|
|
this.state = this.drawStates.ready;
|
|
}
|
|
|
|
return true;
|
|
},
|
|
|
|
dispatch: function dispatchFn()
|
|
{
|
|
// Nothing to dispatch.
|
|
var numGroups = this.numGroups;
|
|
if (numGroups === 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var graphicsDevice = this.graphicsDevice;
|
|
var techniqueParameters = this.techniqueParameters;
|
|
graphicsDevice.setIndexBuffer(this.indexBuffer);
|
|
|
|
var drawGroups = this.drawGroups;
|
|
var renderTargetUsed = false;
|
|
if (this.currentRenderTarget)
|
|
{
|
|
renderTargetUsed = graphicsDevice.beginRenderTarget(this.currentRenderTarget.renderTarget);
|
|
}
|
|
|
|
var performanceData = this.performanceData;
|
|
|
|
var i;
|
|
for (i = 0; i < numGroups; i += 1)
|
|
{
|
|
var group = drawGroups[i];
|
|
|
|
var textures = group.textures;
|
|
var indices = group.indices;
|
|
var setIndex = 0;
|
|
|
|
var vindex = 0;
|
|
var vlimit = group.numVertices;
|
|
while (vindex < vlimit)
|
|
{
|
|
// number of vertices remaining.
|
|
var vcount = vlimit - vindex;
|
|
if (vcount > this.maxVertices)
|
|
{
|
|
vcount = this.maxVertices;
|
|
}
|
|
|
|
// Upload group vertex sub-buffer to graphics device.
|
|
this.uploadBuffer(group, vcount, vindex);
|
|
graphicsDevice.setStream(this.vertexBuffer, this.semantics);
|
|
|
|
// sprite uses 4 vertices, and 6 indices
|
|
// so for 'vcount' number of vertices, we have vcount * 1.5 indices
|
|
var ilimit = vcount * 1.5;
|
|
var iindex = 0;
|
|
while (iindex < ilimit) {
|
|
techniqueParameters.texture = textures[setIndex];
|
|
|
|
// number of indices remaining to render.
|
|
var icount = ilimit - iindex;
|
|
if (icount >= indices[setIndex])
|
|
{
|
|
// finish rendering sub list.
|
|
icount = indices[setIndex];
|
|
setIndex += 1;
|
|
}
|
|
else
|
|
{
|
|
// sub list still has remaining indices to render.
|
|
indices[setIndex] -= icount;
|
|
}
|
|
|
|
var batchSize = icount / 6;
|
|
if (performanceData.batchCount === 0)
|
|
{
|
|
performanceData.minBatchSize = batchSize;
|
|
performanceData.maxBatchSize = batchSize;
|
|
performanceData.avgBatchSize = batchSize;
|
|
performanceData.batchCount = 1;
|
|
}
|
|
else
|
|
{
|
|
if (batchSize < performanceData.minBatchSize)
|
|
{
|
|
performanceData.minBatchSize = batchSize;
|
|
}
|
|
if (batchSize > performanceData.maxBatchSize)
|
|
{
|
|
performanceData.maxBatchSize = batchSize;
|
|
}
|
|
performanceData.avgBatchSize *= performanceData.batchCount;
|
|
performanceData.avgBatchSize += batchSize;
|
|
performanceData.batchCount += 1;
|
|
performanceData.avgBatchSize /= performanceData.batchCount;
|
|
}
|
|
|
|
graphicsDevice.setTechniqueParameters(techniqueParameters);
|
|
graphicsDevice.drawIndexed(graphicsDevice.PRIMITIVE_TRIANGLES, icount, iindex);
|
|
|
|
iindex += icount;
|
|
}
|
|
|
|
vindex += vcount;
|
|
}
|
|
|
|
group.numSets = 0;
|
|
group.numVertices = 0;
|
|
}
|
|
|
|
if (this.currentRenderTarget && renderTargetUsed)
|
|
{
|
|
graphicsDevice.endRenderTarget();
|
|
}
|
|
|
|
return true;
|
|
},
|
|
|
|
bufferSizeAlgorithm : function bufferSizeAlgorithmFn(target, stride)
|
|
{
|
|
// scale factor of 2 is asymtopically optimal in terms of number of resizes
|
|
// performed and copies performed, but we want to try and conserve memory
|
|
// and so choose a less optimal 1.25 so that buffer will never be too much
|
|
// larger than necessary.
|
|
var factor = 1.25;
|
|
|
|
// We size buffer to the next power of the factor which is >= target
|
|
var logf = Math.ceil(Math.log(target) / Math.log(factor));
|
|
var size = Math.floor(Math.pow(factor, logf));
|
|
|
|
// Additionally ensure that we always take a multiple of of the stride
|
|
// to avoid wasted bytes that could never be used.
|
|
return (stride * Math.ceil(size / stride));
|
|
},
|
|
|
|
updateRenderTargetVbo : function updateRenderTargetVboFn(viewX, viewY, viewWidth, viewHeight)
|
|
{
|
|
var graphicsDevice = this.graphicsDevice;
|
|
var halfGraphicsDeviceWidth = 0.5 * graphicsDevice.width;
|
|
var halfGraphicsDeviceHeight = 0.5 * graphicsDevice.height;
|
|
|
|
//
|
|
// Update the VBO for the presentRenderTarget
|
|
//
|
|
var vertexBuffer = this.copyVertexBuffer;
|
|
|
|
var left = (viewX - halfGraphicsDeviceWidth) / halfGraphicsDeviceWidth;
|
|
var right = (viewX + viewWidth - halfGraphicsDeviceWidth) / halfGraphicsDeviceWidth;
|
|
var topv = (viewY - halfGraphicsDeviceHeight) / halfGraphicsDeviceHeight;
|
|
var bottom = (viewY + viewHeight - halfGraphicsDeviceHeight) / halfGraphicsDeviceHeight;
|
|
|
|
var vertexData = this.vertexBufferData;
|
|
vertexData[0] = left;
|
|
vertexData[1] = bottom;
|
|
vertexData[2] = 0.0;
|
|
vertexData[3] = 1.0;
|
|
|
|
vertexData[4] = left;
|
|
vertexData[5] = topv;
|
|
vertexData[6] = 0.0;
|
|
vertexData[7] = 0.0;
|
|
|
|
vertexData[8] = right;
|
|
vertexData[9] = bottom;
|
|
vertexData[10] = 1.0;
|
|
vertexData[11] = 1.0;
|
|
|
|
vertexData[12] = right;
|
|
vertexData[13] = topv;
|
|
vertexData[14] = 1.0;
|
|
vertexData[15] = 0.0;
|
|
|
|
vertexBuffer.setData(vertexData, 0, 4);
|
|
},
|
|
|
|
// always overallocate.
|
|
/*jshint bitwise: false*/
|
|
makePow2 : function makePow2Fn(dim)
|
|
{
|
|
var index = Math.log(dim) / Math.log(2);
|
|
return (1 << Math.ceil(index));
|
|
},
|
|
/*jshint bitwise: true*/
|
|
|
|
createRenderTarget : function createRenderTargetFn(params)
|
|
{
|
|
var gd = this.graphicsDevice;
|
|
var renderTargets = this.renderTargetStructs;
|
|
var index = renderTargets.length;
|
|
|
|
var name = (params && params.name) ? params.name : ("RenderTarget#" + index);
|
|
var backBuffer = (params && params.backBuffer !== undefined) ? params.backBuffer : true;
|
|
var matchScreen = (params.width === undefined || params.height === undefined);
|
|
|
|
var texParams = this.renderTargetTextureParameters;
|
|
texParams.name = name;
|
|
|
|
var width = (matchScreen) ? gd.width : params.width;
|
|
var height = (matchScreen) ? gd.height : params.height;
|
|
texParams.width = this.makePow2(width);
|
|
texParams.height = this.makePow2(height);
|
|
|
|
var texture = gd.createTexture(texParams);
|
|
var targetParams = this.renderTargetParams;
|
|
targetParams.colorTexture0 = texture;
|
|
var renderTarget = gd.createRenderTarget(targetParams);
|
|
|
|
renderTargets.push({
|
|
managed : matchScreen,
|
|
renderTarget : renderTarget,
|
|
texture : texture,
|
|
backBuffer : backBuffer,
|
|
actualWidth : (backBuffer ? width : texture.width),
|
|
actualHeight : (backBuffer ? height : texture.height)
|
|
});
|
|
|
|
return index;
|
|
},
|
|
|
|
validateTarget : function validateTargetFn(target, viewWidth, viewHeight)
|
|
{
|
|
if (target.managed)
|
|
{
|
|
var tex = target.texture;
|
|
if (target.backBuffer)
|
|
{
|
|
target.actualWidth = viewWidth;
|
|
target.actualHeight = viewHeight;
|
|
}
|
|
viewWidth = this.makePow2(viewWidth);
|
|
viewHeight = this.makePow2(viewHeight);
|
|
if (!target.backBuffer)
|
|
{
|
|
target.actualWidth = viewWidth;
|
|
target.actualHeight = viewHeight;
|
|
}
|
|
if (tex.width !== viewWidth || tex.height !== viewHeight)
|
|
{
|
|
var texParams = this.renderTargetTextureParameters;
|
|
var targetParams = this.renderTargetParams;
|
|
|
|
texParams.name = tex.name;
|
|
texParams.width = viewWidth;
|
|
texParams.height = viewHeight;
|
|
|
|
tex.destroy();
|
|
target.renderTarget.destroy();
|
|
|
|
var graphicsDevice = this.graphicsDevice;
|
|
target.texture = graphicsDevice.createTexture(texParams);
|
|
targetParams.colorTexture0 = target.texture;
|
|
target.renderTarget = graphicsDevice.createRenderTarget(targetParams);
|
|
}
|
|
}
|
|
},
|
|
|
|
setBackBuffer : function setBackBufferFn()
|
|
{
|
|
if (this.state !== this.drawStates.ready)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
this.currentRenderTarget = null;
|
|
this.forceUpdate = true;
|
|
|
|
return true;
|
|
},
|
|
|
|
getRenderTargetTexture : function getRenderTargetTextureFn(renderTargetIndex)
|
|
{
|
|
var renderTargets = this.renderTargetStructs;
|
|
if (renderTargetIndex < 0 || renderTargetIndex >= renderTargets.length)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return renderTargets[renderTargetIndex].texture;
|
|
},
|
|
|
|
getRenderTarget : function getRenderTargetFn(renderTargetIndex)
|
|
{
|
|
var renderTargets = this.renderTargetStructs;
|
|
if (renderTargetIndex < 0 || renderTargetIndex >= renderTargets.length)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return renderTargets[renderTargetIndex].renderTarget;
|
|
},
|
|
|
|
setRenderTarget : function setRenderTargetFn(renderTargetIndex)
|
|
{
|
|
var renderTargets = this.renderTargetStructs;
|
|
if (renderTargetIndex < 0 || renderTargetIndex >= renderTargets.length)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (this.state !== this.drawStates.ready)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
this.currentRenderTarget = renderTargets[renderTargetIndex];
|
|
this.forceUpdate = true;
|
|
|
|
return true;
|
|
},
|
|
|
|
copyRenderTarget: function copyRenderTargetFn(renderTargetIndex)
|
|
{
|
|
if (this.state !== this.drawStates.ready)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var renderTargets = this.renderTargetStructs;
|
|
if (renderTargetIndex < 0 || renderTargetIndex >= renderTargets.length)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check the buffers are correct before we render.
|
|
this.update();
|
|
|
|
if (!this.currentRenderTarget)
|
|
{
|
|
this.graphicsDevice.setScissor(this.scissorX, this.scissorY, this.scissorWidth, this.scissorHeight);
|
|
}
|
|
|
|
var graphicsDevice = this.graphicsDevice;
|
|
var target = renderTargets[renderTargetIndex];
|
|
var tex = target.texture;
|
|
|
|
var technique = this.copyTechnique;
|
|
var params = this.copyTechniqueParameters;
|
|
var copyUVScale = params.copyUVScale;
|
|
copyUVScale[0] = target.actualWidth / tex.width;
|
|
copyUVScale[1] = target.actualHeight / tex.height;
|
|
params.copyFlip = (!this.currentRenderTarget ? -1.0 : 1.0);
|
|
params.inputTexture0 = tex;
|
|
|
|
var renderTargetUsed = false;
|
|
var currentTarget = this.currentRenderTarget;
|
|
var vbo = this.copyVertexBuffer;
|
|
if (currentTarget)
|
|
{
|
|
renderTargetUsed = graphicsDevice.beginRenderTarget(currentTarget.renderTarget);
|
|
}
|
|
|
|
graphicsDevice.setTechnique(technique);
|
|
graphicsDevice.setTechniqueParameters(params);
|
|
|
|
graphicsDevice.setStream(vbo, this.quadSemantics);
|
|
graphicsDevice.draw(this.quadPrimitive, 4, 0);
|
|
|
|
if (currentTarget && renderTargetUsed)
|
|
{
|
|
graphicsDevice.endRenderTarget();
|
|
}
|
|
|
|
return true;
|
|
},
|
|
|
|
resetPerformanceData : function resetPerformanceDataFn()
|
|
{
|
|
var data = this.performanceData;
|
|
data.minBatchSize = data.maxBatchSize = data.avgBatchSize = undefined;
|
|
data.batchCount = 0;
|
|
data.dataTransfers = 0;
|
|
}
|
|
};
|
|
|
|
// Constructor function
|
|
//
|
|
// params : {
|
|
// graphicsDevice : gd,
|
|
// blendModes : { // optional
|
|
// name : Technique,
|
|
// **repeated**
|
|
// }
|
|
// }
|
|
Draw2D.create = function draw2DCreateFn(params)
|
|
{
|
|
var o = new Draw2D();
|
|
var gd = o.graphicsDevice = params.graphicsDevice;
|
|
|
|
// Current sort and blend mode.
|
|
o.sortMode = undefined;
|
|
o.blendMode = undefined;
|
|
// Disjoint stack of modes for nested begins.
|
|
o.sortModeStack = [];
|
|
o.blendModeStack = [];
|
|
|
|
// Set of render groups to be dispatched.
|
|
o.drawGroups = [Draw2DGroup.create()];
|
|
o.numGroups = 0;
|
|
|
|
// Set of render groups for texture sort mode.
|
|
// dictionary on texture name.
|
|
o.texLists = [];
|
|
// Cached reference to last retrieved group to accelerate
|
|
// texture sort mode draw calls.
|
|
o.texGroup = undefined;
|
|
|
|
// Sprite data instance used for rectangle draw calls.
|
|
o.drawSprite = Draw2DSpriteData.create();
|
|
|
|
// Solid fill texture for draw calls that do not specify a texture.
|
|
o.defaultTexture = gd.createTexture({
|
|
name : "DefaultDraw2DTexture",
|
|
width : 1,
|
|
height : 1,
|
|
depth : 1,
|
|
format : "L8",
|
|
cubemap : false,
|
|
mipmaps : true,
|
|
renderable : false,
|
|
dynamic : false,
|
|
data : [0xff]
|
|
});
|
|
|
|
// Draw call methods.
|
|
// These are set based on current sort mode.
|
|
o.draw = undefined;
|
|
o.drawSprite = undefined;
|
|
o.drawRaw = undefined;
|
|
|
|
// Load embedded default shader and techniques
|
|
/*jshint white: false*/
|
|
var shader = gd.createShader(
|
|
{
|
|
"version": 1,
|
|
"name": "draw2D.cgfx",
|
|
"samplers":
|
|
{
|
|
"texture":
|
|
{
|
|
"MinFilter": 9985,
|
|
"MagFilter": 9729,
|
|
"WrapS": 33071,
|
|
"WrapT": 33071
|
|
},
|
|
"inputTexture0":
|
|
{
|
|
"MinFilter": 9728,
|
|
"MagFilter": 9729,
|
|
"WrapS": 33071,
|
|
"WrapT": 33071
|
|
}
|
|
},
|
|
"parameters":
|
|
{
|
|
"clipSpace":
|
|
{
|
|
"type": "float",
|
|
"columns": 4
|
|
},
|
|
"copyUVScale":
|
|
{
|
|
"type": "float",
|
|
"columns": 2
|
|
},
|
|
"copyFlip":
|
|
{
|
|
"type": "float"
|
|
},
|
|
"texture":
|
|
{
|
|
"type": "sampler2D"
|
|
},
|
|
"inputTexture0":
|
|
{
|
|
"type": "sampler2D"
|
|
}
|
|
},
|
|
"techniques":
|
|
{
|
|
"opaque":
|
|
[
|
|
{
|
|
"parameters": ["clipSpace","texture"],
|
|
"semantics": ["POSITION","COLOR","TEXCOORD0"],
|
|
"states":
|
|
{
|
|
"DepthTestEnable": false,
|
|
"DepthMask": false,
|
|
"CullFaceEnable": false,
|
|
"BlendEnable": false
|
|
},
|
|
"programs": ["vp_draw2D","fp_draw2D"]
|
|
}
|
|
],
|
|
"alpha":
|
|
[
|
|
{
|
|
"parameters": ["clipSpace","texture"],
|
|
"semantics": ["POSITION","COLOR","TEXCOORD0"],
|
|
"states":
|
|
{
|
|
"DepthTestEnable": false,
|
|
"DepthMask": false,
|
|
"CullFaceEnable": false,
|
|
"BlendEnable": true,
|
|
"BlendFunc": [770,771]
|
|
},
|
|
"programs": ["vp_draw2D","fp_draw2D"]
|
|
}
|
|
],
|
|
"additive":
|
|
[
|
|
{
|
|
"parameters": ["clipSpace","texture"],
|
|
"semantics": ["POSITION","COLOR","TEXCOORD0"],
|
|
"states":
|
|
{
|
|
"DepthTestEnable": false,
|
|
"DepthMask": false,
|
|
"CullFaceEnable": false,
|
|
"BlendEnable": true,
|
|
"BlendFunc": [770,1]
|
|
},
|
|
"programs": ["vp_draw2D","fp_draw2D"]
|
|
}
|
|
],
|
|
"copy":
|
|
[
|
|
{
|
|
"parameters": ["copyUVScale","copyFlip","inputTexture0"],
|
|
"semantics": ["POSITION","TEXCOORD0"],
|
|
"states":
|
|
{
|
|
"DepthTestEnable": false,
|
|
"DepthMask": false,
|
|
"CullFaceEnable": false,
|
|
"BlendEnable": false
|
|
},
|
|
"programs": ["vp_copy","fp_copy"]
|
|
}
|
|
]
|
|
},
|
|
"programs":
|
|
{
|
|
"fp_copy":
|
|
{
|
|
"type": "fragment",
|
|
"code": "#ifdef GL_ES\n#define TZ_LOWP lowp\nprecision mediump float;\nprecision mediump int;\n#else\n#define TZ_LOWP\n#endif\nvarying vec4 tz_TexCoord[8];\nvec4 _ret_0;uniform sampler2D inputTexture0;void main()\n{_ret_0=texture2D(inputTexture0,tz_TexCoord[0].xy);gl_FragColor=_ret_0;}"
|
|
},
|
|
"vp_copy":
|
|
{
|
|
"type": "vertex",
|
|
"code": "#ifdef GL_ES\n#define TZ_LOWP lowp\nprecision mediump float;\nprecision mediump int;\n#else\n#define TZ_LOWP\n#endif\nvarying vec4 tz_TexCoord[8];attribute vec4 ATTR8;attribute vec4 ATTR0;\nvec4 _OutPosition1;vec2 _OutUV1;uniform vec2 copyUVScale;uniform float copyFlip;void main()\n{_OutPosition1.x=ATTR0.x;_OutPosition1.y=ATTR0.y*copyFlip;_OutPosition1.zw=ATTR0.zw;_OutUV1=ATTR8.xy*copyUVScale;tz_TexCoord[0].xy=_OutUV1;gl_Position=_OutPosition1;}"
|
|
},
|
|
"fp_draw2D":
|
|
{
|
|
"type": "fragment",
|
|
"code": "#ifdef GL_ES\n#define TZ_LOWP lowp\nprecision mediump float;\nprecision mediump int;\n#else\n#define TZ_LOWP\n#endif\nvarying TZ_LOWP vec4 tz_Color;varying vec4 tz_TexCoord[8];\nvec4 _ret_0;vec4 _TMP0;uniform sampler2D texture;void main()\n{_TMP0=texture2D(texture,tz_TexCoord[0].xy);_ret_0=tz_Color*_TMP0;gl_FragColor=_ret_0;}"
|
|
},
|
|
"vp_draw2D":
|
|
{
|
|
"type": "vertex",
|
|
"code": "#ifdef GL_ES\n#define TZ_LOWP lowp\nprecision mediump float;\nprecision mediump int;\n#else\n#define TZ_LOWP\n#endif\nvarying TZ_LOWP vec4 tz_Color;varying vec4 tz_TexCoord[8];attribute vec4 ATTR8;attribute vec4 ATTR3;attribute vec4 ATTR0;\nvec4 _OUTPosition1;vec4 _OUTColor1;vec2 _OUTTexCoord01;uniform vec4 clipSpace;void main()\n{vec2 _position;_position=ATTR0.xy*clipSpace.xy+clipSpace.zw;_OUTPosition1.x=_position.x;_OUTPosition1.y=_position.y;_OUTPosition1.z=0.0;_OUTPosition1.w=1.0;_OUTColor1=ATTR3;_OUTTexCoord01=ATTR8.xy;tz_TexCoord[0].xy=ATTR8.xy;tz_Color=ATTR3;gl_Position=_OUTPosition1;}"
|
|
}
|
|
}
|
|
}
|
|
);
|
|
/*jshint white: true*/
|
|
|
|
// supported blend modes.
|
|
o.blend = {
|
|
additive : 'additive',
|
|
alpha : 'alpha',
|
|
opaque : 'opaque'
|
|
},
|
|
|
|
// Mapping from blend mode name to Technique object.
|
|
o.blendModeTechniques = {};
|
|
o.blendModeTechniques.additive = shader.getTechnique("additive");
|
|
o.blendModeTechniques.alpha = shader.getTechnique("alpha");
|
|
o.blendModeTechniques.opaque = shader.getTechnique("opaque");
|
|
|
|
// Append techniques and supported blend modes with user supplied techniques.
|
|
if (params.blendModes)
|
|
{
|
|
for (var name in params.blendModes)
|
|
{
|
|
if (params.blendModes.hasOwnProperty(name))
|
|
{
|
|
o.blend[name] = name;
|
|
o.blendModeTechniques[name] = params.blendModes[name];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Blending techniques.
|
|
o.techniqueParameters = gd.createTechniqueParameters({
|
|
clipSpace: new Draw2D.prototype.floatArray(4),
|
|
texture: null
|
|
});
|
|
|
|
// Current render target
|
|
o.currentRenderTarget = null;
|
|
o.renderTargetStructs = [];
|
|
|
|
o.state = o.drawStates.ready;
|
|
|
|
o.scaleMode = 'none';
|
|
o.blendMode = 'opaque';
|
|
|
|
// View port, back buffer and managed render target values.
|
|
o.width = 0;
|
|
o.height = 0;
|
|
|
|
o.scissorX = 0;
|
|
o.scissorY = 0;
|
|
o.scissorWidth = o.graphicsDevice.width;
|
|
o.scissorHeight = o.graphicsDevice.height;
|
|
|
|
o.clipOffsetX = -1.0;
|
|
o.clipOffsetY = 1;
|
|
o.clipScaleX = 2.0 / o.graphicsDevice.width;
|
|
o.clipScaleY = -2.0 / o.graphicsDevice.height;
|
|
|
|
o.viewScaleX = 1;
|
|
o.viewScaleY = 1;
|
|
|
|
// GPU Memory.
|
|
// -----------
|
|
|
|
var initial = (params.initialGpuMemory ? params.initialGpuMemory : 0);
|
|
if (initial < 140)
|
|
{
|
|
// 140 = minimum that can be used to draw a single sprite.
|
|
initial = 140;
|
|
}
|
|
if (initial > 2293760)
|
|
{
|
|
// 2293760 = maximum that can ever be used in 16bit indices.
|
|
initial = 2293760;
|
|
}
|
|
|
|
o.performanceData = {
|
|
gpuMemoryUsage : initial,
|
|
minBatchSize : 0,
|
|
maxBatchSize : 0,
|
|
avgBatchSize : 0,
|
|
batchCount : 0,
|
|
dataTransfers : 0
|
|
};
|
|
|
|
o.maxGpuMemory = (params.maxGpuMemory ? params.maxGpuMemory : 2293760);
|
|
if (o.maxGpuMemory < initial)
|
|
{
|
|
o.maxGpuMemory = initial;
|
|
}
|
|
|
|
var initialVertices = Math.floor(initial / 140) * 4;
|
|
o.maxVertices = Math.floor(o.maxGpuMemory / 140) * 4;
|
|
if (o.maxVertices > 65536)
|
|
{
|
|
o.maxVertices = 65536;
|
|
}
|
|
|
|
// number of bytes used per-sprite on cpu vertex buffers.
|
|
o.cpuStride = 64;
|
|
|
|
// vertex buffer is in terms of number of vertices.
|
|
// so we have a stride of 4 rather than 128.
|
|
o.gpuStride = 4;
|
|
|
|
// Index and vertex buffer setup.
|
|
o.vertexBufferParameters = {
|
|
numVertices: initialVertices,
|
|
attributes: [gd.VERTEXFORMAT_FLOAT2, gd.VERTEXFORMAT_FLOAT4, gd.VERTEXFORMAT_FLOAT2],
|
|
'transient': true
|
|
};
|
|
o.vertexBuffer = gd.createVertexBuffer(o.vertexBufferParameters);
|
|
|
|
o.semantics = gd.createSemantics([gd.SEMANTIC_POSITION, gd.SEMANTIC_COLOR, gd.SEMANTIC_TEXCOORD0]);
|
|
o.indexBufferParameters = {
|
|
numIndices: (initialVertices * 1.5),
|
|
format: gd.INDEXFORMAT_USHORT,
|
|
dynamic: false,
|
|
data : o.indexData((initialVertices * 1.5))
|
|
};
|
|
o.indexBuffer = gd.createIndexBuffer(o.indexBufferParameters);
|
|
|
|
// Render Target API
|
|
// -----------------
|
|
|
|
// Objects and values used in render target management.
|
|
o.renderTargetIndex = 0;
|
|
o.renderTargetCount = 0;
|
|
|
|
o.renderTargetTextureParameters = {
|
|
name : '',
|
|
width : 0,
|
|
height : 0,
|
|
depth : 1,
|
|
format : "R8G8B8A8",
|
|
cubemap : false,
|
|
mipmaps : true,
|
|
renderable : true,
|
|
dynamic : true
|
|
};
|
|
|
|
o.renderTargetParams = {
|
|
colorTexture0 : null
|
|
};
|
|
|
|
// Render Target copying.
|
|
// ----------------------
|
|
|
|
// Copy technique for copyRenderTarget
|
|
o.copyTechnique = shader.getTechnique("copy");
|
|
o.copyTechniqueParameters = gd.createTechniqueParameters({
|
|
inputTexture0 : null,
|
|
copyFlip : 1,
|
|
copyUVScale : new Draw2D.prototype.floatArray([1, 1])
|
|
});
|
|
|
|
// Objects used in copyRenderTarget method.
|
|
o.quadSemantics = gd.createSemantics([gd.SEMANTIC_POSITION, gd.SEMANTIC_TEXCOORD0]);
|
|
o.quadPrimitive = gd.PRIMITIVE_TRIANGLE_STRIP;
|
|
|
|
o.copyVertexBufferParams = {
|
|
numVertices: 4,
|
|
attributes: [gd.VERTEXFORMAT_FLOAT2, gd.VERTEXFORMAT_FLOAT2],
|
|
'transient': true
|
|
};
|
|
o.copyVertexBuffer = gd.createVertexBuffer(o.copyVertexBufferParams);
|
|
|
|
// updateRenderTargetVBO
|
|
// ---------------------
|
|
|
|
/*jshint white: false*/
|
|
o.vertexBufferData = new Draw2D.prototype.floatArray([-1.0, -1.0, 0.0, 0.0,
|
|
1.0, -1.0, 1.0, 0.0,
|
|
-1.0, 1.0, 0.0, 1.0,
|
|
1.0, 1.0, 1.0, 1.0]);
|
|
/*jshint white: true*/
|
|
|
|
return o;
|
|
};
|
|
|
|
// Detect correct typed arrays
|
|
(function () {
|
|
Draw2D.prototype.uint16Array = function (arg) {
|
|
if (arguments.length === 0)
|
|
{
|
|
return [];
|
|
}
|
|
|
|
var i, ret;
|
|
if (typeof arg === "number")
|
|
{
|
|
ret = new Array(arg);
|
|
}
|
|
else
|
|
{
|
|
ret = [];
|
|
for (i = 0; i < arg.length; i += 1)
|
|
{
|
|
ret[i] = arg[i];
|
|
}
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
var testArray;
|
|
var textDescriptor;
|
|
|
|
if (typeof Uint16Array !== "undefined")
|
|
{
|
|
testArray = new Uint16Array(4);
|
|
textDescriptor = Object.prototype.toString.call(testArray);
|
|
if (textDescriptor === '[object Uint16Array]')
|
|
{
|
|
Draw2D.prototype.uint16Array = Uint16Array;
|
|
}
|
|
}
|
|
|
|
Draw2D.prototype.floatArray = function (arg) {
|
|
if (arguments.length === 0)
|
|
{
|
|
return [];
|
|
}
|
|
|
|
var i, ret;
|
|
if (typeof arg === "number")
|
|
{
|
|
ret = new Array(arg);
|
|
}
|
|
else
|
|
{
|
|
ret = [];
|
|
for (i = 0; i < arg.length; i += 1)
|
|
{
|
|
ret[i] = arg[i];
|
|
}
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
if (typeof Float32Array !== "undefined")
|
|
{
|
|
testArray = new Float32Array(4);
|
|
textDescriptor = Object.prototype.toString.call(testArray);
|
|
if (textDescriptor === '[object Float32Array]')
|
|
{
|
|
Draw2D.prototype.floatArray = Float32Array;
|
|
Draw2D.prototype.defaultClearColor = new Float32Array(Draw2D.prototype.defaultClearColor);
|
|
}
|
|
}
|
|
}());
|