@awayjs/stage
Version:
Stage for AwayJS
597 lines (595 loc) • 23.4 kB
JavaScript
import { __extends } from "tslib";
import { EventDispatcher, Rectangle, CSS, UUID } from '@awayjs/core';
import { ContextMode } from './base/ContextMode';
import { ContextGLMipFilter } from './base/ContextGLMipFilter';
import { ContextGLTextureFilter } from './base/ContextGLTextureFilter';
import { ContextGLWrapMode } from './base/ContextGLWrapMode';
import { ContextGLProfile } from './base/ContextGLProfile';
import { StageEvent } from './events/StageEvent';
import { ProgramDataPool } from './image/ProgramDataPool';
import { ContextWebGL } from './webgl/ContextWebGL';
import { ContextGLClearMask } from './base/ContextGLClearMask';
import { UnloadService } from './managers/UnloadManager';
import { ImageUtils } from './utils/ImageUtils';
import { TouchPoint } from './base/TouchPoint';
import { FilterManager } from './managers/FilterManager';
import { BUFFER_FORMATS_MAP } from './utils/BufferFormat';
var TMP_POINT = { x: 0, y: 0 };
/**
* Stage provides a proxy class to handle the creation and attachment of the Context
* (and in turn the back buffer) it uses. Stage should never be created directly,
* but requested through StageManager.
*
* @see away.managers.StageManager
*
*/
var Stage = /** @class */ (function (_super) {
__extends(Stage, _super);
function Stage(container, stageIndex, stageManager, forceSoftware, profile) {
if (forceSoftware === void 0) { forceSoftware = false; }
if (profile === void 0) { profile = ContextGLProfile.BASELINE; }
var _this = _super.call(this) || this;
_this._programData = new Array();
_this._x = 0;
_this._y = 0;
_this._touchPoints = new Array();
//private static _frameEventDriver:Shape = new Shape(); // TODO: add frame driver/request animation frame
_this._stageIndex = -1;
_this._antiAlias = 4;
_this.savedTargetStack = [];
_this._activeTargetConf = {
mipSelector: 0,
surfaceSelector: 0,
target: null,
depthStencil: true,
msaa: true
};
_this._frameEndCallbackOnce = [];
_this._initialised = false;
_this.globalDisableMipmap = false;
_this.globalDisableSmooth = false;
_this.numUsedStreams = 0;
_this.numUsedTextures = 0;
_this.id = UUID.Next();
_this._programDataPool = new ProgramDataPool(_this);
_this._container = container;
if (_this._container) {
_this._container.addEventListener('webglcontextlost', function (event) { return _this.onContextLost(event); });
_this._container.addEventListener('webglcontextrestored', function (event) { return _this.onContextRestored(event); });
}
_this._stageIndex = stageIndex;
_this._stageManager = stageManager;
CSS.setElementX(_this._container, 0);
CSS.setElementY(_this._container, 0);
_this._width = _this._container.clientWidth;
_this._height = _this._container.clientHeight;
_this.visible = true;
_this.filterManager = new FilterManager(_this);
return _this;
}
Object.defineProperty(Stage.prototype, "pixelRatio", {
get: function () {
return this._context.pixelRatio;
},
set: function (v) {
this._context.pixelRatio = v;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Stage.prototype, "glVersion", {
get: function () {
return this._context.glVersion;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Stage.prototype, "screenX", {
get: function () {
return this._screenX;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Stage.prototype, "screenY", {
get: function () {
return this._screenY;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Stage.prototype, "touchPoints", {
get: function () {
return this._touchPoints;
},
enumerable: false,
configurable: true
});
// for avoid using a Dispather, because it has big overhead
Stage.prototype.requiestFrameEnd = function (func) {
if (this._frameEndCallbackOnce.indexOf(func) > -1 || typeof func !== 'function') {
return;
}
this._frameEndCallbackOnce.push(func);
};
/**
* @description Should be executed AFTER rendering process
*/
Stage.prototype.present = function () {
this._context.present();
UnloadService.executeAll();
for (var _i = 0, _a = this._frameEndCallbackOnce; _i < _a.length; _i++) {
var call = _a[_i];
call();
}
this._frameEndCallbackOnce.length = 0;
};
Stage.prototype.getProgramData = function (vertexString, fragmentString) {
return this._programDataPool.getItem(vertexString, fragmentString);
};
/**
* Safe current target config to stack
*/
Stage.prototype.pushRenderTargetConfig = function () {
this.savedTargetStack.push(Object.assign({}, this._activeTargetConf));
};
/**
* Pop render target config and apply it, used for deep caching
*/
Stage.prototype.popRenderTarget = function () {
if (this.savedTargetStack.length === 0)
return;
var _a = this.savedTargetStack.pop(), target = _a.target, depthStencil = _a.depthStencil, surfaceSelector = _a.surfaceSelector, mipSelector = _a.mipSelector, msaa = _a.msaa;
this.setRenderTarget(target, depthStencil, surfaceSelector, mipSelector, !msaa);
};
Stage.prototype.setRenderTarget = function (target, enableDepthAndStencil, surfaceSelector, mipmapSelector, disableMSAA) {
if (enableDepthAndStencil === void 0) { enableDepthAndStencil = false; }
if (surfaceSelector === void 0) { surfaceSelector = 0; }
if (mipmapSelector === void 0) { mipmapSelector = 0; }
if (disableMSAA === void 0) { disableMSAA = false; }
var conf = this._activeTargetConf;
if (conf.target === target
&& conf.surfaceSelector === surfaceSelector
&& conf.mipSelector === mipmapSelector
&& conf.depthStencil === enableDepthAndStencil
&& conf.msaa !== disableMSAA) {
return;
}
conf.msaa = !disableMSAA;
conf.target = target;
conf.surfaceSelector = surfaceSelector;
conf.mipSelector = mipmapSelector;
conf.depthStencil = enableDepthAndStencil;
if (target) {
var targetStageElement = target.getAbstraction(this);
var antiallias = typeof target.antialiasQuality === 'number' // for SceneImage2D MSAA
? target.antialiasQuality
: this._antiAlias;
this._context.setRenderToTexture(targetStageElement.getTexture(), enableDepthAndStencil, disableMSAA ? 0 : antiallias, surfaceSelector, mipmapSelector);
if (mipmapSelector != 0 && this._context.glVersion != 1) { //hack to stop auto generated mipmaps
targetStageElement._invalidMipmaps = false;
targetStageElement._mipmap = true;
}
}
else {
this._context.setRenderToBackBuffer();
this.configureBackBuffer(this._width, this._height, this._antiAlias, conf.depthStencil);
}
};
Stage.prototype.copyPixels = function (source, target, rect, destPoint, alphaBitmapData, alphaPoint, mergeAlpha) {
if (alphaBitmapData === void 0) { alphaBitmapData = null; }
if (alphaPoint === void 0) { alphaPoint = null; }
if (mergeAlpha === void 0) { mergeAlpha = false; }
// migrated to filter manager
this.filterManager.copyPixels(source, target, rect, destPoint, mergeAlpha);
};
Stage.prototype.threshold = function (source, target, rect, destPoint, operation, threshold, color, mask, copySource) {
// migrated to filter manager
this.filterManager.threshold(source, target, rect, destPoint, operation, threshold, color, mask, copySource);
};
Stage.prototype.colorTransform = function (source, target, rect, colorTransform) {
// migrated to filter manager
this.filterManager.colorTransform(source, target, rect, colorTransform);
};
Stage.prototype.requestAbstraction = function (asset) {
return new Stage._abstractionClassPool[asset.assetType];
};
Stage.prototype.storeAbstraction = function (abstraction) {
};
/**
*
* @param imageObjectClass
*/
Stage.registerAbstraction = function (abstractionClass, assetClass) {
Stage._abstractionClassPool[assetClass.assetType] = abstractionClass;
};
/**
* Requests a Context object to attach to the managed gl canvas.
*/
Stage.prototype.requestContext = function (forceSoftware, profile, _mode, alpha) {
// If forcing software, we can be certain that the
// returned Context will be running software mode.
// If not, we can't be sure and should stick to the
// old value (will likely be same if re-requesting.)
if (forceSoftware === void 0) { forceSoftware = false; }
if (profile === void 0) { profile = ContextGLProfile.BASELINE; }
if (_mode === void 0) { _mode = ContextMode.AUTO; }
if (alpha === void 0) { alpha = false; }
if (this._usesSoftwareRendering != null)
this._usesSoftwareRendering = forceSoftware;
this._profile = profile;
try {
this._context = new ContextWebGL(this._container, alpha);
}
catch (e) {
this.dispatchEvent(new StageEvent(StageEvent.STAGE_ERROR, this));
}
if (this._context)
this._callback(this._context);
};
Object.defineProperty(Stage.prototype, "width", {
/**
* The width of the gl canvas
*/
get: function () {
this._sizeDirty = false;
return this._width;
},
set: function (val) {
if (this._width == val)
return;
this._container.style.width = val + 'px';
this._container.width = val * this.pixelRatio;
this._width = val;
this._backBufferDirty = true;
if (this._width && this._height)
this._invalidateSize();
},
enumerable: false,
configurable: true
});
Object.defineProperty(Stage.prototype, "height", {
/**
* The height of the gl canvas
*/
get: function () {
this._sizeDirty = false;
return this._height;
},
set: function (val) {
if (this._height == val)
return;
this._container.style.height = val + 'px';
this._container.height = val * this.pixelRatio;
this._height = val;
this._backBufferDirty = true;
if (this._width && this._height)
this._invalidateSize();
},
enumerable: false,
configurable: true
});
Object.defineProperty(Stage.prototype, "x", {
/**
* The x position of the gl canvas
*/
get: function () {
return this._x;
},
set: function (val) {
if (this._x == val)
return;
CSS.setElementX(this._container, val);
this._x = val;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Stage.prototype, "y", {
/**
* The y position of the gl canvas
*/
get: function () {
return this._y;
},
set: function (val) {
if (this._y == val)
return;
CSS.setElementY(this._container, val);
this._y = val;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Stage.prototype, "visible", {
get: function () {
return CSS.getElementVisibility(this._container);
},
set: function (val) {
CSS.setElementVisibility(this._container, val);
},
enumerable: false,
configurable: true
});
Object.defineProperty(Stage.prototype, "container", {
get: function () {
return this._container;
},
set: function (value) {
this._container = value;
if (this._context) {
this._context.container = this._container;
}
},
enumerable: false,
configurable: true
});
Object.defineProperty(Stage.prototype, "context", {
/**
* The Context object associated with the given stage object.
*/
get: function () {
return this._context;
},
enumerable: false,
configurable: true
});
Stage.prototype._invalidateSize = function () {
if (this._sizeDirty)
return;
this._sizeDirty = true;
this.dispatchEvent(new StageEvent(StageEvent.INVALIDATE_SIZE, this));
};
Object.defineProperty(Stage.prototype, "profile", {
get: function () {
return this._profile;
},
enumerable: false,
configurable: true
});
/**
* Disposes the Stage object, freeing the Context attached to the Stage.
*/
Stage.prototype.dispose = function () {
/*
for (var id in this._abstractionPool){
if(this._abstractionPool[id].clear)
this._abstractionPool[id].clear();
}*/
this._stageManager.iRemoveStage(this);
this.freeContext();
this._stageManager = null;
this._stageIndex = -1;
UnloadService.clearAll();
};
/**
* Configures the back buffer associated with the Stage object.
* @param backBufferWidth The width of the backbuffer.
* @param backBufferHeight The height of the backbuffer.
* @param antiAlias The amount of anti-aliasing to use.
* @param enableDepthAndStencil Indicates whether the back buffer contains a depth and stencil buffer.
*/
Stage.prototype.configureBackBuffer = function (backBufferWidth, backBufferHeight, antiAlias, enableDepthAndStencil) {
this.width = backBufferWidth;
this.height = backBufferHeight;
this._antiAlias = antiAlias;
this._activeTargetConf.depthStencil = enableDepthAndStencil;
if (this._context)
this._context.configureBackBuffer(backBufferWidth, backBufferHeight, antiAlias, enableDepthAndStencil);
};
Object.defineProperty(Stage.prototype, "enableDepthAndStencil", {
/*
* Indicates whether the depth and stencil buffer is used
*/
get: function () {
return this._activeTargetConf.depthStencil;
},
set: function (enableDepthAndStencil) {
if (this._activeTargetConf.depthStencil == enableDepthAndStencil)
return;
this._activeTargetConf.depthStencil = enableDepthAndStencil;
this._backBufferDirty = true;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Stage.prototype, "renderSurfaceSelector", {
get: function () {
return this._activeTargetConf.surfaceSelector;
},
enumerable: false,
configurable: true
});
/*
* Clear and reset the back buffer when using a shared context
*/
Stage.prototype.clear = function (red, green, blue, alpha, depth, stencil, mask) {
if (red === void 0) { red = 0; }
if (green === void 0) { green = 0; }
if (blue === void 0) { blue = 0; }
if (alpha === void 0) { alpha = 1; }
if (depth === void 0) { depth = 1; }
if (stencil === void 0) { stencil = 0; }
if (mask === void 0) { mask = ContextGLClearMask.ALL; }
if (!this._context)
return;
if (this._backBufferDirty && this._activeTargetConf.target == null) {
this._backBufferDirty = false;
this.configureBackBuffer(this._width, this._height, this._antiAlias, this._activeTargetConf.depthStencil);
}
this._context.clear(red, green, blue, alpha, depth, stencil, mask);
};
Object.defineProperty(Stage.prototype, "stageIndex", {
/**
* The index of the Stage which is managed by this instance of StageProxy.
*/
get: function () {
return this._stageIndex;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Stage.prototype, "usesSoftwareRendering", {
/**
* Indicates whether the Stage managed by this proxy is running in software mode.
* Remember to wait for the CONTEXT_CREATED event before checking this property,
* as only then will it be guaranteed to be accurate.
*/
get: function () {
return this._usesSoftwareRendering;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Stage.prototype, "antiAlias", {
/**
* The antiAliasing of the Stage.
*/
get: function () {
return this._antiAlias;
},
set: function (antiAlias) {
if (this._antiAlias == antiAlias)
return;
this._antiAlias = antiAlias;
this._backBufferDirty = true;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Stage.prototype, "color", {
/**
* The background color of the Stage.
*/
get: function () {
return this._color;
},
set: function (color) {
this._color = color;
},
enumerable: false,
configurable: true
});
Stage.prototype.registerProgram = function (programData) {
var i = 0;
while (this._programData[i] != null)
i++;
this._programData[i] = programData;
programData.id = i;
};
Stage.prototype.unRegisterProgram = function (programData) {
this._programData[programData.id] = null;
programData.id = -1;
};
Stage.prototype.interactionHandler = function (event) {
var screenX = (event.clientX != null) ? event.clientX : event.changedTouches[0].clientX;
var screenY = (event.clientY != null) ? event.clientY : event.changedTouches[0].clientY;
var point = this.mapWindowToStage(screenX, screenY, TMP_POINT);
this._screenX = point.x;
this._screenY = point.y;
this._touchPoints.length = 0;
if (event.touches) {
for (var i = 0; i < event.touches.length; i++) {
var touch = event.touches[i];
point = this.mapWindowToStage(touch.clientX, touch.clientY, TMP_POINT);
this._touchPoints.push(new TouchPoint(point.x, point.y, touch.identifier));
}
}
};
Stage.prototype.mapWindowToStage = function (x, y, out) {
if (out === void 0) { out = { x: 0, y: 0 }; }
var container = this.container;
// IE 11 fix
var rect = (!container.parentElement)
? { x: 0, y: 0, width: 0, height: 0 }
: container.getBoundingClientRect();
// workground when pixelRatio !== self.devicePixelRatio
out.x = (x - rect.x) * container.width / (rect.width * this.pixelRatio);
out.y = (y - rect.y) * container.height / (rect.height * this.pixelRatio);
return out;
};
/**
* Frees the Context associated with this StageProxy.
*/
Stage.prototype.freeContext = function () {
if (this._context) {
this._context.dispose();
this.dispatchEvent(new StageEvent(StageEvent.CONTEXT_DISPOSED, this));
}
this._context = null;
this._initialised = false;
};
Stage.prototype.onContextLost = function (_event) {
};
Stage.prototype.onContextRestored = function (_event) {
};
Stage.prototype.recoverFromDisposal = function () {
if (!this._context)
return false;
//away.Debug.throwPIR( 'StageProxy' , 'recoverFromDisposal' , '' );
/*
if (this._iContext.driverInfo == "Disposed")
{
this._iContext = null;
this.dispatchEvent(new StageEvent(StageEvent.CONTEXT_DISPOSED));
return false;
}
*/
return true;
};
Stage.prototype._callback = function (context) {
var gl = context._gl;
ImageUtils.MAX_SIZE = gl.getParameter(gl.MAX_TEXTURE_SIZE);
this._context = context;
this._container = this._context.container;
// Only configure back buffer if width and height have been set,
// which they may not have been if View.render() has yet to be
// invoked for the first time.
if (this._width && this._height) {
this._context.configureBackBuffer(this._width, this._height, this._antiAlias, this._activeTargetConf.depthStencil);
}
// Dispatch the appropriate event depending on whether context was
// created for the first time or recreated after a device loss.
this.dispatchEvent(new StageEvent(this._initialised ? StageEvent.CONTEXT_RECREATED : StageEvent.CONTEXT_CREATED, this));
this._initialised = true;
};
Stage.prototype.setVertexBuffer = function (index, buffer, size, dimensions, offset, unsigned) {
if (unsigned === void 0) { unsigned = false; }
this._context.setVertexBufferAt(index, buffer, offset, BUFFER_FORMATS_MAP[unsigned ? size + 4 : size][dimensions - 1]);
};
Stage.prototype.setScissor = function (rectangle) {
if (this._backBufferDirty && this._activeTargetConf.target == null) {
this._backBufferDirty = false;
this.configureBackBuffer(this._width, this._height, this._antiAlias, this._activeTargetConf.depthStencil);
}
if (!this._lastScissorBox && !rectangle) {
return;
}
if (rectangle && this._lastScissorBox && this._lastScissorBox.equals(rectangle)) {
return;
}
if (!rectangle) {
this._lastScissorBox = null;
}
else {
if (!this._lastScissorBox) {
this._lastScissorBox = new Rectangle();
}
this._lastScissorBox.copyFrom(rectangle);
}
this._context.setScissorRectangle(rectangle);
};
Stage.prototype.setSamplerAt = function (index, sampler) {
var wrap = sampler.repeat ? ContextGLWrapMode.REPEAT : ContextGLWrapMode.CLAMP;
var filter = (sampler.smooth && !this.globalDisableSmooth)
? ContextGLTextureFilter.LINEAR
: ContextGLTextureFilter.NEAREST;
var mipfilter = (sampler.mipmap && !this.globalDisableMipmap)
? ContextGLMipFilter.MIPLINEAR
: ContextGLMipFilter.MIPNONE;
this._context.setSamplerStateAt(index, wrap, filter, mipfilter);
};
Stage._abstractionClassPool = {};
return Stage;
}(EventDispatcher));
export { Stage };