@awayjs/renderer
Version:
Renderer for AwayJS
344 lines (271 loc) • 8.26 kB
text/typescript
import {
AbstractMethodError,
AssetEvent,
Matrix,
Matrix3D,
AbstractionBase,
IAsset,
} from '@awayjs/core';
import {
Stage,
_Stage_ImageBase,
ImageSampler,
ImageBase,
} from '@awayjs/stage';
import { ContainerNode } from '@awayjs/view';
import { RenderableEvent } from '../events/RenderableEvent';
import { MaterialUtils } from '../utils/MaterialUtils';
import { _Render_MaterialBase } from './_Render_MaterialBase';
import { _Stage_ElementsBase } from './_Stage_ElementsBase';
import { IPass } from './IPass';
import { IMaterial } from './IMaterial';
import { RenderEntity } from './RenderEntity';
import { ITexture } from './ITexture';
import { Style } from './Style';
import { IRenderable } from './IRenderable';
import { Settings } from '../Settings';
import { IShaderBase } from './IShaderBase';
/**
* @class RenderableListItem
*/
export class _Render_RenderableBase extends AbstractionBase implements IRenderable {
private _onInvalidateElementsDelegate: (event: RenderableEvent) => void;
private _onInvalidateMaterialDelegate: (event: RenderableEvent) => void;
private _onInvalidateStyleDelegate: (event: RenderableEvent) => void;
private _materialDirty: boolean = true;
private _stageElements: _Stage_ElementsBase;
private _elementsDirty: boolean = true;
private _styleDirty: boolean = true;
private _images: Array<_Stage_ImageBase> = new Array<_Stage_ImageBase>();
private _samplers: Array<ImageSampler> = new Array<ImageSampler>();
private _uvMatrix: Matrix;
public JOINT_INDEX_FORMAT: string;
public JOINT_WEIGHT_FORMAT: string;
public _count: number = 0;
public _offset: number = 0;
protected _stage: Stage;
protected _renderMaterial: _Render_MaterialBase;
/**
*
*/
public get images(): Array<_Stage_ImageBase> {
if (this._styleDirty)
this._updateStyle();
return this._images;
}
/**
*
*/
public get samplers(): Array<ImageSampler> {
if (this._styleDirty)
this._updateStyle();
return this._samplers;
}
/**
*
*/
public renderSceneTransform: Matrix3D;
/**
*
*/
public get uvMatrix(): Matrix {
if (this._styleDirty)
this._updateStyle();
return this._uvMatrix;
}
/**
*
*/
public next: IRenderable;
public id: number;
/**
*
*/
public materialID: number;
/**
*
*/
public renderOrderId: number;
/**
*
*/
public zIndex: number;
/**
*
*/
private _maskId: number = -1;
public set maskId(v: number) {
if (v !== this._maskId) {
this._updateMaskHack(v >= 0);
}
this._maskId = v;
}
public get maskId() {
return this._maskId;
}
/**
*
*/
public maskOwners: ContainerNode[];
/**
*
*/
public cascaded: boolean;
/**
*
* @returns {_Stage_ElementsBase}
*/
public get stageElements(): _Stage_ElementsBase {
if (this._elementsDirty)
this._updateElements();
return this._stageElements;
}
/**
*
* @returns {_Render_MaterialBase}
*/
public get renderMaterial(): _Render_MaterialBase {
if (this._materialDirty)
this._updateMaterial();
return this._renderMaterial;
}
public get entity(): RenderEntity {
return this._useWeak ? (<WeakRef<RenderEntity>> this._pool).deref() : <RenderEntity> this._pool;
}
constructor() {
super();
this._onInvalidateElementsDelegate = (event: RenderableEvent) => this._onInvalidateElements(event);
this._onInvalidateMaterialDelegate = (event: RenderableEvent) => this._onInvalidateMaterial(event);
this._onInvalidateStyleDelegate = (event: RenderableEvent) => this._onInvalidateStyle(event);
}
/**
*
* @param renderable
* @param sourceEntity
* @param surface
* @param renderer
*/
public init(renderable: IAsset, entity: RenderEntity): void {
super.init(renderable, entity, true);
//store references
this._stage = entity.stage;
entity.addRenderable(this);
this._asset.addEventListener(RenderableEvent.INVALIDATE_ELEMENTS, this._onInvalidateElementsDelegate);
this._asset.addEventListener(RenderableEvent.INVALIDATE_MATERIAL, this._onInvalidateMaterialDelegate);
this._asset.addEventListener(RenderableEvent.INVALIDATE_STYLE, this._onInvalidateStyleDelegate);
}
private _updateMaskHack(enable: boolean) {
if (!Settings.USE_ALPHA_CUTOFF
|| Settings.ALPHA_CUTOFF_VALUE < 0
|| Settings.ALPHA_CUTOFF_VALUE > 1) {
return;
}
const mat = this.renderMaterial?.material as IMaterial & {alphaThreshold: number};
if (!mat) {
return;
}
mat.alphaThreshold = enable ? Settings.ALPHA_CUTOFF_VALUE : 0;
}
/**
* Renders an object to the current render target.
*
* @private
*/
public executeRender(
enableDepthAndStencil: boolean = true, surfaceSelector: number = 0,
mipmapSelector: number = 0, maskConfig: number = 0): void {
const pass: IPass = this._renderMaterial._activePass;
pass._setRenderState(this);
const shader: IShaderBase = pass.shader;
const elements: _Stage_ElementsBase = this.stageElements;
if (shader.activeElements != elements) {
shader.activeElements = elements;
elements._setRenderState(this, shader);
}
this._stageElements.draw(this, shader, this._count, this._offset);
}
public onClear(event: AssetEvent): void {
this._asset.removeEventListener(RenderableEvent.INVALIDATE_ELEMENTS, this._onInvalidateElementsDelegate);
this._asset.removeEventListener(RenderableEvent.INVALIDATE_MATERIAL, this._onInvalidateMaterialDelegate);
this._asset.removeEventListener(RenderableEvent.INVALIDATE_STYLE, this._onInvalidateStyleDelegate);
this.entity?.removeRenderable(this);
this.renderSceneTransform = null;
this._stage = null;
this.next = null;
this.maskOwners = null;
this._renderMaterial.removeOwner(this);
this._renderMaterial = null;
this._stageElements = null;
this._materialDirty = true;
this._elementsDirty = true;
this._styleDirty = true;
super.onClear(event);
}
public _onInvalidateElements(event: RenderableEvent = null): void {
this._elementsDirty = true;
}
public _onInvalidateMaterial(event: RenderableEvent = null): void {
this._materialDirty = true;
this._styleDirty = true;
}
public _onInvalidateStyle(event: RenderableEvent = null): void {
this._styleDirty = true;
}
protected _getStageElements(): _Stage_ElementsBase {
throw new AbstractMethodError();
}
protected _getRenderMaterial(): _Render_MaterialBase {
throw new AbstractMethodError();
}
protected _getStyle(): Style {
throw new AbstractMethodError();
}
/**
* //TODO
*
* @private
*/
private _updateElements(): void {
this._stageElements = this._getStageElements();
this._elementsDirty = false;
}
protected _updateMaterial(): void {
const renderMaterial: _Render_MaterialBase = this._getRenderMaterial();
if (this._renderMaterial != renderMaterial) {
if (this._renderMaterial)
this._renderMaterial.removeOwner(this);
this._renderMaterial = renderMaterial;
this._renderMaterial.addOwner(this);
}
this._materialDirty = false;
}
protected _updateStyle(): void {
const style: Style = this._getStyle();
if (this._materialDirty)
this._updateMaterial();
//create a cache of image & sampler objects for the renderable
let numImages: number = this._renderMaterial.numImages;
const material: IMaterial = this._renderMaterial.material;
this._images.length = numImages;
this._samplers.length = numImages;
this._uvMatrix = style ? style.uvMatrix : material.style ? material.style.uvMatrix : null;
const numTextures: number = this._renderMaterial.material.getNumTextures();
let texture: ITexture;
let image: ImageBase;
let index: number;
for (let i: number = 0; i < numTextures; i++) {
texture = material.getTextureAt(i);
numImages = texture.getNumImages();
for (let j: number = 0; j < numImages; j++) {
index = this._renderMaterial.getImageIndex(texture, j);
image = style ? style.getImageAt(texture, j) : null;
this._images[index] = image ? image.getAbstraction<_Stage_ImageBase>(this._stage) : null;
this._samplers[index] = style ? style.getSamplerAt(texture, j) : null;
}
}
this._styleDirty = false;
}
protected getDefaultMaterial(): IMaterial {
return MaterialUtils.getDefaultTextureMaterial();
}
}