UNPKG

@awayjs/renderer

Version:
1,282 lines (1,043 loc) 41.8 kB
import { Matrix, Matrix3D, Vector3D, ColorTransform, ArgumentError, IAssetClass, IAbstractionClass, IAsset, UUID, IAbstraction, AbstractionSet, } from '@awayjs/core'; import { AGALMiniAssembler, ContextGLProfile, ContextGLBlendFactor, ContextGLBlendEquation, ContextGLCompareMode, ContextGLTriangleFace, ProgramData, Stage, BlendMode, ShaderRegisterCache, ShaderRegisterData, ShaderRegisterElement, Part, } from '@awayjs/stage'; import { _Render_RenderableBase } from './_Render_RenderableBase'; import { _Render_ElementsBase } from './_Render_ElementsBase'; import { AnimationRegisterData } from './AnimationRegisterData'; import { _Render_MaterialBase } from './_Render_MaterialBase'; import { _Stage_ElementsBase } from './_Stage_ElementsBase'; import { IAnimationSet } from './IAnimationSet'; import { View } from '@awayjs/view'; import { IPass } from './IPass'; import { IShaderBase } from './IShaderBase'; import { IRenderContainer } from './IRenderContainer'; // SRC, DST, ?SRC_ALPHA, ?DST_ALPHA type IBlendFactorType = [ContextGLBlendFactor, ContextGLBlendFactor] | [ContextGLBlendFactor, ContextGLBlendFactor, ContextGLBlendFactor, ContextGLBlendFactor]; type IBlendEquationType = [ContextGLBlendEquation] | [ContextGLBlendEquation, ContextGLBlendEquation]; /** * ShaderBase keeps track of the number of dependencies for "named registers" used across a pass. * Named registers are that are not necessarily limited to a single method. They are created by the compiler and * passed on to methods. The compiler uses the results to reserve usages through RegisterPool, which can be removed * each time a method has been compiled into the shader. * * @see RegisterPool.addUsage */ export class ShaderBase implements IShaderBase { private static _store: Record<string, IAbstraction[]> = {}; private static _abstractionClassPool: Object = new Object(); private _renderElements: _Render_ElementsBase; private _renderMaterial: _Render_MaterialBase; private _view: View; private _pass: IPass; public _stage: Stage; private _programData: ProgramData; private _blendFactor: IBlendFactorType = [ ContextGLBlendFactor.ONE, ContextGLBlendFactor.ZERO ]; private _blendEquation: IBlendEquationType = [ ContextGLBlendEquation.ADD, ContextGLBlendEquation.ADD ]; /* private _blendFactorSource: ContextGLBlendFactor = ContextGLBlendFactor.ONE; private _blendFactorDest: ContextGLBlendFactor = ContextGLBlendFactor.ZERO; */ private _invalidProgram: boolean = true; private _animationVertexCode: string = ''; private _animationFragmentCode: string = ''; private _numUsedVertexConstants: number; private _numUsedFragmentConstants: number; private _numUsedStreams: number; private _numUsedTextures: number; private _usesAnimation: boolean; protected _sharedRegisters: ShaderRegisterData; protected _registerCache: ShaderRegisterCache; protected _vertexCode: string = ''; protected _fragmentCode: string = ''; protected _postAnimationFragmentCode: string = ''; public get view(): View { return this._view; } public get stage(): Stage { return this._stage; } public get pass(): IPass { return this._pass; } public get renderMaterial(): _Render_MaterialBase { return this._renderMaterial; } public get programData(): ProgramData { if (this._invalidProgram) this._updateProgram(); return this._programData; } public usesBlending: boolean = false; public useImageRect: boolean = false; public usesCurves: boolean = false; /** * */ public activeElements: _Stage_ElementsBase; /** * The depth compare mode used to render the renderables using this material. * * @see away.stagegl.ContextGLCompareMode */ public depthCompareMode: ContextGLCompareMode = ContextGLCompareMode.LESS_EQUAL; /** * Indicate whether the shader should write to the depth buffer or not. Ignored when blending is enabled. */ public writeDepth: boolean = true; public profile: ContextGLProfile; public get usesAnimation(): boolean { return this._usesAnimation; } public set usesAnimation(value: boolean) { if (this._usesAnimation == value) return; this._usesAnimation = value; this.invalidateProgram(); } private _defaultCulling: ContextGLTriangleFace = ContextGLTriangleFace.BACK; public _pInverseSceneMatrix: Float32Array = new Float32Array(16); public animationRegisterData: AnimationRegisterData; public get numUsedVertexConstants(): number { if (this._invalidProgram) this._updateProgram(); return this._numUsedVertexConstants; } public get numUsedFragmentConstants(): number { if (this._invalidProgram) this._updateProgram(); return this._numUsedFragmentConstants; } /** * The amount of used vertex streams in the vertex code. * Used by the animation code generation to know from which index on streams are available. */ public get numUsedStreams(): number { if (this._invalidProgram) this._updateProgram(); return this._numUsedStreams; } /** * */ public get numUsedTextures(): number { if (this._invalidProgram) this._updateProgram(); return this._numUsedTextures; } public numLights: number; public usesPremultipliedAlpha: boolean; public useBothSides: boolean; public usesUVTransform: boolean; public usesColorTransform: boolean; public alphaThreshold: number; //set ambient values to default public ambientR: number = 0xFF; public ambientG: number = 0xFF; public ambientB: number = 0xFF; /** * */ public usesCommonData: boolean; /** * Indicates whether the pass requires any fragment animation code. */ public usesFragmentAnimation: boolean; /** * The amount of dependencies on the projected position. */ public projectionDependencies: number; /** * The amount of dependencies on the normal vector. */ public normalDependencies: number; /** * The amount of dependencies on the vertex color. */ public colorDependencies: number; /** * The amount of dependencies on the view direction. */ public viewDirDependencies: number; /** * The amount of dependencies on the primary UV coordinates. */ public uvDependencies: number; /** * The amount of dependencies on the secondary UV coordinates. */ public secondaryUVDependencies: number; /** * The amount of dependencies on the global position. This can be 0 while hasGlobalPosDependencies is true when * the global position is used as a temporary value (fe to calculate the view direction) */ public globalPosDependencies: number; /** * The amount of tangent vector dependencies (fragment shader). */ public tangentDependencies: number; /** * */ public outputsColors: boolean; /** * Indicates whether or not normals are output. */ public outputsNormals: boolean; /** * Indicates whether or not normal calculations are output in tangent space. */ public outputsTangentNormals: boolean; /** * Indicates whether or not normal calculations are * expected in tangent space. This is only the case if no world-space * dependencies exist and normals are being output. */ public usesTangentSpace: boolean; /** * Indicates whether there are any dependencies on the world-space position vector. */ public usesGlobalPosFragment: boolean = false; /** * Indicates whether there are any dependencies on the local position vector. */ public usesPositionFragment: boolean = false; public vertexConstantData: Float32Array; public fragmentConstantData: Float32Array; public viewMatrix: Matrix3D; public sceneMatrix: Matrix3D; public sceneNormalMatrix: Matrix3D; /** * The index for the common data register. */ public commonsDataIndex: number; /** * The index for the curve vertex attribute stream. */ public curvesIndex: number; /** * The index for the UV vertex attribute stream. */ public uvIndex: number; /** * The index for the secondary UV vertex attribute stream. */ public secondaryUVIndex: number; /** * The index for the vertex normal attribute stream. */ public normalIndex: number; /** * The index for the color attribute stream. */ public colorBufferIndex: number; /** * The index for the vertex tangent attribute stream. */ public tangentIndex: number; /** * */ public skyboxScaleIndex: number; /** * */ public scenePositionIndex: number; /** * The index of the vertex constant containing the view matrix. */ public viewMatrixIndex: number; /** * The index of the vertex constant containing the scene matrix. */ public sceneMatrixIndex: number; /** * The index of the vertex constant containing the uniform scene matrix (the inverse transpose). */ public sceneNormalMatrixIndex: number; /** * The index of the vertex constant containing the camera position. */ public cameraPositionIndex: number; /** * The index for the UV transformation matrix vertex constant. */ public uvMatrixIndex: number; /** * The index for the color transform fragment constant. */ public colorTransformIndex: number; /** * */ public jointIndexIndex: number; /** * */ public jointWeightIndex: number; /** * */ public imageIndices: Array<number> = new Array<number>(); public readonly id: number; public readonly abstractions: AbstractionSet; /** * Creates a new MethodCompilerVO object. */ constructor(renderElements: _Render_ElementsBase, renderMaterial: _Render_MaterialBase, pass: IPass, stage: Stage) { this.id = UUID.Next(); this.abstractions = new AbstractionSet(this); this._renderElements = renderElements; this._renderMaterial = renderMaterial; this._pass = pass; this._stage = stage; this._view = renderElements.renderer.view; this.profile = this._stage.profile; } public requestAbstraction(asset: IAsset): IAbstraction { const store = ShaderBase._store[asset.assetType]; return store.length ? store.pop() : new ShaderBase._abstractionClassPool[asset.assetType]; } public storeAbstraction(abstraction: IAbstraction, assetType: string): void { ShaderBase._store[assetType].push(abstraction); } /** * * @param imageObjectClass */ public static registerAbstraction(abstractionClass: IAbstractionClass, assetClass: IAssetClass): void { ShaderBase._abstractionClassPool[assetClass.assetType] = abstractionClass; ShaderBase._store[assetClass.assetType] = []; } public _includeDependencies(): void { this._renderMaterial.renderElements._includeDependencies(this); this._pass._includeDependencies(this); //this.usesCommonData = this.usesCurves || this.usesCommonData; } /** * Clears dependency counts for all registers. Called when recompiling a pass. */ public reset(): void { this.projectionDependencies = 0; this.normalDependencies = 0; this.colorDependencies = 0; this.viewDirDependencies = 0; this.uvDependencies = 0; this.secondaryUVDependencies = 0; this.globalPosDependencies = 0; this.tangentDependencies = 0; this.usesCommonData = false; this.usesGlobalPosFragment = false; this.usesPositionFragment = false; this.usesFragmentAnimation = false; this.usesTangentSpace = false; this.outputsNormals = false; this.outputsTangentNormals = false; } /** * The blend mode to use when drawing this renderable. The following blend modes are supported: * <ul> * <li>BlendMode.NORMAL: No blending, unless the material inherently needs it</li> * <li>BlendMode.LAYER: Force blending. * This will draw the object the same as NORMAL, but without writing depth writes.</li> * <li>BlendMode.MULTIPLY</li> * <li>BlendMode.ADD</li> * <li>BlendMode.ALPHA</li> * </ul> */ public setBlendMode(value: string): void { this.usesBlending = true; this.usesPremultipliedAlpha = true; // reset blend equation onto ADD, ADD this._blendEquation = [0,0]; //ADD switch (value) { case BlendMode.NORMAL: { this._blendFactor = [ ContextGLBlendFactor.ONE, ContextGLBlendFactor.ZERO ]; this.usesBlending = false; this.usesPremultipliedAlpha = false; break; } case BlendMode.LAYER: { this._blendFactor = [ ContextGLBlendFactor.ONE, ContextGLBlendFactor.ONE_MINUS_SOURCE_ALPHA ]; break; } case BlendMode.MULTIPLY: { this._blendFactor = [ ContextGLBlendFactor.DESTINATION_COLOR, ContextGLBlendFactor.ONE_MINUS_SOURCE_ALPHA, ContextGLBlendFactor.ONE, ContextGLBlendFactor.ONE_MINUS_SOURCE_ALPHA ]; break; } case BlendMode.ADD: { this._blendFactor = [ ContextGLBlendFactor.ONE, ContextGLBlendFactor.ONE ]; break; } case BlendMode.SCREEN: { this._blendFactor = [ ContextGLBlendFactor.ONE, ContextGLBlendFactor.ONE_MINUS_SOURCE_COLOR, ContextGLBlendFactor.ONE, ContextGLBlendFactor.ONE_MINUS_SOURCE_ALPHA ]; break; } case BlendMode.SUBTRACT: { this._blendFactor = [ ContextGLBlendFactor.ONE, ContextGLBlendFactor.ONE, ContextGLBlendFactor.ONE, ContextGLBlendFactor.ONE ]; this._blendEquation = [ ContextGLBlendEquation.SUBTRACT, ContextGLBlendEquation.ADD ]; break; } case BlendMode.ALPHA: { this._blendFactor = [ContextGLBlendFactor.ZERO, ContextGLBlendFactor.SOURCE_ALPHA]; this.usesPremultipliedAlpha = false; break; } case BlendMode.ERASE: { this._blendFactor = [ContextGLBlendFactor.ZERO, ContextGLBlendFactor.ONE_MINUS_SOURCE_ALPHA]; this.usesPremultipliedAlpha = false; break; } default: { throw new ArgumentError(`Unsupported blend mode: ${value}`); } } } /** * @inheritDoc */ public _activate(): void { if (!this.programData.program) { this.programData.program = this._stage.context.createProgram(); const vertexPart: Part = (new AGALMiniAssembler() .assemble('part vertex 1\n' + this.programData.vertexString + 'endpart')).vertex; const fragmentPart: Part = (new AGALMiniAssembler() .assemble('part fragment 1\n' + this.programData.fragmentString + 'endpart')).fragment; //@ts-ignore this.programData.program.upload(vertexPart, fragmentPart); } //set program data this._stage.context.setProgram(this.programData.program); this._stage.context.setCulling( this.useBothSides ? ContextGLTriangleFace.NONE : this._defaultCulling, this._view.projection.coordinateSystem); if (!this.usesTangentSpace && this.cameraPositionIndex >= 0) { const pos: Vector3D = this._view.projection.transform.matrix3D.position; this.vertexConstantData[this.cameraPositionIndex] = pos.x; this.vertexConstantData[this.cameraPositionIndex + 1] = pos.y; this.vertexConstantData[this.cameraPositionIndex + 2] = pos.z; } this._stage.context.setDepthTest((this.writeDepth && !this.usesBlending), this.depthCompareMode); //@ts-ignore this._stage.context.setBlendEquation( this._blendEquation[0], this._blendEquation[1] ); //@ts-ignore this._stage.context.setBlendFactors( this._blendFactor[0], this._blendFactor[1], // MUST be undef for non-separated mode this._blendFactor[2], this._blendFactor[3] ); this.activeElements = null; } /** * @inheritDoc */ public _deactivate(): void { //For the love of god don't remove this if you want your multi-material shadows to not flicker like shit this._stage.context.setDepthTest(true, ContextGLCompareMode.LESS_EQUAL); this.activeElements = null; } /** * * * @param renderable * @param stage * @param camera */ public _setRenderState(renderState: _Render_RenderableBase): void { const node = renderState.entity.node; if ((<IRenderContainer> node.container).animator) (<IRenderContainer> node.container).animator.setRenderState(this, renderState); let rawData: Float32Array; if (this.usesUVTransform) { const uvMatrix: Matrix = renderState.uvMatrix; if (uvMatrix) { //transpose rawData = uvMatrix.rawData; this.vertexConstantData[this.uvMatrixIndex] = rawData[0]; this.vertexConstantData[this.uvMatrixIndex + 1] = rawData[2]; this.vertexConstantData[this.uvMatrixIndex + 3] = rawData[4]; this.vertexConstantData[this.uvMatrixIndex + 4] = rawData[1]; this.vertexConstantData[this.uvMatrixIndex + 5] = rawData[3]; this.vertexConstantData[this.uvMatrixIndex + 7] = rawData[5]; } else { this.vertexConstantData[this.uvMatrixIndex] = 1; this.vertexConstantData[this.uvMatrixIndex + 1] = 0; this.vertexConstantData[this.uvMatrixIndex + 3] = 0; this.vertexConstantData[this.uvMatrixIndex + 4] = 0; this.vertexConstantData[this.uvMatrixIndex + 5] = 1; this.vertexConstantData[this.uvMatrixIndex + 7] = 0; } } if (this.usesColorTransform) { const colorTransform: ColorTransform = renderState.entity.colorTransform; if (colorTransform) { //TODO: AWDParser to write normalised color offsets rawData = colorTransform._rawData; this.fragmentConstantData[this.colorTransformIndex] = rawData[0]; this.fragmentConstantData[this.colorTransformIndex + 1] = rawData[1]; this.fragmentConstantData[this.colorTransformIndex + 2] = rawData[2]; this.fragmentConstantData[this.colorTransformIndex + 3] = rawData[3]; this.fragmentConstantData[this.colorTransformIndex + 4] = rawData[4] / 255; this.fragmentConstantData[this.colorTransformIndex + 5] = rawData[5] / 255; this.fragmentConstantData[this.colorTransformIndex + 6] = rawData[6] / 255; this.fragmentConstantData[this.colorTransformIndex + 7] = rawData[7] / 255; } else { this.fragmentConstantData[this.colorTransformIndex] = 1; this.fragmentConstantData[this.colorTransformIndex + 1] = 1; this.fragmentConstantData[this.colorTransformIndex + 2] = 1; this.fragmentConstantData[this.colorTransformIndex + 3] = 1; this.fragmentConstantData[this.colorTransformIndex + 4] = 0; this.fragmentConstantData[this.colorTransformIndex + 5] = 0; this.fragmentConstantData[this.colorTransformIndex + 6] = 0; this.fragmentConstantData[this.colorTransformIndex + 7] = 0; } } if (this.sceneNormalMatrixIndex >= 0) { this.sceneNormalMatrix.copyFrom(node.getInverseMatrix3D()); } if (this.usesTangentSpace && this.cameraPositionIndex >= 0) { node.getInverseMatrix3D().copyRawDataTo(this._pInverseSceneMatrix); const pos: Vector3D = this._view.projection.transform.matrix3D.position; const x: number = pos.x; const y: number = pos.y; const z: number = pos.z; this.vertexConstantData[this.cameraPositionIndex] = this._pInverseSceneMatrix[0] * x + this._pInverseSceneMatrix[4] * y + this._pInverseSceneMatrix[8] * z + this._pInverseSceneMatrix[12]; this.vertexConstantData[this.cameraPositionIndex + 1] = this._pInverseSceneMatrix[1] * x + this._pInverseSceneMatrix[5] * y + this._pInverseSceneMatrix[9] * z + this._pInverseSceneMatrix[13]; this.vertexConstantData[this.cameraPositionIndex + 2] = this._pInverseSceneMatrix[2] * x + this._pInverseSceneMatrix[6] * y + this._pInverseSceneMatrix[10] * z + this._pInverseSceneMatrix[14]; } } public invalidateProgram(): void { this._invalidProgram = true; } public dispose(): void { this._programData.dispose(); this._programData = null; this._registerCache.dispose(); this._registerCache = null; this._sharedRegisters = null; } private _updateProgram(): void { this._invalidProgram = false; this._sharedRegisters = new ShaderRegisterData(); this._registerCache = new ShaderRegisterCache(this.profile); this.reset(); this._includeDependencies(); this._initRegisterIndices(); this._compileDependencies(); //compile custom vertex & fragment codes from pass this._vertexCode += this._pass._getVertexCode(this._registerCache, this._sharedRegisters); this._fragmentCode += this._pass._getFragmentCode(this._registerCache, this._sharedRegisters); this._postAnimationFragmentCode += this._pass._getPostAnimationFragmentCode(this._registerCache, this._sharedRegisters); //check if alpha needs to be pre-multipled if (this.usesPremultipliedAlpha) { const target = this._sharedRegisters.shadedTarget; this._postAnimationFragmentCode += `mul ${target}.xyz, ${target}, ${target}.w\n`; } //assign the final output color to the output register this._postAnimationFragmentCode += `mov ${this._registerCache.fragmentOutputRegister}, ${this._sharedRegisters.shadedTarget}\n`; this._registerCache.removeFragmentTempUsage(this._sharedRegisters.shadedTarget); this._compileAnimationCode(); //initialise the required shader constants this._initConstantData(); const programData: ProgramData = this._stage.getProgramData( this._animationVertexCode + this._vertexCode, this._fragmentCode + this._animationFragmentCode + this._postAnimationFragmentCode); //check program data hasn't changed, keep count of program usages if (this._programData != programData) { if (this._programData) this._programData.dispose(); this._programData = programData; programData.usages++; } } /** * Reset all the indices to "unused". */ protected _initRegisterIndices(): void { this.commonsDataIndex = -1; this.cameraPositionIndex = -1; this.curvesIndex = -1; this.uvIndex = -1; this.uvMatrixIndex = -1; this.colorTransformIndex = -1; this.secondaryUVIndex = -1; this.normalIndex = -1; this.colorBufferIndex = -1; this.tangentIndex = -1; this.sceneMatrixIndex = -1; this.sceneNormalMatrixIndex = -1; this.jointIndexIndex = -1; this.jointWeightIndex = -1; this.imageIndices.length = 0; this._sharedRegisters.animatedPosition = this._registerCache.getFreeVertexVectorTemp(); this._registerCache.addVertexTempUsages(this._sharedRegisters.animatedPosition, 1); this._sharedRegisters.animatableAttributes.push(this._registerCache.getFreeVertexAttribute()); this._sharedRegisters.animationTargetRegisters.push(this._sharedRegisters.animatedPosition); this._vertexCode = ''; this._fragmentCode = ''; this._postAnimationFragmentCode = ''; //create commonly shared constant registers if (this.usesCommonData || this.normalDependencies > 0) { this._sharedRegisters.commons = this._registerCache.getFreeFragmentConstant(); this.commonsDataIndex = this._sharedRegisters.commons.index * 4; } //Creates the registers to contain the tangent data. //Needs to be created FIRST and in this order (for when using tangent space) if (this.tangentDependencies > 0 || this.outputsNormals) { this._sharedRegisters.tangentInput = this._registerCache.getFreeVertexAttribute(); this.tangentIndex = this._sharedRegisters.tangentInput.index; this._sharedRegisters.animatedTangent = this._registerCache.getFreeVertexVectorTemp(); this._registerCache.addVertexTempUsages(this._sharedRegisters.animatedTangent, 1); if (this.usesTangentSpace) { this._sharedRegisters.bitangent = this._registerCache.getFreeVertexVectorTemp(); this._registerCache.addVertexTempUsages(this._sharedRegisters.bitangent, 1); } this._sharedRegisters.animatableAttributes.push(this._sharedRegisters.tangentInput); this._sharedRegisters.animationTargetRegisters.push(this._sharedRegisters.animatedTangent); } if (this.normalDependencies > 0) { this._sharedRegisters.normalInput = this._registerCache.getFreeVertexAttribute(); this.normalIndex = this._sharedRegisters.normalInput.index; this._sharedRegisters.animatedNormal = this._registerCache.getFreeVertexVectorTemp(); this._registerCache.addVertexTempUsages(this._sharedRegisters.animatedNormal, 1); this._sharedRegisters.animatableAttributes.push(this._sharedRegisters.normalInput); this._sharedRegisters.animationTargetRegisters.push(this._sharedRegisters.animatedNormal); } if (this.uvDependencies > 0) { this._sharedRegisters.uvInput = this._registerCache.getFreeVertexAttribute(); this.uvIndex = this._sharedRegisters.uvInput.index; if (!this.usesUVTransform) { this._sharedRegisters.animatedUV = this._registerCache.getFreeVertexVectorTemp(); this._registerCache.addVertexTempUsages(this._sharedRegisters.animatedUV, 1); } } this._sharedRegisters.shadedTarget = this._registerCache.getFreeFragmentVectorTemp(); this._registerCache.addFragmentTempUsages(this._sharedRegisters.shadedTarget, 1); } /** * Compile the code for the methods. */ protected _compileDependencies(): void { if (this.colorDependencies > 0) { this._compileColorCode(); } //compile the world-space position if required if (this.globalPosDependencies > 0) this._compileGlobalPositionCode(); //compile the local-space position if required if (this.usesPositionFragment) this._compilePositionCode(); if (this.usesCurves) this._compileCurvesCode(); if (this.usesColorTransform) this._compileColorTransformCode(); if (this.secondaryUVDependencies > 0) this._compileSecondaryUVCode(); if (this.normalDependencies > 0) this._compileNormalCode(); if (this.viewDirDependencies > 0) this._compileViewDirCode(); //collect code from elements this._vertexCode += this._renderElements._getVertexCode(this, this._registerCache, this._sharedRegisters); this._fragmentCode += this._renderElements._getFragmentCode(this, this._registerCache, this._sharedRegisters); } /** * Initializes the unchanging constant data for this shader object. */ protected _initConstantData(): void { const rc = this._registerCache; //Updates the amount of used register indices. const usedVC = this._numUsedVertexConstants = rc.numUsedVertexConstants; const usedFC = this._numUsedFragmentConstants = rc.numUsedFragmentConstants; this._numUsedStreams = rc.numUsedStreams; this._numUsedTextures = rc.numUsedTextures; if (!this.vertexConstantData || this.vertexConstantData.length !== usedVC * 4) this.vertexConstantData = new Float32Array(usedVC * 4); if (!this.fragmentConstantData || this.fragmentConstantData.length !== usedFC * 4) this.fragmentConstantData = new Float32Array(this._registerCache.numUsedFragmentConstants * 4); //Initialies viewMatrix if (this.viewMatrixIndex >= 0) { const data = new Float32Array(this.vertexConstantData.buffer, this.viewMatrixIndex * 4, 16); if (!this.viewMatrix) { this.viewMatrix = new Matrix3D(data); } else { this.viewMatrix._rawData = data; } } else if (this.viewMatrix) { this.viewMatrix = null; } //Initialies sceneMatrix if (this.sceneMatrixIndex >= 0) { const data = new Float32Array(this.vertexConstantData.buffer, this.sceneMatrixIndex * 4, 16); if (!this.sceneMatrix) { this.sceneMatrix = new Matrix3D(data); } else { this.sceneMatrix._rawData = data; } } else if (this.sceneMatrix) { this.sceneMatrix = null; } //Initializes commonly required constant values. if (this.commonsDataIndex >= 0) { this.fragmentConstantData[this.commonsDataIndex] = .5; this.fragmentConstantData[this.commonsDataIndex + 1] = 0; this.fragmentConstantData[this.commonsDataIndex + 2] = 1 / 255; this.fragmentConstantData[this.commonsDataIndex + 3] = 1; } //Initializes the default UV transformation matrix. if (this.uvMatrixIndex >= 0) { this.vertexConstantData[this.uvMatrixIndex] = 1; this.vertexConstantData[this.uvMatrixIndex + 1] = 0; this.vertexConstantData[this.uvMatrixIndex + 2] = 0; this.vertexConstantData[this.uvMatrixIndex + 3] = 0; this.vertexConstantData[this.uvMatrixIndex + 4] = 0; this.vertexConstantData[this.uvMatrixIndex + 5] = 1; this.vertexConstantData[this.uvMatrixIndex + 6] = 0; this.vertexConstantData[this.uvMatrixIndex + 7] = 0; } //Initializes the default colorTransform. if (this.colorTransformIndex >= 0) { this.fragmentConstantData[this.colorTransformIndex] = 1; this.fragmentConstantData[this.colorTransformIndex + 1] = 1; this.fragmentConstantData[this.colorTransformIndex + 2] = 1; this.fragmentConstantData[this.colorTransformIndex + 3] = 1; this.fragmentConstantData[this.colorTransformIndex + 4] = 0; this.fragmentConstantData[this.colorTransformIndex + 5] = 0; this.fragmentConstantData[this.colorTransformIndex + 6] = 0; this.fragmentConstantData[this.colorTransformIndex + 7] = 0; } if (this.sceneNormalMatrixIndex >= 0) { const data = new Float32Array(this.vertexConstantData.buffer, this.sceneNormalMatrixIndex * 4, 16); if (!this.sceneNormalMatrix) { this.sceneNormalMatrix = new Matrix3D(data); } else { this.sceneNormalMatrix._rawData = data; } } else if (this.sceneNormalMatrix) { this.sceneNormalMatrix = null; } if (this.cameraPositionIndex >= 0) this.vertexConstantData[this.cameraPositionIndex + 3] = 1; // init constant data in pass this._pass._initConstantData(); //init constant data in animation if (this._usesAnimation) this._renderMaterial.animationSet.doneAGALCode(this); } private _compileColorCode(): void { this._sharedRegisters.colorInput = this._registerCache.getFreeVertexAttribute(); this.colorBufferIndex = this._sharedRegisters.colorInput.index; this._sharedRegisters.colorVarying = this._registerCache.getFreeVarying(); this._vertexCode += `mov ${this._sharedRegisters.colorVarying}, ${this._sharedRegisters.colorInput}\n`; } private _compileGlobalPositionCode(): void { const temp = this._sharedRegisters.globalPositionVertex = this._registerCache.getFreeVertexVectorTemp(); this._registerCache.addVertexTempUsages(temp, this.globalPosDependencies); const sceneMatrixReg: ShaderRegisterElement = this._registerCache.getFreeVertexConstant(); this._registerCache.getFreeVertexConstant(); this._registerCache.getFreeVertexConstant(); this._registerCache.getFreeVertexConstant(); this.sceneMatrixIndex = sceneMatrixReg.index * 4; const r = this._sharedRegisters; this._vertexCode += `m44 ${r.globalPositionVertex}, ${r.animatedPosition}, ${sceneMatrixReg}\n`; if (this.usesGlobalPosFragment) { r.globalPositionVarying = this._registerCache.getFreeVarying(); this._vertexCode += `mov ${r.globalPositionVarying}, ${r.globalPositionVertex}\n`; } } private _compilePositionCode() { const r = this._sharedRegisters; r.positionVarying = this._registerCache.getFreeVarying(); this._vertexCode += `mov ${r.positionVarying}, ${r.animatedPosition}\n`; } private _compileCurvesCode(): void { const r = this._sharedRegisters; r.curvesInput = this._registerCache.getFreeVertexAttribute(); this.curvesIndex = r.curvesInput.index; r.curvesVarying = this._registerCache.getFreeVarying(); this._vertexCode += 'mov ' + r.curvesVarying + ', ' + r.curvesInput + '\n'; const temp = this._registerCache.getFreeFragmentSingleTemp(); this._fragmentCode += 'mul ' + temp + ', ' + r.curvesVarying + '.y, ' + r.curvesVarying + '.y\n' + 'sub ' + temp + ', ' + temp + ', ' + r.curvesVarying + '.z\n' + 'mul ' + temp + ', ' + temp + ', ' + r.curvesVarying + '.x\n' + 'kil ' + temp + '\n'; } /** * Calculate the transformed colours */ private _compileColorTransformCode(): void { // rm, gm, bm, am - multiplier // ro, go, bo, ao - offset const ct1 = this._registerCache.getFreeFragmentConstant(); const ct2 = this._registerCache.getFreeFragmentConstant(); const target = this._sharedRegisters.shadedTarget; this.colorTransformIndex = ct1.index * 4; this._postAnimationFragmentCode += 'mul ' + target + ', ' + target + ', ' + ct1 + '\n'; this._postAnimationFragmentCode += 'add ' + target + ', ' + target + ', ' + ct2 + '\n'; } /** * Provide the secondary UV coordinates. */ private _compileSecondaryUVCode(): void { const uvAttributeReg: ShaderRegisterElement = this._registerCache.getFreeVertexAttribute(); const r = this._sharedRegisters; this.secondaryUVIndex = uvAttributeReg.index; r.secondaryUVVarying = this._registerCache.getFreeVarying(); this._vertexCode += 'mov ' + r.secondaryUVVarying + ', ' + uvAttributeReg + '\n'; } /** * Calculate the view direction. */ private _compileViewDirCode(): void { const camPosReg = this._registerCache.getFreeVertexConstant(); const r = this._sharedRegisters; r.viewDirVarying = this._registerCache.getFreeVarying(); r.viewDirFragment = this._registerCache.getFreeFragmentVectorTemp(); this._registerCache.addFragmentTempUsages(r.viewDirFragment, this.viewDirDependencies); this.cameraPositionIndex = camPosReg.index * 4; if (this.usesTangentSpace) { const temp = this._registerCache.getFreeVertexVectorTemp(); this._vertexCode += 'sub ' + temp + ', ' + camPosReg + ', ' + r.animatedPosition + '\n' + 'm33 ' + r.viewDirVarying + '.xyz, ' + temp + ', ' + r.animatedTangent + '\n' + 'mov ' + r.viewDirVarying + '.w, ' + r.animatedPosition + '.w\n'; } else { this._vertexCode += 'sub ' + r.viewDirVarying + ', ' + camPosReg + ', ' + r.globalPositionVertex + '\n'; this._registerCache.removeVertexTempUsage(this._sharedRegisters.globalPositionVertex); } //TODO is this required in all cases? (re: distancemappass) this._fragmentCode += 'nrm ' + r.viewDirFragment + '.xyz, ' + r.viewDirVarying + '\n' + 'mov ' + r.viewDirFragment + '.w, ' + r.viewDirVarying + '.w\n'; } /** * Calculate the normal. */ private _compileNormalCode(): void { const r = this._sharedRegisters; r.normalFragment = this._registerCache.getFreeFragmentVectorTemp(); this._registerCache.addFragmentTempUsages(r.normalFragment, this.normalDependencies); //simple normal aquisition if no tangent space is being used if (this.outputsNormals && !this.outputsTangentNormals) { this._vertexCode += this._pass._getNormalVertexCode(this._registerCache, r); this._fragmentCode += this._pass._getNormalFragmentCode(this._registerCache, r); return; } let normalMatrix: Array<ShaderRegisterElement>; if (!this.outputsNormals || !this.usesTangentSpace) { normalMatrix = new Array<ShaderRegisterElement>(3); normalMatrix[0] = this._registerCache.getFreeVertexConstant(); normalMatrix[1] = this._registerCache.getFreeVertexConstant(); normalMatrix[2] = this._registerCache.getFreeVertexConstant(); this._registerCache.getFreeVertexConstant(); this.sceneNormalMatrixIndex = normalMatrix[0].index * 4; r.normalVarying = this._registerCache.getFreeVarying(); } if (this.outputsNormals) { if (this.usesTangentSpace) { // normalize normal + tangent vector and generate (approximated) bitangent // used in m33 operation for view this._vertexCode += 'nrm ' + r.animatedNormal + '.xyz, ' + r.animatedNormal + '\n' + 'nrm ' + r.animatedTangent + '.xyz, ' + r.animatedTangent + '\n' + 'crs ' + r.bitangent + '.xyz, ' + r.animatedNormal + ', ' + r.animatedTangent + '\n'; this._fragmentCode += this._pass._getNormalFragmentCode(this._registerCache, r); } else { //Compiles the vertex shader code for tangent-space normal maps. r.tangentVarying = this._registerCache.getFreeVarying(); r.bitangentVarying = this._registerCache.getFreeVarying(); const temp: ShaderRegisterElement = this._registerCache.getFreeVertexVectorTemp(); this._vertexCode += 'm33 ' + temp + '.xyz, ' + r.animatedNormal + ', ' + normalMatrix[0] + '\n' + 'nrm ' + r.animatedNormal + '.xyz, ' + temp + '\n' + 'm33 ' + temp + '.xyz, ' + r.animatedTangent + ', ' + normalMatrix[0] + '\n' + 'nrm ' + r.animatedTangent + '.xyz, ' + temp + '\n' + 'mov ' + r.tangentVarying + '.x, ' + r.animatedTangent + '.x \n' + 'mov ' + r.tangentVarying + '.z, ' + r.animatedNormal + '.x \n' + 'mov ' + r.tangentVarying + '.w, ' + r.normalInput + '.w \n' + 'mov ' + r.bitangentVarying + '.x, ' + r.animatedTangent + '.y \n' + 'mov ' + r.bitangentVarying + '.z, ' + r.animatedNormal + '.y \n' + 'mov ' + r.bitangentVarying + '.w, ' + r.normalInput + '.w \n' + 'mov ' + r.normalVarying + '.x, ' + r.animatedTangent + '.z \n' + 'mov ' + r.normalVarying + '.z, ' + r.animatedNormal + '.z \n' + 'mov ' + r.normalVarying + '.w, ' + r.normalInput + '.w \n' + 'crs ' + temp + '.xyz, ' + r.animatedNormal + ', ' + r.animatedTangent + '\n' + 'mov ' + r.tangentVarying + '.y, ' + temp + '.x \n' + 'mov ' + r.bitangentVarying + '.y, ' + temp + '.y \n' + 'mov ' + r.normalVarying + '.y, ' + temp + '.z \n'; this._registerCache.removeVertexTempUsage(r.animatedTangent); //Compiles the fragment shader code for tangent-space normal maps. const t = this._registerCache.getFreeFragmentVectorTemp(); this._registerCache.addFragmentTempUsages(t, 1); const b = this._registerCache.getFreeFragmentVectorTemp(); this._registerCache.addFragmentTempUsages(b, 1); const n = this._registerCache.getFreeFragmentVectorTemp(); this._registerCache.addFragmentTempUsages(n, 1); this._fragmentCode += 'nrm ' + t + '.xyz, ' + r.tangentVarying + '\n' + 'mov ' + t + '.w, ' + r.tangentVarying + '.w \n' + 'nrm ' + b + '.xyz, ' + r.bitangentVarying + '\n' + 'nrm ' + n + '.xyz, ' + r.normalVarying + '\n'; //compile custom fragment code for normal calcs this._fragmentCode += this._pass._getNormalFragmentCode(this._registerCache, r) + 'm33 ' + r.normalFragment + '.xyz, ' + r.normalFragment + ', ' + t + '\n' + 'mov ' + r.normalFragment + '.w, ' + r.normalVarying + '.w\n'; this._registerCache.removeFragmentTempUsage(b); this._registerCache.removeFragmentTempUsage(t); this._registerCache.removeFragmentTempUsage(n); } } else { // no output, world space is enough this._vertexCode += 'm33 ' + r.normalVarying + '.xyz, ' + r.animatedNormal + ', ' + normalMatrix[0] + '\n' + 'mov ' + r.normalVarying + '.w, ' + r.animatedNormal + '.w\n'; this._fragmentCode += 'nrm ' + r.normalFragment + '.xyz, ' + r.normalVarying + '\n' + 'mov ' + r.normalFragment + '.w, ' + r.normalVarying + '.w\n'; if (this.tangentDependencies > 0) { r.tangentVarying = this._registerCache.getFreeVarying(); this._vertexCode += 'm33 ' + r.tangentVarying + '.xyz, ' + r.animatedTangent + ', ' + normalMatrix[0] + '\n' + 'mov ' + r.tangentVarying + '.w, ' + r.animatedTangent + '.w\n'; } } if (!this.usesTangentSpace) this._registerCache.removeVertexTempUsage(r.animatedNormal); } private _compileAnimationCode(): void { //reset code this._animationVertexCode = ''; this._animationFragmentCode = ''; const r = this._sharedRegisters; //check to see if GPU animation is used if (this._usesAnimation) { const animationSet: IAnimationSet = <IAnimationSet> this._renderMaterial.animationSet; this._animationVertexCode += animationSet.getAGALVertexCode(this, this._registerCache, r); if (this.uvDependencies > 0 && !this.usesUVTransform) this._animationVertexCode += animationSet.getAGALUVCode(this, this._registerCache, r); if (this.usesFragmentAnimation) this._animationFragmentCode += animationSet.getAGALFragmentCode( this, this._registerCache, r.shadedTarget); } else { // simply write attributes to targets, do not animate them // projection will pick up on targets[0] to do the projection const len: number = r.animatableAttributes.length; for (let i: number = 0; i < len; ++i) this._animationVertexCode += 'mov ' + r.animationTargetRegisters[i] + ', ' + r.animatableAttributes[i] + '\n'; if (this.uvDependencies > 0 && !this.usesUVTransform) this._animationVertexCode += 'mov ' + r.animatedUV + ',' + r.uvInput + '\n'; } } public setVertexConst(index: number, x: number = 0, y: number = 0, z: number = 0, w: number = 0): void { index *= 4; this.vertexConstantData[index++] = x; this.vertexConstantData[index++] = y; this.vertexConstantData[index++] = z; this.vertexConstantData[index] = w; } public setVertexConstFromArray(index: number, data: Float32Array): void { index *= 4; for (let i: number /*int*/ = 0; i < data.length; i++) this.vertexConstantData[index++] = data[i]; } public setVertexConstFromMatrix(index: number, matrix: Matrix3D): void { index *= 4; const rawData: Float32Array = matrix._rawData; this.vertexConstantData[index++] = rawData[0]; this.vertexConstantData[index++] = rawData[4]; this.vertexConstantData[index++] = rawData[8]; this.vertexConstantData[index++] = rawData[12]; this.vertexConstantData[index++] = rawData[1]; this.vertexConstantData[index++] = rawData[5]; this.vertexConstantData[index++] = rawData[9]; this.vertexConstantData[index++] = rawData[13]; this.vertexConstantData[index++] = rawData[2]; this.vertexConstantData[index++] = rawData[6]; this.vertexConstantData[index++] = rawData[10]; this.vertexConstantData[index++] = rawData[14]; this.vertexConstantData[index++] = rawData[3]; this.vertexConstantData[index++] = rawData[7]; this.vertexConstantData[index++] = rawData[11]; this.vertexConstantData[index] = rawData[15]; } public setFragmentConst(index: number, x: number = 0, y: number = 0, z: number = 0, w: number = 0): void { index *= 4; this.fragmentConstantData[index++] = x; this.fragmentConstantData[index++] = y; this.fragmentConstantData[index++] = z; this.fragmentConstantData[index] = w; } }