Commit 9272db0e authored by wjf's avatar wjf

增加webgl优化

parent ac74cb04
This diff is collapsed.
......@@ -386,7 +386,7 @@ export class Entity extends HashObject {
*/
getComponents(clazz: any): any[] {
return this._components.filter((component: any) => {
return typeof clazz === 'string' ? component.constructor.__class__ === clazz : component instanceof clazz;
return typeof clazz === 'string' ? component.constructor.name === clazz : component instanceof clazz;
});
}
......
......@@ -3,8 +3,8 @@
*/
import HashObject from "./HashObject";
import {Entity, } from "./Entity";
import {EngineConfig} from "../engine-config";
import { Entity, } from "./Entity";
import { EngineConfig } from "../engine-config";
const interactiveMap = [
'_dealGlobalTouchBegin',
......@@ -80,36 +80,36 @@ export class ScillaComponent extends HashObject {
/**
* 当组件生效时
*/
onEnable(){
onEnable() {
}
/**
* 当组件失效时
*/
onDisable(){
onDisable() {
}
$onUpdate(t) {
this.onUpdate(t);
if(!this._firstUpdate){
if (!this._firstUpdate) {
this.invokeDelayCallback(t);
}
this._firstUpdate = false;
}
private invokeDelayCallback(t){
private invokeDelayCallback(t) {
const removed = [];
for (let i = 0, li = this.delayCallbacks.length; i < li; i++) {
let {callback, once} = this.delayCallbacks[i];
if(once){
let { callback, once } = this.delayCallbacks[i];
if (once) {
removed.push(i);
}
callback.call(this, t);
}
for(let item of removed){
for (let item of removed) {
this.delayCallbacks.splice(item, 1);
}
}
......@@ -149,14 +149,14 @@ export class ScillaComponent extends HashObject {
* @param key
* @param oldValue
*/
protected onModify(value, key, oldValue){
protected onModify(value, key, oldValue) {
}
private getDelayCallback(callback){
private getDelayCallback(callback) {
let result;
for(let item of this.delayCallbacks){
if(item.callback == callback){
for (let item of this.delayCallbacks) {
if (item.callback == callback) {
result = item;
break;
}
......@@ -172,14 +172,14 @@ export class ScillaComponent extends HashObject {
callOnNextTick(callback, once = true) {
const item = this.getDelayCallback(callback);
if (!item) {
this.delayCallbacks.push({callback, once});
this.delayCallbacks.push({ callback, once });
}
}
cancelOnNextTick(callback){
cancelOnNextTick(callback) {
const item = this.getDelayCallback(callback);
const index = this.delayCallbacks.indexOf(item);
if(index >= 0){
if (index >= 0) {
this.delayCallbacks.splice(index, 1);
}
}
......@@ -233,9 +233,15 @@ export class ScillaComponent extends HashObject {
onGlobalTouchEnd(e) {
}
get transform(){
return this.entity.getComponent('components/base/Transform');
private _transform;
get transform() {
if (!this._transform) {
this._transform = this.entity.getComponent('Transform');
return this._transform
} else {
return this._transform
}
// return this.entity.getComponent('Transform');
}
/**
......
......@@ -4,7 +4,7 @@
import Bounds from "../support/Bounds";
import HashObject from "../core/HashObject";
import {createCanvas} from "./context/RenderContext";
// import {createCanvas} from "./context/RenderContext";
/**
* 纹理类
......@@ -20,14 +20,36 @@ export default class Texture extends HashObject {
this.bounds = new Bounds();
}
/**
* 原图img上的纹理坐标,归一处理,左上角顺时针开始
*/
uvs = [0, 65535, -1, -65536]
/**
* 设置图集中的坐标和尺寸
* @param frame
*/
setFrame(frame) {
let {x, y, w, h} = frame;
let { x, y, w, h } = frame;
this.bounds.setTo(x, y, w, h);
if (!this.img.complete && !(this.img instanceof HTMLCanvasElement)) return
let { width, height } = this.img;
var x0 = x / width;
var y0 = y / height;
var x1 = (x + w) / width;
var y1 = y / height;
var x2 = (x + w) / width;
var y2 = (y + h) / height;
var x3 = x / width;
var y3 = (y + h) / height;
this.uvs[0] = (((y0 * 65535) & 0xFFFF) << 16) | ((x0 * 65535) & 0xFFFF);
this.uvs[1] = (((y1 * 65535) & 0xFFFF) << 16) | ((x1 * 65535) & 0xFFFF);
this.uvs[2] = (((y2 * 65535) & 0xFFFF) << 16) | ((x2 * 65535) & 0xFFFF);
this.uvs[3] = (((y3 * 65535) & 0xFFFF) << 16) | ((x3 * 65535) & 0xFFFF);
}
/**
......@@ -36,6 +58,12 @@ export default class Texture extends HashObject {
*/
setImg(img) {
this.img = img;
//暂时全放img
this.img._glTextures = {};
this.img._enabled = 0;
this.img._virtalBoundId = -1;
this.img.touched = 0;
}
/**
......@@ -56,11 +84,11 @@ export default class Texture extends HashObject {
* 产生一个缓存画布
*/
getCacheCanvas() {
const {width, height} = this.bounds;
const { width, height } = this.bounds;
let canvas = this._cacheCanvas;
if (!canvas) {
canvas = this._cacheCanvas = createCanvas();
canvas = this._cacheCanvas = document.createElement("canvas");
}
canvas.width = width;
canvas.height = height;
......@@ -82,7 +110,7 @@ export default class Texture extends HashObject {
* @param dh
*/
drawToCanvas(context, dx = 0, dy = 0, sx?, sy?, dw?, dh?) {
const {x, y, width, height} = this.bounds;
const { x, y, width, height } = this.bounds;
context.drawImage(this.img, sx || x, sy || y, width, height, dx, dy, dw || width, dh || height);
}
......@@ -99,7 +127,7 @@ export default class Texture extends HashObject {
/**
* 销毁缓存画布
*/
destroyCacheCanvas(){
destroyCacheCanvas() {
this._cacheCanvas = null;
}
}
......@@ -112,7 +140,7 @@ export default class Texture extends HashObject {
export function createTexture(img, frame?): Texture {
const texture = new Texture();
texture.setImg(img);
texture.setFrame(frame || {x: 0, y: 0, w: img.width, h: img.height});
texture.setFrame(frame || { x: 0, y: 0, w: img.width, h: img.height });
return texture;
}
/**
* Created by rockyl on 2018/11/5.
*
* 渲染上下文
*/
import { updateScaleMode } from "./InteractContext";
import Bounds from "../../support/Bounds";
let _canvas, context, width, height;
let scaleX, scaleY, rotation = 0;
let _designWidth, _designHeight, _scaleMode, _modifyCanvasSize;
let shortcutCanvas;
let renderStyle = "canvas"
/**
* 缩放模式
*
* SHOW_ALL: 全可见
* FIXED_WIDTH: 宽度固定
* FIXED_HEIGHT: 高度固定
*/
export const ScaleMode = {
SHOW_ALL: 'showAll',
FIXED_WIDTH: 'fixedWidth',
FIXED_HEIGHT: 'fixedHeight',
};
/**
* 装配上下文
* @param options
*/
export function setupContext(options: any = {}) {
const { canvas, designWidth, designHeight, scaleMode = ScaleMode.SHOW_ALL, modifyCanvasSize = false } = options;
_designWidth = designWidth;
_designHeight = designHeight;
_scaleMode = scaleMode;
_modifyCanvasSize = modifyCanvasSize;
_canvas = canvas;
if (options.renderStyle == "webgl") {
renderStyle = "webgl"
//参数有需要时开放
var option = {
// alpha: true,
antialias: false,
// premultipliedAlpha: true,
stencil: true,
// preserveDrawingBuffer: false,
// powerPreference: false, //双显卡设备参数切换高性能,移动端暂时无用
}
context = canvas.getContext('webgl', option) ||
canvas.getContext('experimental-webgl', option);
if (!context) {
console.log("不支持webgl,回退canvas模式")
renderStyle = "canvas";
context = canvas.getContext('2d');
}
} else {
context = canvas.getContext('2d');
}
updateScaleModeSelf();
}
/**
* 获取渲染模式
*/
export function getRenderStyle() {
return renderStyle
}
/**
* 清空渲染上下文
*/
export function clear() {
context.setTransform(1, 0, 0, 1, 0, 0);
context.clearRect(0, 0, width, height);
}
/**
* 获取渲染上下文
*/
export function getContext() {
return context;
}
/**
* 获取舞台尺寸
*/
export function getStageSize() {
return {
width,
height,
}
}
/**
* 获取舞台缩放
*/
export function getStageScale() {
return {
x: scaleX,
y: scaleY,
}
}
/**
* 获取舞台中心
*/
export function getStageCenter() {
return {
x: width / 2,
y: height / 2,
}
}
/**
* 创建canvas
*/
export function createCanvas() {
return document.createElement('canvas');
}
interface ShortcutParams {
imgType: string;
zoomToDom?: boolean;
quality?: number;
bounds?: Bounds;
}
/**
* 截图
* @param type 目标格式 0:Image, 1:DataURL
* @param params
*/
export async function shortcut(type: number = 0, params: ShortcutParams) {
let targetImg;
if (params.bounds || params.zoomToDom) {
const dataUrl = _canvas.toDataURL('image/png');
const img = await dataUrlToImage(dataUrl);
targetImg = await shortcutWithSize(img, type, params.imgType, params.quality, params.bounds, params.zoomToDom ? scaleX : 1);
} else {
targetImg = _canvas.toDataURL(params.imgType, params.quality);
}
return targetImg;
}
function dataUrlToImage(dataUrl) {
return new Promise((resolve, reject) => {
const image = new Image();
image.onload = function () {
resolve(image);
};
image.onerror = function (e) {
reject(e);
};
image.src = dataUrl;
})
}
async function shortcutWithSize(img, type, imgType, quality?, bounds?: Bounds, scale = 1) {
if (!shortcutCanvas) {
shortcutCanvas = createCanvas();
}
const sx = bounds ? bounds.x || 0 : 0;
const sy = bounds ? bounds.y || 0 : 0;
const sw = bounds ? (bounds.width ? Math.min(bounds.width, width) : width) : width;
const sh = bounds ? (bounds.height ? Math.min(bounds.height, height) : height) : height;
const dw = sw * scale;
const dh = sh * scale;
shortcutCanvas.width = dw;
shortcutCanvas.height = dh;
const context = shortcutCanvas.getContext('2d');
context.drawImage(img, sx, sy, sw, sh, 0, 0, dw, dh);
const dataUrl = shortcutCanvas.toDataURL('image/' + imgType, quality);
switch (type) {
case 0:
return await dataUrlToImage(dataUrl);
case 1:
return dataUrl;
}
}
/**
* 更新缩放模式
*/
function updateScaleModeSelf() {
let parent = _canvas.parentElement;
let containerWidth = parent.clientWidth;
let containerHeight = parent.clientHeight;
const designWidth = _designWidth || containerWidth;
const designHeight = _designHeight || containerHeight;
scaleX = containerWidth / designWidth;
scaleY = containerHeight / designHeight;
switch (_scaleMode) {
case ScaleMode.SHOW_ALL:
width = designWidth;
height = designHeight;
break;
case ScaleMode.FIXED_WIDTH:
width = designWidth;
if (_modifyCanvasSize) {
height = designHeight;
} else {
height = containerHeight / scaleX;
}
scaleY = scaleX;
break;
case ScaleMode.FIXED_HEIGHT:
if (_modifyCanvasSize) {
width = designWidth;
} else {
width = containerWidth / scaleY;
}
height = designHeight;
scaleX = scaleY;
break;
}
updateScaleMode(scaleX, scaleY, rotation);
let styleWidth = _modifyCanvasSize ? designWidth * scaleX : containerWidth;
let styleHeight = _modifyCanvasSize ? designHeight * scaleY : containerHeight;
_canvas.width = width;
_canvas.height = height;
_canvas.style.display = 'block';
_canvas.style.width = styleWidth + 'px';
_canvas.style.height = styleHeight + 'px';
}
......@@ -6,10 +6,14 @@ export {ScillaComponent} from "./ScillaComponent";
export {Entity} from './Entity'
export {Scene} from './Scene'
export {ScillaEvent} from './ScillaEvent'
export {getContext, createCanvas, getStageSize, getStageScale, getStageCenter, shortcut, ScaleMode} from './context/RenderContext';
// export {getContext, createCanvas, getStageSize, getStageScale, getStageCenter, shortcut, ScaleMode} from './context/RenderContext';
export { getContext, getRenderStyle, createCanvas, getStageSize, getStageScale, getStageCenter, shortcut } from './context/RenderContextGL';
export {pagePosToCanvasPos, canvasPosToPagePos} from './context/InteractContext';
export * from './manager'
export {default as Texture, createTexture} from './Texture'
export * from './Sheet'
export * from './FrameAnimation'
\ No newline at end of file
export * from './FrameAnimation'
//导出webgl渲染器
export {default as WebGLRenderer} from "./webgl/WebGLRenderer";
\ No newline at end of file
......@@ -2,12 +2,13 @@
* Created by rockyl on 2018/11/23.
*/
import {Entity, traverse, traversePostorder} from "./Entity";
import {injectProp} from "../tools/utils";
import {setupContext as setupInteractContext} from "./context/InteractContext";
import {clear, ScaleMode, setupContext as setupRenderContext} from "./context/RenderContext";
import { Entity, traverse, traversePostorder } from "./Entity";
import { injectProp } from "../tools/utils";
import { setupContext as setupInteractContext } from "./context/InteractContext";
// import {clear, ScaleMode, setupContext as setupRenderContext} from "./context/RenderContext";
import { clear, getRenderStyle, ScaleMode, setupContext as setupRenderContext } from "./context/RenderContextGL";
import './requestAnimationFrame';
import WebglRenderer from "./webgl/WebGLRenderer";
/**
* 默认配置
*/
......@@ -33,7 +34,7 @@ let lastFPS = 0;
export function setup(_options?) {
injectProp(options, _options);
const {canvas, designWidth, designHeight, scaleMode, modifyCanvasSize, touchEnabled} = options;
const { canvas, designWidth, designHeight, scaleMode, modifyCanvasSize, touchEnabled,renderStyle } = options;
let canvasElement = typeof canvas == 'object' ? canvas : document.getElementById(canvas);
......@@ -53,6 +54,7 @@ export function setup(_options?) {
designHeight,
scaleMode,
modifyCanvasSize,
renderStyle
});
root = new Entity('root');
......@@ -103,7 +105,7 @@ export function getEntityPath(entity?: Entity): string {
* 根据节点路径获取节点
* @param path
*/
export function getEntityByPath(path?: string): Entity{
export function getEntityByPath(path?: string): Entity {
let target = root;
if (path.length > 0) {
......@@ -166,12 +168,17 @@ function flush(tsNow): void {
}
const nextTicks = [];
export function nextTick(func){
export function nextTick(func) {
nextTicks.push(func);
}
function onFrameTick(tsNow) {
clear();
//循环前
if (getRenderStyle() == "webgl") {
WebglRenderer.ins.preRender();
} else {
clear();
}
const tsNow2 = Date.now();
lastFPS = Math.floor(1000 / (tsNow - tsLast));
tsLast = tsNow;
......@@ -188,10 +195,14 @@ function onFrameTick(tsNow) {
});
//const tsPass = Date.now() - tsNow;
while(nextTicks.length > 0){
while (nextTicks.length > 0) {
let func = nextTicks.shift();
func();
}
//循环后
if (getRenderStyle() == "webgl") {
WebglRenderer.ins.postRender()
}
}
/**
......
/**
* Base for a common object renderer that can be used as a system renderer plugin.
*
* @class
*/
export default class ObjectRenderer {
renderer
constructor(renderer) {
this.renderer = renderer
}
/**
* Starts the renderer and sets the shader
*
*/
start() {
// set the shader..
}
/**
* Stops the renderer
*
*/
stop() {
this.flush();
}
/**
* Stub method for rendering content and emptying the current batch.
*
*/
flush() {
// flush!
}
/**
* Renders an object
*
* @param {DisplayObject} object - The object to render.
*/
render(object) // eslint-disable-line no-unused-vars
{
// render the object
}
}
This diff is collapsed.
import mapWebGLBlendModesTo from './utils/mapWebGLBlendModesTo';
const BLEND = 0;
const DEPTH_TEST = 1;
const FRONT_FACE = 2;
const CULL_FACE = 3;
const BLEND_FUNC = 4;
/**
* A WebGL state machines
*
* @class
*/
export default class WebGLState {
activeState: Uint8Array;
defaultState: Uint8Array;
stackIndex: number;
stack: any[];
gl: any;
maxAttribs: any;
attribState: { tempAttribState: any[]; attribState: any[]; };
blendModes: any[];
nativeVaoExtension: any;
/**
* @param {WebGLRenderingContext} gl - The current WebGL rendering context
*/
constructor(gl) {
/**
* The current active state
*
* @member {Uint8Array}
*/
this.activeState = new Uint8Array(16);
/**
* The default state
*
* @member {Uint8Array}
*/
this.defaultState = new Uint8Array(16);
// default blend mode..
this.defaultState[0] = 1;
/**
* The current state index in the stack
*
* @member {number}
* @private
*/
this.stackIndex = 0;
/**
* The stack holding all the different states
*
* @member {Array<*>}
* @private
*/
this.stack = [];
/**
* The current WebGL rendering context
*
* @member {WebGLRenderingContext}
*/
this.gl = gl;
this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
this.attribState = {
tempAttribState: new Array(this.maxAttribs),
attribState: new Array(this.maxAttribs),
};
this.blendModes = mapWebGLBlendModesTo(gl);
// check we have vao..
this.nativeVaoExtension = (
gl.getExtension('OES_vertex_array_object')
|| gl.getExtension('MOZ_OES_vertex_array_object')
|| gl.getExtension('WEBKIT_OES_vertex_array_object')
);
}
/**
* Pushes a new active state
*/
push() {
// next state..
let state = this.stack[this.stackIndex];
if (!state) {
state = this.stack[this.stackIndex] = new Uint8Array(16);
}
++this.stackIndex;
// copy state..
// set active state so we can force overrides of gl state
for (let i = 0; i < this.activeState.length; i++) {
state[i] = this.activeState[i];
}
}
/**
* Pops a state out
*/
pop() {
const state = this.stack[--this.stackIndex];
this.setState(state);
}
/**
* Sets the current state
*
* @param {*} state - The state to set.
*/
setState(state) {
this.setBlend(state[BLEND]);
this.setDepthTest(state[DEPTH_TEST]);
this.setFrontFace(state[FRONT_FACE]);
this.setCullFace(state[CULL_FACE]);
this.setBlendMode(state[BLEND_FUNC]);
}
/**
* Enables or disabled blending.
*
* @param {boolean} value - Turn on or off webgl blending.
*/
setBlend(value) {
value = value ? 1 : 0;
if (this.activeState[BLEND] === value) {
return;
}
this.activeState[BLEND] = value;
this.gl[value ? 'enable' : 'disable'](this.gl.BLEND);
}
/**
* Sets the blend mode.
*
* @param {number} value - The blend mode to set to.
*/
setBlendMode(value) {
if (value === this.activeState[BLEND_FUNC]) {
return;
}
this.activeState[BLEND_FUNC] = value;
const mode = this.blendModes[value];
if (mode.length === 2) {
this.gl.blendFunc(mode[0], mode[1]);
}
else {
this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]);
}
}
/**
* Sets whether to enable or disable depth test.
*
* @param {boolean} value - Turn on or off webgl depth testing.
*/
setDepthTest(value) {
value = value ? 1 : 0;
if (this.activeState[DEPTH_TEST] === value) {
return;
}
this.activeState[DEPTH_TEST] = value;
this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST);
}
/**
* Sets whether to enable or disable cull face.
*
* @param {boolean} value - Turn on or off webgl cull face.
*/
setCullFace(value) {
value = value ? 1 : 0;
if (this.activeState[CULL_FACE] === value) {
return;
}
this.activeState[CULL_FACE] = value;
this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE);
}
/**
* Sets the gl front face.
*
* @param {boolean} value - true is clockwise and false is counter-clockwise
*/
setFrontFace(value) {
value = value ? 1 : 0;
if (this.activeState[FRONT_FACE] === value) {
return;
}
this.activeState[FRONT_FACE] = value;
this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']);
}
/**
* Disables all the vaos in use
*
*/
resetAttributes() {
for (let i = 0; i < this.attribState.tempAttribState.length; i++) {
this.attribState.tempAttribState[i] = 0;
}
for (let i = 0; i < this.attribState.attribState.length; i++) {
this.attribState.attribState[i] = 0;
}
// im going to assume one is always active for performance reasons.
for (let i = 1; i < this.maxAttribs; i++) {
this.gl.disableVertexAttribArray(i);
}
}
// used
/**
* Resets all the logic and disables the vaos
*/
resetToDefault() {
// unbind any VAO if they exist..
if (this.nativeVaoExtension) {
this.nativeVaoExtension.bindVertexArrayOES(null);
}
// reset all attributes..
this.resetAttributes();
// set active state so we can force overrides of gl state
for (let i = 0; i < this.activeState.length; ++i) {
this.activeState[i] = 32;
}
this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false);
this.setState(this.defaultState);
}
}
/**
* Two Pi.
*
* @static
* @constant
* @memberof PIXI
* @type {number}
*/
export const PI_2 = Math.PI * 2;
/**
* Conversion factor for converting radians to degrees.
*
* @static
* @constant
* @memberof PIXI
* @type {number}
*/
export const RAD_TO_DEG = 180 / Math.PI;
/**
* Conversion factor for converting degrees to radians.
*
* @static
* @constant
* @memberof PIXI
* @type {number}
*/
export const DEG_TO_RAD = Math.PI / 180;
/**
* Constant to identify the Renderer Type.
*
* @static
* @constant
* @memberof PIXI
* @name RENDERER_TYPE
* @type {object}
* @property {number} UNKNOWN - Unknown render type.
* @property {number} WEBGL - WebGL render type.
* @property {number} CANVAS - Canvas render type.
*/
export const RENDERER_TYPE = {
UNKNOWN: 0,
WEBGL: 1,
CANVAS: 2,
};
/**
* Various blend modes supported by
*
* IMPORTANT - The WebGL renderer only supports the NORMAL, ADD, MULTIPLY and SCREEN blend modes.
* Anything else will silently act like NORMAL.
*
* @static
* @constant
* @memberof PIXI
* @name BLEND_MODES
* @type {object}
* @property {number} NORMAL
* @property {number} ADD
* @property {number} MULTIPLY
* @property {number} SCREEN
* @property {number} OVERLAY
* @property {number} DARKEN
* @property {number} LIGHTEN
* @property {number} COLOR_DODGE
* @property {number} COLOR_BURN
* @property {number} HARD_LIGHT
* @property {number} SOFT_LIGHT
* @property {number} DIFFERENCE
* @property {number} EXCLUSION
* @property {number} HUE
* @property {number} SATURATION
* @property {number} COLOR
* @property {number} LUMINOSITY
*/
export const BLEND_MODES = {
NORMAL: 0,
ADD: 1,
MULTIPLY: 2,
SCREEN: 3,
OVERLAY: 4,
DARKEN: 5,
LIGHTEN: 6,
COLOR_DODGE: 7,
COLOR_BURN: 8,
HARD_LIGHT: 9,
SOFT_LIGHT: 10,
DIFFERENCE: 11,
EXCLUSION: 12,
HUE: 13,
SATURATION: 14,
COLOR: 15,
LUMINOSITY: 16,
NORMAL_NPM: 17,
ADD_NPM: 18,
SCREEN_NPM: 19,
};
/**
* Various webgl draw modes. These can be used to specify which GL drawMode to use
* under certain situations and renderers.
*
* @static
* @constant
* @memberof PIXI
* @name DRAW_MODES
* @type {object}
* @property {number} POINTS
* @property {number} LINES
* @property {number} LINE_LOOP
* @property {number} LINE_STRIP
* @property {number} TRIANGLES
* @property {number} TRIANGLE_STRIP
* @property {number} TRIANGLE_FAN
*/
export const DRAW_MODES = {
POINTS: 0,
LINES: 1,
LINE_LOOP: 2,
LINE_STRIP: 3,
TRIANGLES: 4,
TRIANGLE_STRIP: 5,
TRIANGLE_FAN: 6,
};
/**
* The scale modes that are supported by
*
* The {@link settings.SCALE_MODE} scale mode affects the default scaling mode of future operations.
* It can be re-assigned to either LINEAR or NEAREST, depending upon suitability.
*
* @static
* @constant
* @memberof PIXI
* @name SCALE_MODES
* @type {object}
* @property {number} LINEAR Smooth scaling
* @property {number} NEAREST Pixelating scaling
*/
export const SCALE_MODES = {
LINEAR: 0,
NEAREST: 1,
};
/**
* The wrap modes that are supported by
*
* The {@link settings.WRAP_MODE} wrap mode affects the default wrapping mode of future operations.
* It can be re-assigned to either CLAMP or REPEAT, depending upon suitability.
* If the texture is non power of two then clamp will be used regardless as webGL can
* only use REPEAT if the texture is po2.
*
* This property only affects WebGL.
*
* @static
* @constant
* @name WRAP_MODES
* @memberof PIXI
* @type {object}
* @property {number} CLAMP - The textures uvs are clamped
* @property {number} REPEAT - The texture uvs tile and repeat
* @property {number} MIRRORED_REPEAT - The texture uvs tile and repeat with mirroring
*/
export const WRAP_MODES = {
CLAMP: 0,
REPEAT: 1,
MIRRORED_REPEAT: 2,
};
/**
* The gc modes that are supported by
*
* The {@link settings.GC_MODE} Garbage Collection mode for PixiJS textures is AUTO
* If set to GC_MODE, the renderer will occasionally check textures usage. If they are not
* used for a specified period of time they will be removed from the GPU. They will of course
* be uploaded again when they are required. This is a silent behind the scenes process that
* should ensure that the GPU does not get filled up.
*
* Handy for mobile devices!
* This property only affects WebGL.
*
* @static
* @constant
* @name GC_MODES
* @memberof PIXI
* @type {object}
* @property {number} AUTO - Garbage collection will happen periodically automatically
* @property {number} MANUAL - Garbage collection will need to be called manually
*/
export const GC_MODES = {
AUTO: 0,
MANUAL: 1,
};
/**
* Regexp for image type by extension.
*
* @static
* @constant
* @memberof PIXI
* @type {RegExp|string}
* @example `image.png`
*/
export const URL_FILE_EXTENSION = /\.(\w{3,4})(?:$|\?|#)/i;
/**
* Regexp for data URI.
* Based on: {@link https://github.com/ragingwind/data-uri-regex}
*
* @static
* @constant
* @name DATA_URI
* @memberof PIXI
* @type {RegExp|string}
* @example data:image/png;base64
*/
export const DATA_URI = /^\s*data:(?:([\w-]+)\/([\w+.-]+))?(?:;charset=([\w-]+))?(?:;(base64))?,(.*)/i;
/**
* Regexp for SVG size.
*
* @static
* @constant
* @name SVG_SIZE
* @memberof PIXI
* @type {RegExp|string}
* @example &lt;svg width="100" height="100"&gt;&lt;/svg&gt;
*/
export const SVG_SIZE = /<svg[^>]*(?:\s(width|height)=('|")(\d*(?:\.\d+)?)(?:px)?('|"))[^>]*(?:\s(width|height)=('|")(\d*(?:\.\d+)?)(?:px)?('|"))[^>]*>/i; // eslint-disable-line max-len
/**
* Constants that identify shapes, mainly to prevent `instanceof` calls.
*
* @static
* @constant
* @name SHAPES
* @memberof PIXI
* @type {object}
* @property {number} POLY Polygon
* @property {number} RECT Rectangle
* @property {number} CIRC Circle
* @property {number} ELIP Ellipse
* @property {number} RREC Rounded Rectangle
*/
export const SHAPES = {
POLY: 0,
RECT: 1,
CIRC: 2,
ELIP: 3,
RREC: 4,
};
/**
* Constants that specify float precision in shaders.
*
* @static
* @constant
* @name PRECISION
* @memberof PIXI
* @type {object}
* @property {string} LOW='lowp'
* @property {string} MEDIUM='mediump'
* @property {string} HIGH='highp'
*/
export const PRECISION = {
LOW: 'lowp',
MEDIUM: 'mediump',
HIGH: 'highp',
};
/**
* Constants that specify the transform type.
*
* @static
* @constant
* @name TRANSFORM_MODE
* @memberof PIXI
* @type {object}
* @property {number} STATIC
* @property {number} DYNAMIC
*/
export const TRANSFORM_MODE = {
STATIC: 0,
DYNAMIC: 1,
};
/**
* Constants that define the type of gradient on text.
*
* @static
* @constant
* @name TEXT_GRADIENT
* @memberof PIXI
* @type {object}
* @property {number} LINEAR_VERTICAL Vertical gradient
* @property {number} LINEAR_HORIZONTAL Linear gradient
*/
export const TEXT_GRADIENT = {
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
/**
* Represents the update priorities used by internal PIXI classes when registered with
* the {@link ticker.Ticker} object. Higher priority items are updated first and lower
* priority items, such as render, should go later.
*
* @static
* @constant
* @name UPDATE_PRIORITY
* @memberof PIXI
* @type {object}
* @property {number} INTERACTION=50 Highest priority, used for {@link interaction.InteractionManager}
* @property {number} HIGH=25 High priority updating, {@link VideoBaseTexture} and {@link extras.AnimatedSprite}
* @property {number} NORMAL=0 Default priority for ticker events, see {@link ticker.Ticker#add}.
* @property {number} LOW=-25 Low priority used for {@link Application} rendering.
* @property {number} UTILITY=-50 Lowest priority used for {@link prepare.BasePrepare} utility.
*/
export const UPDATE_PRIORITY = {
INTERACTION: 50,
HIGH: 25,
NORMAL: 0,
LOW: -25,
UTILITY: -50,
};
export class BatchBuffer {
vertices: ArrayBuffer;
float32View: Float32Array;
uint32View: Uint32Array;
positions: any;
uvs: any;
colors: any;
/**
* @param {number} size - The size of the buffer in bytes.
*/
constructor(size) {
this.vertices = new ArrayBuffer(size);
/**
* View on the vertices as a Float32Array for positions
*
* @member {Float32Array}
*/
this.float32View = new Float32Array(this.vertices);
/**
* View on the vertices as a Uint32Array for uvs
*
* @member {Float32Array}
*/
this.uint32View = new Uint32Array(this.vertices);
}
/**
* Destroys the buffer.
*
*/
public destroy() {
this.vertices = null;
this.positions = null;
this.uvs = null;
this.colors = null;
}
}
var EMPTY_ARRAY_BUFFER = new ArrayBuffer(0);
/**
* Helper class to create a webGL buffer
*
* @class
* @memberof glCore
* @param gl {WebGLRenderingContext} The current WebGL rendering context
* @param type {gl.ARRAY_BUFFER | gl.ELEMENT_ARRAY_BUFFER} @mat
* @param data {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} an array of data
* @param drawType {gl.STATIC_DRAW|gl.DYNAMIC_DRAW|gl.STREAM_DRAW}
*/
export class GLBuffer {
gl: any;
buffer: any;
type: any;
drawType: any;
data: ArrayBuffer;
_updateID: number;
constructor(gl, type?, data?, drawType?) {
/**
* The current WebGL rendering context
*
* @member {WebGLRenderingContext}
*/
this.gl = gl;
/**
* The WebGL buffer, created upon instantiation
*
* @member {WebGLBuffer}
*/
this.buffer = gl.createBuffer();
/**
* The type of the buffer
*
* @member {gl.ARRAY_BUFFER|gl.ELEMENT_ARRAY_BUFFER}
*/
this.type = type || gl.ARRAY_BUFFER;
/**
* The draw type of the buffer
*
* @member {gl.STATIC_DRAW|gl.DYNAMIC_DRAW|gl.STREAM_DRAW}
*/
this.drawType = drawType || gl.STATIC_DRAW;
/**
* The data in the buffer, as a typed array
*
* @member {ArrayBuffer| SharedArrayBuffer|ArrayBufferView}
*/
this.data = EMPTY_ARRAY_BUFFER;
if (data) {
this.upload(data);
}
this._updateID = 0;
};
/**
* Uploads the buffer to the GPU
* @param data {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} an array of data to upload
* @param offset {Number} if only a subset of the data should be uploaded, this is the amount of data to subtract
* @param dontBind {Boolean} whether to bind the buffer before uploading it
*/
public upload(data, offset?, dontBind?) {
// todo - needed?
if (!dontBind) this.bind();
var gl = this.gl;
data = data || this.data;
offset = offset || 0;
if (this.data.byteLength >= data.byteLength) {
gl.bufferSubData(this.type, offset, data);
}
else {
gl.bufferData(this.type, data, this.drawType);
}
this.data = data;
};
/**
* Binds the buffer
*
*/
public bind() {
var gl = this.gl;
gl.bindBuffer(this.type, this.buffer);
};
/**
* Destroys the buffer
*
*/
public destroy = function () {
this.gl.deleteBuffer(this.buffer);
};
public static createVertexBuffer(gl, data?, drawType?) {
return new GLBuffer(gl, gl.ARRAY_BUFFER, data, drawType);
};
public static createIndexBuffer(gl, data?, drawType?) {
return new GLBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, data, drawType);
};
public static create(gl, type, data, drawType) {
return new GLBuffer(gl, type, data, drawType);
};
}
import { GLTexture } from './GLTexture';
/**
* Helper class to create a webGL Framebuffer
*
* @class
* @memberof glCore
* @param gl {WebGLRenderingContext} The current WebGL rendering context
* @param width {Number} the width of the drawing area of the frame buffer
* @param height {Number} the height of the drawing area of the frame buffer
*/
export class GLFramebuffer {
gl: any;
framebuffer: any;
stencil: any;
texture: any;
width: any;
height: any;
constructor(gl, width, height) {
/**
* The current WebGL rendering context
*
* @member {WebGLRenderingContext}
*/
this.gl = gl;
/**
* The frame buffer
*
* @member {WebGLFramebuffer}
*/
this.framebuffer = gl.createFramebuffer();
/**
* The stencil buffer
*
* @member {WebGLRenderbuffer}
*/
this.stencil = null;
/**
* The stencil buffer
*
* @member {glCore.GLTexture}
*/
this.texture = null;
/**
* The width of the drawing area of the buffer
*
* @member {Number}
*/
this.width = width || 100;
/**
* The height of the drawing area of the buffer
*
* @member {Number}
*/
this.height = height || 100;
};
/**
* Adds a texture to the frame buffer
* @param texture {glCore.GLTexture}
*/
public enableTexture(texture) {
var gl = this.gl;
this.texture = texture || new GLTexture(gl);
this.texture.bind();
//gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
this.bind();
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture.texture, 0);
};
/**
* Initialises the stencil buffer
*/
public enableStencil() {
if (this.stencil) return;
var gl = this.gl;
this.stencil = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil);
// TODO.. this is depth AND stencil?
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, this.stencil);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, this.width, this.height);
};
/**
* Erases the drawing area and fills it with a colour
* @param r {Number} the red value of the clearing colour
* @param g {Number} the green value of the clearing colour
* @param b {Number} the blue value of the clearing colour
* @param a {Number} the alpha value of the clearing colour
*/
public clear(r, g, b, a) {
this.bind();
var gl = this.gl;
gl.clearColor(r, g, b, a);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
};
/**
* Binds the frame buffer to the WebGL context
*/
public bind() {
var gl = this.gl;
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
};
/**
* Unbinds the frame buffer to the WebGL context
*/
public unbind() {
var gl = this.gl;
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
};
/**
* Resizes the drawing area of the buffer to the given width and height
* @param width {Number} the new width
* @param height {Number} the new height
*/
public resize(width, height) {
var gl = this.gl;
this.width = width;
this.height = height;
if (this.texture) {
this.texture.uploadData(null, width, height);
}
if (this.stencil) {
// update the stencil buffer width and height
gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height);
}
};
/**
* Destroys this buffer
*/
public destroy() {
var gl = this.gl;
//TODO
if (this.texture) {
this.texture.destroy();
}
gl.deleteFramebuffer(this.framebuffer);
this.gl = null;
this.stencil = null;
this.texture = null;
};
/**
* Creates a frame buffer with a texture containing the given data
* @static
* @param gl {WebGLRenderingContext} The current WebGL rendering context
* @param width {Number} the width of the drawing area of the frame buffer
* @param height {Number} the height of the drawing area of the frame buffer
* @param data {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} an array of data
*/
public static createRGBA(gl, width, height, data?) {
var texture = GLTexture.fromData(gl, null, width, height);
texture.enableNearestScaling();
texture.enableWrapClamp();
//now create the framebuffer object and attach the texture to it.
var fbo = new GLFramebuffer(gl, width, height);
fbo.enableTexture(texture);
//fbo.enableStencil(); // get this back on soon!
//fbo.enableStencil(); // get this back on soon!
fbo.unbind();
return fbo;
};
/**
* Creates a frame buffer with a texture containing the given data
* @static
* @param gl {WebGLRenderingContext} The current WebGL rendering context
* @param width {Number} the width of the drawing area of the frame buffer
* @param height {Number} the height of the drawing area of the frame buffer
* @param data {ArrayBuffer| SharedArrayBuffer|ArrayBufferView} an array of data
*/
public static createFloat32(gl, width, height, data) {
// create a new texture..
var texture = GLTexture.fromData(gl, data, width, height);
texture.enableNearestScaling();
texture.enableWrapClamp();
//now create the framebuffer object and attach the texture to it.
var fbo = new GLFramebuffer(gl, width, height);
fbo.enableTexture(texture);
fbo.unbind();
return fbo;
};
}
import { compileProgram } from './shader/compileProgram';
import { extractAttributes } from './shader/extractAttributes';
import { extractUniforms } from './shader/extractUniforms';
import { setPrecision } from './shader/setPrecision';
import { generateUniformAccessObject } from './shader/generateUniformAccessObject';
/**
* Helper class to create a webGL Shader
*
* @class
* @memberof glCore
* @param gl {WebGLRenderingContext}
* @param vertexSrc {string|string[]} The vertex shader source as an array of strings.
* @param fragmentSrc {string|string[]} The fragment shader source as an array of strings.
* @param precision {string} The float precision of the shader. Options are 'lowp', 'mediump' or 'highp'.
* @param attributeLocations {object} A key value pair showing which location eact attribute should sit eg {position:0, uvs:1}
*/
export class GLShader {
gl: any;
program: any;
attributes: {};
uniformData: {};
uniforms: { data: {}; };
constructor(gl, vertexSrc, fragmentSrc, precision?, attributeLocations?) {
/**
* The current WebGL rendering context
*
* @member {WebGLRenderingContext}
*/
this.gl = gl;
if (precision) {
vertexSrc = setPrecision(vertexSrc, precision);
fragmentSrc = setPrecision(fragmentSrc, precision);
}
/**
* The shader program
*
* @member {WebGLProgram}
*/
// First compile the program..
this.program = compileProgram(gl, vertexSrc, fragmentSrc, attributeLocations);
/**
* The attributes of the shader as an object containing the following properties
* {
* type,
* size,
* location,
* pointer
* }
* @member {Object}
*/
// next extract the attributes
this.attributes = extractAttributes(gl, this.program);
this.uniformData = extractUniforms(gl, this.program);
/**
* The uniforms of the shader as an object containing the following properties
* {
* gl,
* data
* }
* @member {Object}
*/
this.uniforms = generateUniformAccessObject(gl, this.uniformData);
};
/**
* Uses this shader
*
* @return {glCore.GLShader} Returns itself.
*/
public bind() {
this.gl.useProgram(this.program);
return this;
};
/**
* Destroys this shader
* TODO
*/
public destroy() {
this.attributes = null;
this.uniformData = null;
this.uniforms = null;
var gl = this.gl;
gl.deleteProgram(this.program);
};
}
/**
* Helper class to create a WebGL Texture
*
* @class
* @memberof glCore
* @param gl {WebGLRenderingContext} The current WebGL context
* @param width {number} the width of the texture
* @param height {number} the height of the texture
* @param format {number} the pixel format of the texture. defaults to gl.RGBA
* @param type {number} the gl type of the texture. defaults to gl.UNSIGNED_BYTE
*/
export class GLTexture {
gl: any;
texture: any;
mipmap: boolean;
premultiplyAlpha: boolean;
width: any;
height: any;
format: any;
type: any;
constructor(gl, width?, height?, format?, type?) {
/**
* The current WebGL rendering context
*
* @member {WebGLRenderingContext}
*/
this.gl = gl;
/**
* The WebGL texture
*
* @member {WebGLTexture}
*/
this.texture = gl.createTexture();
/**
* If mipmapping was used for this texture, enable and disable with enableMipmap()
*
* @member {Boolean}
*/
// some settings..
this.mipmap = false;
/**
* Set to true to enable pre-multiplied alpha
*
* @member {Boolean}
*/
this.premultiplyAlpha = false;
/**
* The width of texture
*
* @member {Number}
*/
this.width = width || -1;
/**
* The height of texture
*
* @member {Number}
*/
this.height = height || -1;
/**
* The pixel format of the texture. defaults to gl.RGBA
*
* @member {Number}
*/
this.format = format || gl.RGBA;
/**
* The gl type of the texture. defaults to gl.UNSIGNED_BYTE
*
* @member {Number}
*/
this.type = type || gl.UNSIGNED_BYTE;
};
/**
* Uploads this texture to the GPU
* @param source {HTMLImageElement|ImageData|HTMLVideoElement} the source image of the texture
*/
public upload(source) {
this.bind();
var gl = this.gl;
//设置是否对纹理进行预乘透明通道
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this.premultiplyAlpha);
var newWidth = source.videoWidth || source.width;
var newHeight = source.videoHeight || source.height;
if (newHeight !== this.height || newWidth !== this.width) {
gl.texImage2D(gl.TEXTURE_2D, 0, this.format, this.format, this.type, source);
}
else {
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, this.format, this.type, source);
}
// if the source is a video, we need to use the videoWidth / videoHeight properties as width / height will be incorrect.
this.width = newWidth;
this.height = newHeight;
};
/**
* Use a data source and uploads this texture to the GPU
* @param data {TypedArray} the data to upload to the texture
* @param width {number} the new width of the texture
* @param height {number} the new height of the texture
*/
public uploadData = function (data, width, height) {
this.bind();
var gl = this.gl;
if (data instanceof Float32Array) {
if (!FLOATING_POINT_AVAILABLE) {
var ext = gl.getExtension("OES_texture_float");
if (ext) {
FLOATING_POINT_AVAILABLE = true;
}
else {
throw new Error('floating point textures not available');
}
}
this.type = gl.FLOAT;
}
else {
// TODO support for other types
this.type = this.type || gl.UNSIGNED_BYTE;
}
// what type of data?
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this.premultiplyAlpha);
if (width !== this.width || height !== this.height) {
gl.texImage2D(gl.TEXTURE_2D, 0, this.format, width, height, 0, this.format, this.type, data || null);
}
else {
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, this.format, this.type, data || null);
}
this.width = width;
this.height = height;
// texSubImage2D
};
/**
* Binds the texture
* @param location
*/
public bind(location?) {
var gl = this.gl;
if (location !== undefined) {
gl.activeTexture(gl.TEXTURE0 + location);
}
gl.bindTexture(gl.TEXTURE_2D, this.texture);
};
/**
* Unbinds the texture
*/
public unbind() {
var gl = this.gl;
gl.bindTexture(gl.TEXTURE_2D, null);
};
/**
* @param linear {Boolean} if we want to use linear filtering or nearest neighbour interpolation
*/
public minFilter(linear) {
var gl = this.gl;
this.bind();
if (this.mipmap) {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, linear ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST);
}
else {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, linear ? gl.LINEAR : gl.NEAREST);
}
};
/**
* @param linear {Boolean} if we want to use linear filtering or nearest neighbour interpolation
*/
public magFilter(linear) {
var gl = this.gl;
this.bind();
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, linear ? gl.LINEAR : gl.NEAREST);
};
/**
* Enables mipmapping
*/
public enableMipmap() {
var gl = this.gl;
this.bind();
this.mipmap = true;
gl.generateMipmap(gl.TEXTURE_2D);
};
/**
* Enables linear filtering
*/
public enableLinearScaling() {
this.minFilter(true);
this.magFilter(true);
};
/**
* Enables nearest neighbour interpolation
*/
public enableNearestScaling() {
this.minFilter(false);
this.magFilter(false);
};
/**
* Enables clamping on the texture so WebGL will not repeat it
*/
public enableWrapClamp() {
var gl = this.gl;
this.bind();
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
};
/**
* Enable tiling on the texture
*/
public enableWrapRepeat() {
var gl = this.gl;
this.bind();
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
};
public enableWrapMirrorRepeat() {
var gl = this.gl;
this.bind();
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT);
};
/**
* Destroys this texture
*/
public destroy() {
var gl = this.gl;
//TODO
gl.deleteTexture(this.texture);
};
/**
* @static
* @param gl {WebGLRenderingContext} The current WebGL context
* @param source {HTMLImageElement|ImageData} the source image of the texture
* @param premultiplyAlpha {Boolean} If we want to use pre-multiplied alpha
*/
public static fromSource(gl, source, premultiplyAlpha?) {
var texture = new GLTexture(gl);
texture.premultiplyAlpha = premultiplyAlpha || false;
texture.upload(source);
return texture;
};
/**
* @static
* @param gl {WebGLRenderingContext} The current WebGL context
* @param data {TypedArray} the data to upload to the texture
* @param width {number} the new width of the texture
* @param height {number} the new height of the texture
*/
public static fromData(gl, data, width, height) {
//console.log(data, width, height);
var texture = new GLTexture(gl);
texture.uploadData(data, width, height);
return texture;
};
}
var FLOATING_POINT_AVAILABLE = false;
// import GroupD8 from '../math/GroupD8';
/**
* A standard object to store the Uvs of a texture
*
* @class
* @private
*/
export class TextureUvs
{
x0: number;
y0: number;
x1: number;
y1: number;
x2: number;
y2: number;
x3: number;
y3: number;
uvsUint32: Uint32Array;
/**
*
*/
constructor()
{
this.x0 = 0;
this.y0 = 0;
this.x1 = 1;
this.y1 = 0;
this.x2 = 1;
this.y2 = 1;
this.x3 = 0;
this.y3 = 1;
this.uvsUint32 = new Uint32Array(4);
this.uvsUint32[0] = (((this.y0 * 65535) & 0xFFFF) << 16) | ((this.x0 * 65535) & 0xFFFF);
this.uvsUint32[1] = (((this.y1 * 65535) & 0xFFFF) << 16) | ((this.x1 * 65535) & 0xFFFF);
this.uvsUint32[2] = (((this.y2 * 65535) & 0xFFFF) << 16) | ((this.x2 * 65535) & 0xFFFF);
this.uvsUint32[3] = (((this.y3 * 65535) & 0xFFFF) << 16) | ((this.x3 * 65535) & 0xFFFF);
}
/**
* Sets the texture Uvs based on the given frame information.
*
* @private
* @param {Rectangle} frame - The frame of the texture
* @param {Rectangle} baseFrame - The base frame of the texture
* @param {number} rotate - Rotation of frame, see {@link GroupD8}
*/
set(frame, baseFrame, rotate?)
{
const tw = baseFrame.width;
const th = baseFrame.height;
// if (rotate)
// {
// // width and height div 2 div baseFrame size
// const w2 = frame.width / 2 / tw;
// const h2 = frame.height / 2 / th;
// // coordinates of center
// const cX = (frame.x / tw) + w2;
// const cY = (frame.y / th) + h2;
// rotate = GroupD8.add(rotate, GroupD8.NW); // NW is top-left corner
// this.x0 = cX + (w2 * GroupD8.uX(rotate));
// this.y0 = cY + (h2 * GroupD8.uY(rotate));
// rotate = GroupD8.add(rotate, 2); // rotate 90 degrees clockwise
// this.x1 = cX + (w2 * GroupD8.uX(rotate));
// this.y1 = cY + (h2 * GroupD8.uY(rotate));
// rotate = GroupD8.add(rotate, 2);
// this.x2 = cX + (w2 * GroupD8.uX(rotate));
// this.y2 = cY + (h2 * GroupD8.uY(rotate));
// rotate = GroupD8.add(rotate, 2);
// this.x3 = cX + (w2 * GroupD8.uX(rotate));
// this.y3 = cY + (h2 * GroupD8.uY(rotate));
// }
// else
// {
this.x0 = frame.x / tw;
this.y0 = frame.y / th;
this.x1 = (frame.x + frame.width) / tw;
this.y1 = frame.y / th;
this.x2 = (frame.x + frame.width) / tw;
this.y2 = (frame.y + frame.height) / th;
this.x3 = frame.x / tw;
this.y3 = (frame.y + frame.height) / th;
// }
this.uvsUint32[0] = (((this.y0 * 65535) & 0xFFFF) << 16) | ((this.x0 * 65535) & 0xFFFF);
this.uvsUint32[1] = (((this.y1 * 65535) & 0xFFFF) << 16) | ((this.x1 * 65535) & 0xFFFF);
this.uvsUint32[2] = (((this.y2 * 65535) & 0xFFFF) << 16) | ((this.x2 * 65535) & 0xFFFF);
this.uvsUint32[3] = (((this.y3 * 65535) & 0xFFFF) << 16) | ((this.x3 * 65535) & 0xFFFF);
}
}
// state object//
import { setVertexAttribArrays } from './setVertexAttribArrays';
/**
* Helper class to work with WebGL VertexArrayObjects (vaos)
* Only works if WebGL extensions are enabled (they usually are)
*
* @class
* @memberof glCore
* @param gl {WebGLRenderingContext} The current WebGL rendering context
*/
export class VertexArrayObject {
nativeVaoExtension: any;
nativeState: any;
nativeVao: any;
gl: any;
attributes: any[];
indexBuffer: any;
dirty: boolean;
constructor(gl, state) {
this.nativeVaoExtension = null;
if (!VertexArrayObject.FORCE_NATIVE) {
this.nativeVaoExtension = gl.getExtension('OES_vertex_array_object') ||
gl.getExtension('MOZ_OES_vertex_array_object') ||
gl.getExtension('WEBKIT_OES_vertex_array_object');
}
this.nativeState = state;
if (this.nativeVaoExtension) {
this.nativeVao = this.nativeVaoExtension.createVertexArrayOES();
var maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
// VAO - overwrite the state..
this.nativeState = {
tempAttribState: new Array(maxAttribs),
attribState: new Array(maxAttribs)
};
}
/**
* The current WebGL rendering context
*
* @member {WebGLRenderingContext}
*/
this.gl = gl;
/**
* An array of attributes
*
* @member {Array}
*/
this.attributes = [];
/**
* @member {glCore.GLBuffer}
*/
this.indexBuffer = null;
/**
* A boolean flag
*
* @member {Boolean}
*/
this.dirty = false;
};
/**
* Binds the buffer
*/
public bind() {
if (this.nativeVao) {
this.nativeVaoExtension.bindVertexArrayOES(this.nativeVao);
if (this.dirty) {
this.dirty = false;
this.activate();
return this;
}
if (this.indexBuffer) {
this.indexBuffer.bind();
}
}
else {
this.activate();
}
return this;
};
/**
* Unbinds the buffer
*/
public unbind() {
if (this.nativeVao) {
this.nativeVaoExtension.bindVertexArrayOES(null);
}
return this;
};
/**
* Uses this vao
*/
public activate() {
var gl = this.gl;
var lastBuffer = null;
for (var i = 0; i < this.attributes.length; i++) {
var attrib = this.attributes[i];
if (lastBuffer !== attrib.buffer) {
attrib.buffer.bind();
lastBuffer = attrib.buffer;
}
gl.vertexAttribPointer(attrib.attribute.location,
attrib.attribute.size,
attrib.type || gl.FLOAT,
attrib.normalized || false,
attrib.stride || 0,
attrib.start || 0);
}
setVertexAttribArrays(gl, this.attributes, this.nativeState);
if (this.indexBuffer) {
this.indexBuffer.bind();
}
return this;
};
/**
*
* @param buffer {gl.GLBuffer}
* @param attribute {*}
* @param type {String}
* @param normalized {Boolean}
* @param stride {Number}
* @param start {Number}
*/
public addAttribute(buffer, attribute, type?, normalized?, stride?, start?) {
this.attributes.push({
buffer: buffer,
attribute: attribute,
location: attribute.location,
type: type || this.gl.FLOAT,
normalized: normalized || false,
stride: stride || 0,
start: start || 0
});
this.dirty = true;
return this;
};
/**
*
* @param buffer {gl.GLBuffer}
*/
public addIndex(buffer/*, options*/) {
this.indexBuffer = buffer;
this.dirty = true;
return this;
};
/**
* Unbinds this vao and disables it
*/
public clear() {
// var gl = this.gl;
// TODO - should this function unbind after clear?
// for now, no but lets see what happens in the real world!
if (this.nativeVao) {
this.nativeVaoExtension.bindVertexArrayOES(this.nativeVao);
}
this.attributes.length = 0;
this.indexBuffer = null;
return this;
};
/**
* @param type {Number}
* @param size {Number}
* @param start {Number}
*/
public draw(type, size?, start?) {
var gl = this.gl;
if (this.indexBuffer) {
gl.drawElements(type, size || this.indexBuffer.data.length, gl.UNSIGNED_SHORT, (start || 0) * 2);
}
else {
// TODO need a better way to calculate size..
gl.drawArrays(type, start || 0, size || this.getSize());
}
return this;
};
/**
* Destroy this vao
*/
public destroy() {
// lose references
this.gl = null;
this.indexBuffer = null;
this.attributes = null;
this.nativeState = null;
if (this.nativeVao) {
this.nativeVaoExtension.deleteVertexArrayOES(this.nativeVao);
}
this.nativeVaoExtension = null;
this.nativeVao = null;
};
public getSize() {
var attrib = this.attributes[0];
return attrib.buffer.data.length / ((attrib.stride / 4) || attrib.attribute.size);
};
/**
* Some devices behave a bit funny when using the newer extensions (im looking at you ipad 2!)
* If you find on older devices that things have gone a bit weird then set this to true.
*/
/**
* Lets the VAO know if you should use the WebGL extension or the native methods.
* Some devices behave a bit funny when using the newer extensions (im looking at you ipad 2!)
* If you find on older devices that things have gone a bit weird then set this to true.
* @static
* @property {Boolean} FORCE_NATIVE
*/
public static FORCE_NATIVE = false;
}
import { createContext } from './createContext';
const fragTemplate = [
'precision mediump float;',
'void main(void){',
'float test = 0.1;',
'%forloop%',
'gl_FragColor = vec4(0.0);',
'}',
].join('\n');
export function checkMaxIfStatmentsInShader(maxIfs, gl) {
const createTempContext = !gl;
// @if DEBUG
if (maxIfs === 0) {
throw new Error('Invalid value of `0` passed to `checkMaxIfStatementsInShader`');
}
// @endif
if (createTempContext) {
const tinyCanvas = document.createElement('canvas');
tinyCanvas.width = 1;
tinyCanvas.height = 1;
gl = createContext(tinyCanvas);
}
const shader = gl.createShader(gl.FRAGMENT_SHADER);
while (true) // eslint-disable-line no-constant-condition
{
const fragmentSrc = fragTemplate.replace(/%forloop%/gi, generateIfTestSrc(maxIfs));
gl.shaderSource(shader, fragmentSrc);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
maxIfs = (maxIfs / 2) | 0;
}
else {
// valid!
break;
}
}
if (createTempContext) {
// get rid of context
if (gl.getExtension('WEBGL_lose_context')) {
gl.getExtension('WEBGL_lose_context').loseContext();
}
}
return maxIfs;
}
function generateIfTestSrc(maxIfs) {
let src = '';
for (let i = 0; i < maxIfs; ++i) {
if (i > 0) {
src += '\nelse ';
}
if (i < maxIfs - 1) {
src += `if(test == ${i}.0){}`;
}
}
return src;
}
/**
* Helper class to create a webGL Context
*
* @class
* @memberof glCore
* @param canvas {HTMLCanvasElement} the canvas element that we will get the context from
* @param options {Object} An options object that gets passed in to the canvas element containing the context attributes,
* see https://developer.mozilla.org/en/docs/Web/API/HTMLCanvasElement/getContext for the options available
* @return {WebGLRenderingContext} the WebGL context
*/
export function createContext(canvas, options?) {
var gl = canvas.getContext('webgl', options) ||
canvas.getContext('experimental-webgl', options);
if (!gl) {
// fail, not able to get a context
throw new Error('This browser does not support webGL. Try using the canvas renderer');
}
return gl;
};
\ No newline at end of file
/**
* Generic Mask Stack data structure
*
* @function createIndicesForQuads
* @private
* @param {number} size - Number of quads
* @return {Uint16Array} indices
*/
export function createIndicesForQuads(size)
{
// the total number of indices in our array, there are 6 points per quad.
const totalIndices = size * 6;
const indices = new Uint16Array(totalIndices);
// fill the indices with the quads to draw
for (let i = 0, j = 0; i < totalIndices; i += 6, j += 4)
{
indices[i + 0] = j + 0;
indices[i + 1] = j + 1;
indices[i + 2] = j + 2;
indices[i + 3] = j + 0;
indices[i + 4] = j + 2;
indices[i + 5] = j + 3;
}
return indices;
}
import { GLShader } from './GLShader';
//顶点着色器程序
const VSHADER_SOURCE =
" precision highp float;" +
"attribute vec2 aVertexPosition;" +
"attribute vec2 aTextureCoord;" +
"attribute vec4 aColor;" +
"attribute float aTextureId;" +
"uniform mat3 projectionMatrix;" +
"uniform mat3 modelMatrix;" +
"varying vec2 vTextureCoord;" +
"varying vec4 vColor;" +
"varying float vTextureId;" +
"void main(void){" +
"gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);" +
// "gl_Position = vec4((projectionMatrix *modelMatrix* vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);" +
"vTextureCoord = aTextureCoord;" +
"vTextureId = aTextureId;" +
"vColor = aColor;" +
"}";
const fragTemplate = [
'precision mediump float;',
'varying vec2 vTextureCoord;',
'varying vec4 vColor;',
'varying float vTextureId;',
'uniform sampler2D uSamplers[%count%];',
'void main(void){',
'vec4 color;',
'float textureId = floor(vTextureId+0.5);',
'%forloop%',
'gl_FragColor = color * vColor;',
'}',
].join('\n');
export function generateMultiTextureShader(gl, maxTextures) {
// const vertexSrc = readFileSync(join(__dirname, './texture.vert'), 'utf8');
let fragmentSrc = fragTemplate;
fragmentSrc = fragmentSrc.replace(/%count%/gi, maxTextures);
fragmentSrc = fragmentSrc.replace(/%forloop%/gi, generateSampleSrc(maxTextures));
// console.log(fragmentSrc)
const shader = new GLShader(gl, VSHADER_SOURCE, fragmentSrc);
const sampleValues = [];
for (let i = 0; i < maxTextures; i++) {
sampleValues[i] = i;
}
shader.bind();
shader.uniforms["uSamplers"] = sampleValues;
// console.log(fragmentSrc)
return shader;
}
function generateSampleSrc(maxTextures) {
let src = '';
src += '\n';
src += '\n';
for (let i = 0; i < maxTextures; i++) {
if (i > 0) {
src += '\nelse ';
}
if (i < maxTextures - 1) {
src += `if(textureId == ${i}.0)`;
}
src += '\n{';
src += `\n\tcolor = texture2D(uSamplers[${i}], vTextureCoord);`;
src += '\n}';
}
src += '\n';
src += '\n';
return src;
}
// export { default as GroupD8 } from './GroupD8';
export { GLTexture } from './GLTexture';
export { GLBuffer } from './GLBuffer';
export { VertexArrayObject } from './VertexArrayObject';
export { GLFramebuffer } from "./GLFramebuffer";
export { GLShader } from "./GLShader";
export { TextureUvs } from "./TextureUvs";
export { checkMaxIfStatmentsInShader } from "./checkMaxIfStatmentsInShader"
// var GL_MAP = {};
/**
* @param gl {WebGLRenderingContext} The current WebGL context
* @param attribs {*}
* @param state {*}
*/
export function setVertexAttribArrays(gl, attribs, state?) {
var i;
if (state) {
var tempAttribState = state.tempAttribState,
attribState = state.attribState;
for (i = 0; i < tempAttribState.length; i++) {
tempAttribState[i] = false;
}
// set the new attribs
for (i = 0; i < attribs.length; i++) {
tempAttribState[attribs[i].attribute.location] = true;
}
for (i = 0; i < attribState.length; i++) {
if (attribState[i] !== tempAttribState[i]) {
attribState[i] = tempAttribState[i];
if (state.attribState[i]) {
gl.enableVertexAttribArray(i);
}
else {
gl.disableVertexAttribArray(i);
}
}
}
}
else {
for (i = 0; i < attribs.length; i++) {
var attrib = attribs[i];
gl.enableVertexAttribArray(attrib.attribute.location);
}
}
};
\ No newline at end of file
/**
* @class
* @memberof glCore.shader
* @param gl {WebGLRenderingContext} The current WebGL context {WebGLProgram}
* @param vertexSrc {string|string[]} The vertex shader source as an array of strings.
* @param fragmentSrc {string|string[]} The fragment shader source as an array of strings.
* @param attributeLocations {Object} An attribute location map that lets you manually set the attribute locations
* @return {WebGLProgram} the shader program
*/
export function compileProgram(gl, vertexSrc, fragmentSrc, attributeLocations?) {
var glVertShader = compileShader(gl, gl.VERTEX_SHADER, vertexSrc);
var glFragShader = compileShader(gl, gl.FRAGMENT_SHADER, fragmentSrc);
var program = gl.createProgram();
gl.attachShader(program, glVertShader);
gl.attachShader(program, glFragShader);
// optionally, set the attributes manually for the program rather than letting WebGL decide..
if (attributeLocations) {
for (var i in attributeLocations) {
gl.bindAttribLocation(program, attributeLocations[i], i);
}
}
gl.linkProgram(program);
// if linking fails, then log and cleanup
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error('Error: Could not initialize shader.');
console.error('gl.VALIDATE_STATUS', gl.getProgramParameter(program, gl.VALIDATE_STATUS));
console.error('gl.getError()', gl.getError());
// if there is a program info log, log it
if (gl.getProgramInfoLog(program) !== '') {
console.warn('Warning: gl.getProgramInfoLog()', gl.getProgramInfoLog(program));
}
gl.deleteProgram(program);
program = null;
}
// clean up some shaders
gl.deleteShader(glVertShader);
gl.deleteShader(glFragShader);
return program;
};
/**
* @private
* @param gl {WebGLRenderingContext} The current WebGL context {WebGLProgram}
* @param type {Number} the type, can be either VERTEX_SHADER or FRAGMENT_SHADER
* @param vertexSrc {string|string[]} The vertex shader source as an array of strings.
* @return {WebGLShader} the shader
*/
var compileShader = function (gl, type, src) {
var shader = gl.createShader(type);
gl.shaderSource(shader, src);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.log(gl.getShaderInfoLog(shader));
return null;
}
return shader;
};
/**
* @class
* @memberof glCore.shader
* @param type {String} Type of value
* @param size {Number}
*/
export function defaultValue(type, size) {
switch (type) {
case 'float':
return 0;
case 'vec2':
return new Float32Array(2 * size);
case 'vec3':
return new Float32Array(3 * size);
case 'vec4':
return new Float32Array(4 * size);
case 'int':
case 'sampler2D':
return 0;
case 'ivec2':
return new Int32Array(2 * size);
case 'ivec3':
return new Int32Array(3 * size);
case 'ivec4':
return new Int32Array(4 * size);
case 'bool':
return false;
case 'bvec2':
return booleanArray(2 * size);
case 'bvec3':
return booleanArray(3 * size);
case 'bvec4':
return booleanArray(4 * size);
case 'mat2':
return new Float32Array([1, 0,
0, 1]);
case 'mat3':
return new Float32Array([1, 0, 0,
0, 1, 0,
0, 0, 1]);
case 'mat4':
return new Float32Array([1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1]);
}
};
var booleanArray = function (size) {
var array = new Array(size);
for (var i = 0; i < array.length; i++) {
array[i] = false;
}
return array;
};
import { mapType } from './mapType';
import { mapSize } from './mapSize';
/**
* Extracts the attributes
* @class
* @memberof glCore.shader
* @param gl {WebGLRenderingContext} The current WebGL rendering context
* @param program {WebGLProgram} The shader program to get the attributes from
* @return attributes {Object}
*/
export function extractAttributes(gl, program) {
var attributes = {};
var totalAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
for (var i = 0; i < totalAttributes; i++) {
var attribData = gl.getActiveAttrib(program, i);
var type = mapType(gl, attribData.type);
attributes[attribData.name] = {
type: type,
size: mapSize(type),
location: gl.getAttribLocation(program, attribData.name),
//TODO - make an attribute object
pointer: function (type = gl.FLOAT, normalized = false, stride = 0, start = 0) {
// console.log(this.location)
gl.vertexAttribPointer(this.location, this.size, type, normalized, stride, start);
}
};
}
return attributes;
};
import { mapType } from './mapType';
import { defaultValue } from './defaultValue';
/**
* Extracts the uniforms
* @class
* @memberof glCore.shader
* @param gl {WebGLRenderingContext} The current WebGL rendering context
* @param program {WebGLProgram} The shader program to get the uniforms from
* @return uniforms {Object}
*/
export function extractUniforms(gl, program) {
var uniforms = {};
var totalUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
for (var i = 0; i < totalUniforms; i++) {
var uniformData = gl.getActiveUniform(program, i);
var name = uniformData.name.replace(/\[.*?\]/, "");
var type = mapType(gl, uniformData.type);
uniforms[name] = {
type: type,
size: uniformData.size,
location: gl.getUniformLocation(program, name),
value: defaultValue(type, uniformData.size)
};
}
return uniforms;
};
/**
* Extracts the attributes
* @class
* @memberof glCore.shader
* @param gl {WebGLRenderingContext} The current WebGL rendering context
* @param uniforms {Array} @mat ?
* @return attributes {Object}
*/
export function generateUniformAccessObject(gl, uniformData) {
// this is the object we will be sending back.
// an object hierachy will be created for structs
var uniforms = { data: {} };
uniforms["gl"] = gl;
var uniformKeys = Object.keys(uniformData);
for (var i = 0; i < uniformKeys.length; i++) {
var fullName = uniformKeys[i];
var nameTokens = fullName.split('.');
var name = nameTokens[nameTokens.length - 1];
var uniformGroup = getUniformGroup(nameTokens, uniforms);
var uniform = uniformData[fullName];
uniformGroup.data[name] = uniform;
uniformGroup.gl = gl;
Object.defineProperty(uniformGroup, name, {
get: generateGetter(name),
set: generateSetter(name, uniform)
});
}
return uniforms;
};
var generateGetter = function (name) {
return function () {
return this.data[name].value;
};
};
var GLSL_SINGLE_SETTERS = {
float: function setSingleFloat(gl, location, value) { gl.uniform1f(location, value); },
vec2: function setSingleVec2(gl, location, value) { gl.uniform2f(location, value[0], value[1]); },
vec3: function setSingleVec3(gl, location, value) { gl.uniform3f(location, value[0], value[1], value[2]); },
vec4: function setSingleVec4(gl, location, value) { gl.uniform4f(location, value[0], value[1], value[2], value[3]); },
int: function setSingleInt(gl, location, value) { gl.uniform1i(location, value); },
ivec2: function setSingleIvec2(gl, location, value) { gl.uniform2i(location, value[0], value[1]); },
ivec3: function setSingleIvec3(gl, location, value) { gl.uniform3i(location, value[0], value[1], value[2]); },
ivec4: function setSingleIvec4(gl, location, value) { gl.uniform4i(location, value[0], value[1], value[2], value[3]); },
bool: function setSingleBool(gl, location, value) { gl.uniform1i(location, value); },
bvec2: function setSingleBvec2(gl, location, value) { gl.uniform2i(location, value[0], value[1]); },
bvec3: function setSingleBvec3(gl, location, value) { gl.uniform3i(location, value[0], value[1], value[2]); },
bvec4: function setSingleBvec4(gl, location, value) { gl.uniform4i(location, value[0], value[1], value[2], value[3]); },
mat2: function setSingleMat2(gl, location, value) { gl.uniformMatrix2fv(location, false, value); },
mat3: function setSingleMat3(gl, location, value) { gl.uniformMatrix3fv(location, false, value); },
mat4: function setSingleMat4(gl, location, value) { gl.uniformMatrix4fv(location, false, value); },
sampler2D: function setSingleSampler2D(gl, location, value) { gl.uniform1i(location, value); },
};
var GLSL_ARRAY_SETTERS = {
float: function setFloatArray(gl, location, value) { gl.uniform1fv(location, value); },
vec2: function setVec2Array(gl, location, value) { gl.uniform2fv(location, value); },
vec3: function setVec3Array(gl, location, value) { gl.uniform3fv(location, value); },
vec4: function setVec4Array(gl, location, value) { gl.uniform4fv(location, value); },
int: function setIntArray(gl, location, value) { gl.uniform1iv(location, value); },
ivec2: function setIvec2Array(gl, location, value) { gl.uniform2iv(location, value); },
ivec3: function setIvec3Array(gl, location, value) { gl.uniform3iv(location, value); },
ivec4: function setIvec4Array(gl, location, value) { gl.uniform4iv(location, value); },
bool: function setBoolArray(gl, location, value) { gl.uniform1iv(location, value); },
bvec2: function setBvec2Array(gl, location, value) { gl.uniform2iv(location, value); },
bvec3: function setBvec3Array(gl, location, value) { gl.uniform3iv(location, value); },
bvec4: function setBvec4Array(gl, location, value) { gl.uniform4iv(location, value); },
sampler2D: function setSampler2DArray(gl, location, value) { gl.uniform1iv(location, value); },
};
function generateSetter(name, uniform) {
return function (value) {
this.data[name].value = value;
var location = this.data[name].location;
if (uniform.size === 1) {
GLSL_SINGLE_SETTERS[uniform.type](this.gl, location, value);
}
else {
// glslSetArray(gl, location, type, value) {
GLSL_ARRAY_SETTERS[uniform.type](this.gl, location, value);
}
};
}
function getUniformGroup(nameTokens, uniform) {
var cur = uniform;
for (var i = 0; i < nameTokens.length - 1; i++) {
var o = cur[nameTokens[i]] || { data: {} };
cur[nameTokens[i]] = o;
cur = o;
}
return cur;
}
export { compileProgram } from './compileProgram';
export { defaultValue } from './defaultValue';
export { extractAttributes } from './extractAttributes';
export { extractUniforms } from './extractUniforms';
export { generateUniformAccessObject } from './generateUniformAccessObject';
export { setPrecision } from './setPrecision';
export { mapSize } from './mapSize';
export { mapType } from './mapType';
/**
* @class
* @memberof glCore.shader
* @param type {String}
* @return {Number}
*/
export function mapSize(type) {
return GLSL_TO_SIZE[type];
};
var GLSL_TO_SIZE = {
'float': 1,
'vec2': 2,
'vec3': 3,
'vec4': 4,
'int': 1,
'ivec2': 2,
'ivec3': 3,
'ivec4': 4,
'bool': 1,
'bvec2': 2,
'bvec3': 3,
'bvec4': 4,
'mat2': 4,
'mat3': 9,
'mat4': 16,
'sampler2D': 1
};
\ No newline at end of file
export function mapType(gl, type) {
if (!GL_TABLE) {
var typeNames = Object.keys(GL_TO_GLSL_TYPES);
GL_TABLE = {};
for (var i = 0; i < typeNames.length; ++i) {
var tn = typeNames[i];
GL_TABLE[gl[tn]] = GL_TO_GLSL_TYPES[tn];
}
}
return GL_TABLE[type];
};
var GL_TABLE = null;
var GL_TO_GLSL_TYPES = {
'FLOAT': 'float',
'FLOAT_VEC2': 'vec2',
'FLOAT_VEC3': 'vec3',
'FLOAT_VEC4': 'vec4',
'INT': 'int',
'INT_VEC2': 'ivec2',
'INT_VEC3': 'ivec3',
'INT_VEC4': 'ivec4',
'BOOL': 'bool',
'BOOL_VEC2': 'bvec2',
'BOOL_VEC3': 'bvec3',
'BOOL_VEC4': 'bvec4',
'FLOAT_MAT2': 'mat2',
'FLOAT_MAT3': 'mat3',
'FLOAT_MAT4': 'mat4',
'SAMPLER_2D': 'sampler2D'
};
/**
* Sets the float precision on the shader. If the precision is already present this function will do nothing
* @param {string} src the shader source
* @param {string} precision The float precision of the shader. Options are 'lowp', 'mediump' or 'highp'.
*
* @return {string} modified shader source
*/
export function setPrecision(src, precision) {
if (src.substring(0, 9) !== 'precision') {
return 'precision ' + precision + ' float;\n' + src;
}
return src;
};
\ No newline at end of file
// import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter';
/**
* @class
*/
export default class MaskManager {
renderer: any;
scissor: boolean;
scissorData: any;
scissorRenderTarget: any;
enableScissor: boolean;
/**
* @param {WebGLRenderer} renderer - The renderer this manager works for.
*/
constructor(renderer) {
this.renderer = renderer
// TODO - we don't need both!
this.scissor = false;
this.scissorData = null;
this.scissorRenderTarget = null;
this.enableScissor = true;
// this.alphaMaskPool = [];
// this.alphaMaskIndex = 0;
}
/**
* Applies the Mask and adds it to the current filter stack.
*
* @param {DisplayObject} target - Display Object to push the mask to
* @param {Sprite|Graphics} maskData - The masking data.
*/
pushMask(target, maskData) {
// TODO the root check means scissor rect will not
// be used on render textures more info here:
if (this.enableScissor
&& !this.scissor
&& this.renderer._activeRenderTarget.root
&& !this.renderer.stencilManager.stencilMaskStack.length
&& maskData.maskType == "aaaa") {//Scissor还有问题,再处理
const matrix = maskData.transform.getMatrix();
let rot = Math.atan2(matrix.b, matrix.a);
// use the nearest degree!
rot = Math.round(rot * (180 / Math.PI));
if (rot % 90) {
this.pushStencilMask(maskData);
}
else {
this.pushScissorMask(target, maskData);
}
}
else {
this.pushStencilMask(maskData);
}
}
/**
* Removes the last mask from the mask stack and doesn't return it.
*
* @param {DisplayObject} target - Display Object to pop the mask from
* @param {Sprite|Graphics} maskData - The masking data.
*/
popMask(target, maskData) {
if (this.enableScissor && !this.renderer.stencilManager.stencilMaskStack.length) {
this.popScissorMask(target, maskData);
}
else {
this.popStencilMask(target, maskData);
}
}
/**
* Applies the Mask and adds it to the current filter stack.
*
* @param {RenderTarget} target - Display Object to push the sprite mask to
* @param {Sprite} maskData - Sprite to be used as the mask
*/
// pushSpriteMask(target, maskData)
// {
// let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex];
// if (!alphaMaskFilter)
// {
// alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)];
// }
// alphaMaskFilter[0].resolution = this.renderer.resolution;
// alphaMaskFilter[0].maskSprite = maskData;
// // TODO - may cause issues!
// target.filterArea = maskData.getBounds(true);
// this.renderer.filterManager.pushFilter(target, alphaMaskFilter);
// this.alphaMaskIndex++;
// }
/**
* Removes the last filter from the filter stack and doesn't return it.
*
*/
// popSpriteMask()
// {
// this.renderer.filterManager.popFilter();
// this.alphaMaskIndex--;
// }
/**
* Applies the Mask and adds it to the current filter stack.
*
* @param {Sprite|Graphics} maskData - The masking data.
*/
pushStencilMask(maskData) {
this.renderer.currentRenderer.stop();
this.renderer.stencilManager.pushStencil(maskData);
}
/**
* Removes the last filter from the filter stack and doesn't return it.
*
*/
popStencilMask(target, maskData) {
this.renderer.currentRenderer.stop();
this.renderer.stencilManager.popStencil();
}
/**
*
* @param {DisplayObject} target - Display Object to push the mask to
* @param {Graphics} maskData - The masking data.
*/
pushScissorMask(target, maskData) {
maskData.renderable = true;
const renderTarget = this.renderer._activeRenderTarget;
const bounds = maskData.scissorMaskBounds();
this.fit(bounds,renderTarget.size);
this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST);
this.renderer.gl.scissor(
bounds.x,
(renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y),
bounds.width,
bounds.height
);
this.scissorRenderTarget = renderTarget;
this.scissorData = maskData;
this.scissor = true;
}
/**
*
*
*/
popScissorMask(target, maskData) {
// this.scissorRenderTarget = null;
// this.scissorData = null;
this.scissor = false;
// must be scissor!
const gl = this.renderer.gl;
gl.disable(gl.SCISSOR_TEST);
}
destroy() {
}
fit(bounds,rectangle) {
if (bounds.x < rectangle.x) {
bounds.width += bounds.x;
if (bounds.width < 0) {
bounds.width = 0;
}
bounds.x = rectangle.x;
}
if (bounds.y < rectangle.y) {
bounds.height += bounds.y;
if (bounds.height < 0) {
bounds.height = 0;
}
bounds.y = rectangle.y;
}
if (bounds.x + bounds.width > rectangle.x + rectangle.width) {
bounds.width = rectangle.width - bounds.x;
if (bounds.width < 0) {
bounds.width = 0;
}
}
if (bounds.y + bounds.height > rectangle.y + rectangle.height) {
bounds.height = rectangle.height - bounds.y;
if (bounds.height < 0) {
bounds.height = 0;
}
}
}
}
/**
* @class
*/
export default class StencilManager {
renderer: any;
stencilMaskStack: any;
/**
* @param {WebGLRenderer} renderer - The renderer this manager works for.
*/
constructor(renderer) {
this.renderer = renderer
this.stencilMaskStack = null;
}
/**
* Changes the mask stack that is used by this manager.
*
* @param {Graphics[]} stencilMaskStack - The mask stack
*/
setMaskStack(stencilMaskStack) {
this.stencilMaskStack = stencilMaskStack;
const gl = this.renderer.gl;
if (stencilMaskStack.length === 0) {
gl.disable(gl.STENCIL_TEST);
}
else {
gl.enable(gl.STENCIL_TEST);
}
}
/**
* Applies the Mask and adds it to the current stencil stack. @alvin
*
* @param {Graphics} graphics - The mask
*/
pushStencil(graphics) {
this.renderer.setObjectRenderer(this.renderer.plugins.graphics);
this.renderer._activeRenderTarget.attachStencilBuffer();
const gl = this.renderer.gl;
const prevMaskCount = this.stencilMaskStack.length;
if (prevMaskCount === 0) {
gl.enable(gl.STENCIL_TEST);
}
this.stencilMaskStack.push(graphics);
// Increment the refference stencil value where the new mask overlaps with the old ones.
if(graphics.maskVisible){
//如果显示遮罩
gl.colorMask(true, true, true, true);
}else{
gl.colorMask(false, false, false, false);
}
// gl.colorMask(true, true, true, true);
gl.stencilFunc(gl.EQUAL, prevMaskCount, this._getBitwiseMask());
gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR);
this.renderer.plugins.graphics.render(graphics);
this._useCurrent();
}
/**
* Removes the last mask from the stencil stack. @alvin
*/
popStencil() {
this.renderer.setObjectRenderer(this.renderer.plugins.graphics);
const gl = this.renderer.gl;
const graphics = this.stencilMaskStack.pop();
if (this.stencilMaskStack.length === 0) {
// the stack is empty!
gl.disable(gl.STENCIL_TEST);
gl.clear(gl.STENCIL_BUFFER_BIT);
gl.clearStencil(0);
}
else {
// Decrement the refference stencil value where the popped mask overlaps with the other ones
gl.colorMask(false, false, false, false);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR);
this.renderer.plugins.graphics.render(graphics);
this._useCurrent();
}
}
/**
* Setup renderer to use the current stencil data.
*/
_useCurrent() {
const gl = this.renderer.gl;
gl.colorMask(true, true, true, true);
gl.stencilFunc(gl.EQUAL, this.stencilMaskStack.length, this._getBitwiseMask());
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
}
/**
* Fill 1s equal to the number of acitve stencil masks.
*
* @return {number} The bitwise mask.
*/
_getBitwiseMask() {
return (1 << this.stencilMaskStack.length) - 1;
}
/**
* Destroys the mask stack.
*
*/
destroy() {
this.renderer = null;
this.stencilMaskStack.stencilStack = null;
}
}
import { GC_MODES } from '../const';
import settings from '../settings';
/**
* TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged
* up with textures that are no longer being used.
*
* @class
* @memberof PIXI
*/
export default class TextureGarbageCollector {
renderer: any;
count: number;
checkCount: number;
maxIdle: number;
checkCountMax: number;
mode: number;
/**
* @param {WebGLRenderer} renderer - The renderer this manager works for.
*/
constructor(renderer) {
this.renderer = renderer;
this.count = 0;
this.checkCount = 0;
this.maxIdle = settings.GC_MAX_IDLE;
this.checkCountMax = settings.GC_MAX_CHECK_COUNT;
this.mode = settings.GC_MODE;
}
/**
* Checks to see when the last time a texture was used
* if the texture has not been used for a specified amount of time it will be removed from the GPU
*/
update() {
this.count++;
if (this.mode === GC_MODES.MANUAL) {
return;
}
this.checkCount++;
if (this.checkCount > this.checkCountMax) {
this.checkCount = 0;
this.run();
}
}
/**
* Checks to see when the last time a texture was used
* if the texture has not been used for a specified amount of time it will be removed from the GPU
*/
run() {
const tm = this.renderer.textureManager;
const managedTextures = tm._managedTextures;
let wasRemoved = false;
for (let i = 0; i < managedTextures.length; i++) {
const texture = managedTextures[i];
// only supports non generated textures at the moment!
if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) {
tm.destroyTexture(texture, true);
managedTextures[i] = null;
wasRemoved = true;
}
}
if (wasRemoved) {
let j = 0;
for (let i = 0; i < managedTextures.length; i++) {
if (managedTextures[i] !== null) {
managedTextures[j++] = managedTextures[i];
}
}
managedTextures.length = j;
}
}
/**
* Removes all the textures within the specified displayObject and its children from the GPU
*
* @param {DisplayObject} displayObject - the displayObject to remove the textures from.
*/
unload(displayObject) {
const tm = this.renderer.textureManager;
// only destroy non generated textures
if (displayObject._texture && displayObject._texture._glRenderTargets) {
tm.destroyTexture(displayObject._texture, true);
}
for (let i = displayObject.children.length - 1; i >= 0; i--) {
this.unload(displayObject.children[i]);
}
}
}
import { GLTexture } from '../glCore';
import { WRAP_MODES, SCALE_MODES } from '../const';
import RenderTarget from '../utils/RenderTarget';
import removeItems from '../utils/removeItems';
import {isPow2} from "../utils"
/**
* Helper class to create a webGL Texture
*
* @class
*/
export default class TextureManager {
renderer: any;
gl: any;
_managedTextures: any[];
/**
* @param {WebGLRenderer} renderer - A reference to the current renderer
*/
constructor(renderer) {
/**
* A reference to the current renderer
*
* @member {PIXI.WebGLRenderer}
*/
this.renderer = renderer;
/**
* The current WebGL rendering context
*
* @member {WebGLRenderingContext}
*/
this.gl = renderer.gl;
/**
* Track textures in the renderer so we can no longer listen to them on destruction.
*
* @member {Array<*>}
* @private
*/
this._managedTextures = [];
}
/**
* Binds a texture.
*
*/
bindTexture() {
// empty
}
/**
* Gets a texture.
*
*/
getTexture() {
// empty
}
/**
* Updates and/or Creates a WebGL texture for the renderer's context.
*
* @param {PIXI.BaseTexture|PIXI.Texture} texture - the texture to update
* @param {number} location - the location the texture will be bound to.
* @return {GLTexture} The gl texture.
*/
updateTexture(texture, location) {
// assume it good!
// texture = texture.baseTexture || texture;
const gl = this.gl;
const isRenderTexture = !!texture._glRenderTargets;
// if (!texture.hasLoaded) {
// return null;
// }
if(!texture.complete&&!(texture instanceof HTMLCanvasElement)){
return null;
}
const boundTextures = this.renderer.boundTextures;
// if the location is undefined then this may have been called by n event.
// this being the case the texture may already be bound to a slot. As a texture can only be bound once
// we need to find its current location if it exists.
if (location === undefined) {
location = 0;
// TODO maybe we can use texture bound ids later on...
// check if texture is already bound..
for (let i = 0; i < boundTextures.length; ++i) {
if (boundTextures[i] === texture) {
location = i;
break;
}
}
}
boundTextures[location] = texture;
gl.activeTexture(gl.TEXTURE0 + location);
let glTexture = texture._glTextures[this.renderer.CONTEXT_UID];
if (!glTexture) {
// if (isRenderTexture) {
// const renderTarget = new RenderTarget(
// this.gl,
// texture.width,
// texture.height,
// texture.scaleMode,
// texture.resolution
// );
// renderTarget.resize(texture.width, texture.height);
// texture._glRenderTargets[this.renderer.CONTEXT_UID] = renderTarget;
// glTexture = renderTarget.texture;
// }
// else {
glTexture = new GLTexture(this.gl, null, null, null, null);
glTexture.bind(location);
glTexture.premultiplyAlpha = true;//必设,否则带透明度的纹理渲染会出问题
// glTexture.upload(texture.source);
glTexture.upload(texture);
// }
texture._glTextures[this.renderer.CONTEXT_UID] = glTexture;
// texture.on('update', this.updateTexture, this);
// texture.on('dispose', this.destroyTexture, this);
this._managedTextures.push(texture);
if (isPow2(texture.width)&&isPow2(texture.height)) {
// if (texture.mipmap) {
// glTexture.enableMipmap();
// }
// if (texture.wrapMode === WRAP_MODES.CLAMP) {
// glTexture.enableWrapClamp();
// }
// else if (texture.wrapMode === WRAP_MODES.REPEAT) {
// glTexture.enableWrapRepeat();
// }
// else {
// glTexture.enableWrapMirrorRepeat();
// }
glTexture.enableMipmap();
glTexture.enableWrapClamp();
}
else {
glTexture.enableWrapClamp();
}
// if (texture.scaleMode === SCALE_MODES.NEAREST) {
// glTexture.enableNearestScaling();
// }
// else {
glTexture.enableLinearScaling();
// }
}
// the texture already exists so we only need to update it..
// else if (isRenderTexture) {
// texture._glRenderTargets[this.renderer.CONTEXT_UID].resize(texture.width, texture.height);
// }
else {
glTexture.upload(texture);
}
return glTexture;
}
/**
* Deletes the texture from WebGL
*
* @param {PIXI.BaseTexture|PIXI.Texture} texture - the texture to destroy
* @param {boolean} [skipRemove=false] - Whether to skip removing the texture from the TextureManager.
*/
destroyTexture(texture, skipRemove) {
texture = texture.baseTexture || texture;
if (!texture.hasLoaded) {
return;
}
const uid = this.renderer.CONTEXT_UID;
const glTextures = texture._glTextures;
const glRenderTargets = texture._glRenderTargets;
if (glTextures[uid]) {
this.renderer.unbindTexture(texture);
glTextures[uid].destroy();
// texture.off('update', this.updateTexture, this);
// texture.off('dispose', this.destroyTexture, this);
delete glTextures[uid];
if (!skipRemove) {
const i = this._managedTextures.indexOf(texture);
if (i !== -1) {
removeItems(this._managedTextures, i, 1);
}
}
}
if (glRenderTargets && glRenderTargets[uid]) {
glRenderTargets[uid].destroy();
delete glRenderTargets[uid];
}
}
/**
* Deletes all the textures from WebGL
*/
removeAll() {
// empty all the old gl textures as they are useless now
for (let i = 0; i < this._managedTextures.length; ++i) {
const texture = this._managedTextures[i];
if (texture._glTextures[this.renderer.CONTEXT_UID]) {
delete texture._glTextures[this.renderer.CONTEXT_UID];
}
}
}
/**
* Destroys this manager and removes all its textures
*/
destroy() {
// destroy managed textures
for (let i = 0; i < this._managedTextures.length; ++i) {
const texture = this._managedTextures[i];
this.destroyTexture(texture, true);
// texture.off('update', this.updateTexture, this);
// texture.off('dispose', this.destroyTexture, this);
}
this._managedTextures = null;
}
}
This diff is collapsed.
This diff is collapsed.
import Shader from './Shader';
/**
* This shader is used to draw simple primitive shapes for {@link Graphics}.
*
* @class
* @extends Shader
*/
export default class PrimitiveShader extends Shader {
/**
* 注释掉的aColor暂时不需要,不考虑混色,简单粗暴颜色和透明度
* @param {WebGLRenderingContext} gl - The webgl shader manager this shader works for.
*/
constructor(gl) {
super(gl,
// vertex shader
[
'attribute vec2 aVertexPosition;',
// 'attribute vec4 aColor;',
'uniform mat3 translationMatrix;',
'uniform mat3 projectionMatrix;',
'uniform float alpha;',
'uniform vec3 tint;',
'varying vec4 vColor;',
'void main(void){',
' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);',
// ' vColor = aColor * vec4(tint * alpha, alpha);',
'vColor = vec4(tint * alpha, alpha);',//考虑和vColor = vec4(tint,alpha)的区别
'}',
].join('\n'),
// fragment shader
[
'varying vec4 vColor;',
'void main(void){',
' gl_FragColor = vColor;',
// ' gl_FragColor = vec4(1.0,0.0,0.0,1.0);',
'}',
].join('\n')
);
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
export default function canUploadSameBuffer() {
// Uploading the same buffer multiple times in a single frame can cause perf issues.
// Apparent on IOS so only check for that at the moment
// this check may become more complex if this issue pops up elsewhere.
const ios = !!navigator.platform && (/iPad|iPhone|iPod/).test(navigator.platform);
return !ios;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
export declare function checkMaxIfStatmentsInShader(maxIfs: any, gl: any): any;
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
export declare function mapType(gl: any, type: any): any;
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment