@awayjs/renderer
Version:
Renderer for AwayJS
339 lines (338 loc) • 13.1 kB
JavaScript
import { __extends } from "tslib";
import { AbstractionBase, WeakAssetSet } from '@awayjs/core';
import { ImageUtils, } from '@awayjs/stage';
import { PassEvent } from '../events/PassEvent';
import { MaterialEvent } from '../events/MaterialEvent';
/**
*
* @class away.pool.Passes
*/
var _Render_MaterialBase = /** @class */ (function (_super) {
__extends(_Render_MaterialBase, _super);
function _Render_MaterialBase() {
var _this = _super.call(this) || this;
/**
* A list of material owners, renderables or custom Entities.
*/
_this._owners = new WeakAssetSet();
_this._passes = [];
_this._invalidAnimation = true;
_this._invalidRender = true;
_this._invalidImages = true;
_this._imageIndices = {};
_this._usesAnimation = false;
_this.images = [];
_this.samplers = [];
/**
* Indicates whether or not the renderable requires alpha blending during rendering.
*/
_this.requiresBlending = false;
_this._onInvalidateTexturesDelegate = function (event) { return _this.onInvalidateTextures(event); };
_this._onInvalidatePassesDelegate = function (event) { return _this.onInvalidatePasses(event); };
_this._onPassInvalidateDelegate = function (event) { return _this.onPassInvalidate(event); };
return _this;
}
Object.defineProperty(_Render_MaterialBase.prototype, "animationSet", {
get: function () {
return this._animationSet;
},
enumerable: false,
configurable: true
});
Object.defineProperty(_Render_MaterialBase.prototype, "material", {
get: function () {
return this._useWeak ? this._asset.deref() : this._asset;
},
enumerable: false,
configurable: true
});
Object.defineProperty(_Render_MaterialBase.prototype, "renderElements", {
get: function () {
return this._pool;
},
enumerable: false,
configurable: true
});
Object.defineProperty(_Render_MaterialBase.prototype, "numImages", {
get: function () {
if (this._invalidImages)
this._updateImages();
return this._numImages;
},
enumerable: false,
configurable: true
});
Object.defineProperty(_Render_MaterialBase.prototype, "renderOrderId", {
get: function () {
if (this._invalidRender)
this._pUpdateRender();
if (this._invalidAnimation)
this._updateAnimation();
return this._renderOrderId;
},
enumerable: false,
configurable: true
});
Object.defineProperty(_Render_MaterialBase.prototype, "numPasses", {
get: function () {
if (this._invalidAnimation)
this._updateAnimation();
return this._passes.length;
},
enumerable: false,
configurable: true
});
_Render_MaterialBase.prototype.init = function (material, renderElements) {
_super.prototype.init.call(this, material, renderElements, true);
this.materialID = material.id;
this._stage = renderElements.stage;
material.addEventListener(MaterialEvent.INVALIDATE_TEXTURES, this._onInvalidateTexturesDelegate);
material.addEventListener(MaterialEvent.INVALIDATE_PASSES, this._onInvalidatePassesDelegate);
};
_Render_MaterialBase.prototype.activatePass = function (index) {
this._activePass = this._passes[index];
//clear unused vertex streams
var i;
for (i = this._activePass.shader.numUsedStreams; i < this._stage.numUsedStreams; i++)
this._stage.context.setVertexBufferAt(i, null);
//clear unused texture streams
for (i = this._activePass.shader.numUsedTextures; i < this._stage.numUsedTextures; i++)
this._stage.context.setTextureAt(i, null);
//activate shader object through pass
this._activePass._activate();
};
_Render_MaterialBase.prototype.deactivatePass = function () {
//deactivate shader object through pass
this._activePass._deactivate();
this._stage.numUsedStreams = this._activePass.shader.numUsedStreams;
this._stage.numUsedTextures = this._activePass.shader.numUsedTextures;
};
//
// MATERIAL MANAGEMENT
//
/**
* Mark an IEntity as owner of this material.
* Assures we're not using the same material across renderables with different animations, since the
* Programs depend on animation. This method needs to be called when a material is assigned.
*
* @param owner The IEntity that had this material assigned
*
* @internal
*/
_Render_MaterialBase.prototype.addOwner = function (owner) {
this._owners.add(owner);
var animationSet;
var animator = owner.entity.node.container.animator;
if (animator)
animationSet = animator.animationSet;
if (owner.entity.node.container.animator) {
if (this._animationSet && animationSet != this._animationSet) {
throw new Error('A Material instance cannot be shared across ' +
'material owners with different animation sets');
}
else {
if (this._animationSet != animationSet) {
this._animationSet = animationSet;
this._invalidAnimation = true;
}
}
}
};
/**
* Removes an IEntity as owner.
* @param owner
*
* @internal
*/
_Render_MaterialBase.prototype.removeOwner = function (owner) {
if (!this._owners.numAssets) //check if material is already disposed
return;
this._owners.remove(owner);
if (!this._owners.numAssets)
this.onClear();
};
_Render_MaterialBase.prototype.getImageIndex = function (texture, index) {
if (index === void 0) { index = 0; }
if (this._invalidImages)
this._updateImages();
return this._imageIndices[texture.id][index];
};
/**
*
*/
_Render_MaterialBase.prototype.onClear = function () {
var len = this._passes.length;
for (var i = 0; i < len; i++) {
this._passes[i].removeEventListener(PassEvent.INVALIDATE, this._onPassInvalidateDelegate);
this._passes[i].dispose();
}
this._passes.length = 0;
this._owners.clear();
var material = this.material;
if (material) {
material.removeEventListener(MaterialEvent.INVALIDATE_TEXTURES, this._onInvalidateTexturesDelegate);
material.removeEventListener(MaterialEvent.INVALIDATE_PASSES, this._onInvalidatePassesDelegate);
}
this._animationSet = null;
this._stage = null;
this._invalidAnimation = true;
this._invalidRender = true;
this._invalidImages = true;
this._imageIndices = {};
this._usesAnimation = false;
this._activePass = null;
this.images.length = 0;
this.samplers.length = 0;
_super.prototype.onClear.call(this);
};
/**
*
*/
_Render_MaterialBase.prototype.onInvalidatePasses = function (event) {
var len = this._passes.length;
for (var i = 0; i < len; i++)
this._passes[i].invalidate();
this._invalidAnimation = true;
this._invalidImages = true;
};
/**
* Listener for when a pass's shader code changes. It recalculates the render order id.
*/
_Render_MaterialBase.prototype.onPassInvalidate = function (event) {
this._invalidAnimation = true;
};
/**
*
*/
_Render_MaterialBase.prototype.onInvalidateTextures = function (event) {
this._owners.forEach(function (asset) { return asset._onInvalidateStyle(); });
};
/**
*
*/
_Render_MaterialBase.prototype.onInvalidate = function () {
_super.prototype.onInvalidate.call(this);
this._invalidRender = true;
this._invalidAnimation = true;
//prevent infinite loop with cacheRenderer invalidation
if (this.renderElements.renderer != this.material)
this.renderElements.renderer.invalidate();
};
/**
* Removes all passes from the surface
*/
_Render_MaterialBase.prototype._pClearPasses = function () {
var len = this._passes.length;
for (var i = 0; i < len; ++i)
this._passes[i].removeEventListener(PassEvent.INVALIDATE, this._onPassInvalidateDelegate);
this._passes.length = 0;
};
/**
* Adds a pass to the surface
* @param pass
*/
_Render_MaterialBase.prototype._pAddPass = function (pass) {
this._passes.push(pass);
pass.addEventListener(PassEvent.INVALIDATE, this._onPassInvalidateDelegate);
};
/**
* Removes a pass from the surface.
* @param pass The pass to be removed.
*/
_Render_MaterialBase.prototype._pRemovePass = function (pass) {
pass.removeEventListener(PassEvent.INVALIDATE, this._onPassInvalidateDelegate);
this._passes.splice(this._passes.indexOf(pass), 1);
};
/**
* Performs any processing that needs to occur before any of its passes are used.
*
* @private
*/
_Render_MaterialBase.prototype._pUpdateRender = function () {
if (this._invalidImages)
this._updateImages();
this._invalidRender = false;
};
/**
*
* @param surface
*/
_Render_MaterialBase.prototype._updateAnimation = function () {
// if (this._invalidRender)
// this._pUpdateRender();
this._invalidAnimation = false;
var usesAnimation = this._getEnabledGPUAnimation();
var renderOrderId = 0;
var mult = 1;
var shader;
var len = this._passes.length;
for (var i = 0; i < len; i++) {
shader = this._passes[i].shader;
shader.usesAnimation = usesAnimation;
// this is getter, this enforce update programData
// renderOrderId += shader.programData.id * mult;
if (shader._programData) {
renderOrderId += shader._programData.id * mult;
}
else {
renderOrderId += shader.programData.id * mult;
}
mult *= 1000;
}
if (this._usesAnimation != usesAnimation) {
this._usesAnimation = usesAnimation;
this._owners.forEach(function (asset) { return asset._onInvalidateElements(); });
}
this._renderOrderId = renderOrderId;
};
_Render_MaterialBase.prototype._updateImages = function () {
var material = this.material;
this._invalidImages = false;
var style = material.style;
var numTextures = material.getNumTextures();
var texture;
var numImages;
var images;
var index = 0;
for (var i = 0; i < numTextures; i++) {
texture = material.getTextureAt(i);
numImages = texture.getNumImages();
images = this._imageIndices[texture.id] = [];
for (var j = 0; j < numImages; j++) {
this.images[index] = (style === null || style === void 0 ? void 0 : style.getImageAt(texture, j))
|| texture.getImageAt(j)
|| ImageUtils.getDefaultImage2D();
this.samplers[index] = (style === null || style === void 0 ? void 0 : style.getSamplerAt(texture, j))
|| texture.getSamplerAt(j)
|| ImageUtils.getDefaultImageSampler();
images[j] = index++;
}
}
this._numImages = index;
};
/**
* test if animation will be able to run on gpu BEFORE compiling materials
* test if the shader objects supports animating the animation set in the vertex shader
* if any object using this material fails to support accelerated animations for any of the shader objects,
* we should do everything on cpu (otherwise we have the cost of both gpu + cpu animations)
*/
_Render_MaterialBase.prototype._getEnabledGPUAnimation = function () {
if (this._animationSet) {
this._animationSet.resetGPUCompatibility();
var len = this._passes.length;
var shader_1;
for (var i = 0; i < len; i++) {
shader_1 = this._passes[i].shader;
shader_1.usesAnimation = false;
this._owners.forEach(function (asset) {
if (asset.entity.node.container.animator)
asset.entity.node.container.animator.testGPUCompatibility(shader_1);
});
}
return !this._animationSet.usesCPU;
}
return false;
};
return _Render_MaterialBase;
}(AbstractionBase));
export { _Render_MaterialBase };