UNPKG

@awayjs/stage

Version:
597 lines (595 loc) 23.4 kB
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 };