| // SwiftShader Software Renderer |
| // |
| // Copyright(c) 2005-2012 TransGaming Inc. |
| // |
| // All rights reserved. No part of this software may be copied, distributed, transmitted, |
| // transcribed, stored in a retrieval system, translated into any human or computer |
| // language by any means, or disclosed to third parties without the explicit written |
| // agreement of TransGaming Inc. Without such an agreement, no rights or licenses, express |
| // or implied, including but not limited to any patent rights, are granted to you. |
| // |
| |
| #include "Context.hpp" |
| |
| #include "PixelShader.hpp" |
| #include "VertexShader.hpp" |
| #include "Primitive.hpp" |
| #include "Surface.hpp" |
| #include "Memory.hpp" |
| #include "CPUID.hpp" |
| #include "Debug.hpp" |
| |
| #include <string.h> |
| #include <malloc.h> |
| |
| namespace sw |
| { |
| extern bool perspectiveCorrection; |
| |
| bool halfIntegerCoordinates = false; // Pixel centers are not at integer coordinates |
| bool symmetricNormalizedDepth = false; // [-1, 1] instead of [0, 1] |
| bool booleanFaceRegister = false; |
| bool fullPixelPositionRegister = false; |
| |
| bool forceWindowed = false; |
| bool quadLayoutEnabled = false; |
| bool veryEarlyDepthTest = true; |
| bool complementaryDepthBuffer = false; |
| bool postBlendSRGB = false; |
| bool exactColorRounding = false; |
| TransparencyAntialiasing transparencyAntialiasing = TRANSPARENCY_NONE; |
| bool forceClearRegisters = false; |
| |
| Context::Context() |
| { |
| init(); |
| } |
| |
| Context::~Context() |
| { |
| } |
| |
| void *Context::operator new(size_t bytes) |
| { |
| return allocate((unsigned int)bytes); |
| } |
| |
| void Context::operator delete(void *pointer, size_t bytes) |
| { |
| deallocate(pointer); |
| } |
| |
| bool Context::isDrawPoint(bool fillModeAware) const |
| { |
| switch(drawType) |
| { |
| case DRAW_POINTLIST: |
| case DRAW_INDEXEDPOINTLIST8: |
| case DRAW_INDEXEDPOINTLIST16: |
| case DRAW_INDEXEDPOINTLIST32: |
| return true; |
| case DRAW_LINELIST: |
| case DRAW_LINESTRIP: |
| case DRAW_LINELOOP: |
| case DRAW_INDEXEDLINELIST8: |
| case DRAW_INDEXEDLINESTRIP8: |
| case DRAW_INDEXEDLINELOOP8: |
| case DRAW_INDEXEDLINELIST16: |
| case DRAW_INDEXEDLINESTRIP16: |
| case DRAW_INDEXEDLINELOOP16: |
| case DRAW_INDEXEDLINELIST32: |
| case DRAW_INDEXEDLINESTRIP32: |
| case DRAW_INDEXEDLINELOOP32: |
| return false; |
| case DRAW_TRIANGLELIST: |
| case DRAW_TRIANGLESTRIP: |
| case DRAW_TRIANGLEFAN: |
| case DRAW_INDEXEDTRIANGLELIST8: |
| case DRAW_INDEXEDTRIANGLESTRIP8: |
| case DRAW_INDEXEDTRIANGLEFAN8: |
| case DRAW_INDEXEDTRIANGLELIST16: |
| case DRAW_INDEXEDTRIANGLESTRIP16: |
| case DRAW_INDEXEDTRIANGLEFAN16: |
| case DRAW_INDEXEDTRIANGLELIST32: |
| case DRAW_INDEXEDTRIANGLESTRIP32: |
| case DRAW_INDEXEDTRIANGLEFAN32: |
| return fillModeAware ? fillMode == FILL_VERTEX : false; |
| case DRAW_QUADLIST: |
| return false; |
| default: |
| ASSERT(false); |
| } |
| |
| return false; |
| } |
| |
| bool Context::isDrawLine(bool fillModeAware) const |
| { |
| switch(drawType) |
| { |
| case DRAW_POINTLIST: |
| case DRAW_INDEXEDPOINTLIST8: |
| case DRAW_INDEXEDPOINTLIST16: |
| case DRAW_INDEXEDPOINTLIST32: |
| return false; |
| case DRAW_LINELIST: |
| case DRAW_LINESTRIP: |
| case DRAW_LINELOOP: |
| case DRAW_INDEXEDLINELIST8: |
| case DRAW_INDEXEDLINESTRIP8: |
| case DRAW_INDEXEDLINELOOP8: |
| case DRAW_INDEXEDLINELIST16: |
| case DRAW_INDEXEDLINESTRIP16: |
| case DRAW_INDEXEDLINELOOP16: |
| case DRAW_INDEXEDLINELIST32: |
| case DRAW_INDEXEDLINESTRIP32: |
| case DRAW_INDEXEDLINELOOP32: |
| return true; |
| case DRAW_TRIANGLELIST: |
| case DRAW_TRIANGLESTRIP: |
| case DRAW_TRIANGLEFAN: |
| case DRAW_INDEXEDTRIANGLELIST8: |
| case DRAW_INDEXEDTRIANGLESTRIP8: |
| case DRAW_INDEXEDTRIANGLEFAN8: |
| case DRAW_INDEXEDTRIANGLELIST16: |
| case DRAW_INDEXEDTRIANGLESTRIP16: |
| case DRAW_INDEXEDTRIANGLEFAN16: |
| case DRAW_INDEXEDTRIANGLELIST32: |
| case DRAW_INDEXEDTRIANGLESTRIP32: |
| case DRAW_INDEXEDTRIANGLEFAN32: |
| return fillModeAware ? fillMode == FILL_WIREFRAME : false; |
| case DRAW_QUADLIST: |
| return false; |
| default: |
| ASSERT(false); |
| } |
| |
| return false; |
| } |
| |
| bool Context::isDrawTriangle(bool fillModeAware) const |
| { |
| switch(drawType) |
| { |
| case DRAW_POINTLIST: |
| case DRAW_INDEXEDPOINTLIST8: |
| case DRAW_INDEXEDPOINTLIST16: |
| case DRAW_INDEXEDPOINTLIST32: |
| return false; |
| case DRAW_LINELIST: |
| case DRAW_LINESTRIP: |
| case DRAW_LINELOOP: |
| case DRAW_INDEXEDLINELIST8: |
| case DRAW_INDEXEDLINESTRIP8: |
| case DRAW_INDEXEDLINELOOP8: |
| case DRAW_INDEXEDLINELIST16: |
| case DRAW_INDEXEDLINESTRIP16: |
| case DRAW_INDEXEDLINELOOP16: |
| case DRAW_INDEXEDLINELIST32: |
| case DRAW_INDEXEDLINESTRIP32: |
| case DRAW_INDEXEDLINELOOP32: |
| return false; |
| case DRAW_TRIANGLELIST: |
| case DRAW_TRIANGLESTRIP: |
| case DRAW_TRIANGLEFAN: |
| case DRAW_INDEXEDTRIANGLELIST8: |
| case DRAW_INDEXEDTRIANGLESTRIP8: |
| case DRAW_INDEXEDTRIANGLEFAN8: |
| case DRAW_INDEXEDTRIANGLELIST16: |
| case DRAW_INDEXEDTRIANGLESTRIP16: |
| case DRAW_INDEXEDTRIANGLEFAN16: |
| case DRAW_INDEXEDTRIANGLELIST32: |
| case DRAW_INDEXEDTRIANGLESTRIP32: |
| case DRAW_INDEXEDTRIANGLEFAN32: |
| return fillModeAware ? fillMode == FILL_SOLID : true; |
| case DRAW_QUADLIST: |
| // Quads are broken up into triangles |
| return fillModeAware ? fillMode == FILL_SOLID : true; |
| default: |
| ASSERT(false); |
| } |
| |
| return true; |
| } |
| |
| void Context::init() |
| { |
| for(int i = 0; i < 8; i++) |
| { |
| textureStage[i].init(i, &sampler[i], (i >= 1) ? &textureStage[i - 1] : 0); |
| } |
| |
| // Set vertex streams to null stream |
| for(int i = 0; i < TEXTURE_IMAGE_UNITS; i++) |
| { |
| input[i].defaults(); |
| } |
| |
| fogStart = 0.0f; |
| fogEnd = 1.0f; |
| |
| for(int i = 0; i < TEXTURE_IMAGE_UNITS; i++) textureWrap[i] = 0; |
| for(int i = 0; i < 8; i++) texGen[i] = TEXGEN_PASSTHRU; |
| for(int i = 0; i < 8; i++) textureTransformCount[i] = 0; |
| for(int i = 0; i < 8; i++) textureTransformProject[i] = false; |
| textureWrapActive = false; |
| localViewer = true; |
| normalizeNormals = false; |
| |
| renderTarget[0] = 0; |
| renderTarget[1] = 0; |
| renderTarget[2] = 0; |
| renderTarget[3] = 0; |
| depthStencil = 0; |
| |
| stencilEnable = false; |
| stencilCompareMode = STENCIL_ALWAYS; |
| stencilReference = 0; |
| stencilMask = 0xFFFFFFFF; |
| stencilFailOperation = OPERATION_KEEP; |
| stencilPassOperation = OPERATION_KEEP; |
| stencilZFailOperation = OPERATION_KEEP; |
| stencilWriteMask = 0xFFFFFFFF; |
| |
| twoSidedStencil = false; |
| stencilCompareModeCCW = STENCIL_ALWAYS; |
| stencilReferenceCCW = 0; |
| stencilMaskCCW = 0xFFFFFFFF; |
| stencilFailOperationCCW = OPERATION_KEEP; |
| stencilPassOperationCCW = OPERATION_KEEP; |
| stencilZFailOperationCCW = OPERATION_KEEP; |
| stencilWriteMaskCCW = 0xFFFFFFFF; |
| |
| setGlobalMipmapBias(0); |
| |
| lightingEnable = true; |
| specularEnable = false; |
| for(int i = 0; i < 8; i++) lightEnable[i] = false; |
| for(int i = 0; i < 8; i++) worldLightPosition[i] = 0; |
| |
| alphaCompareMode = ALPHA_ALWAYS; |
| alphaTestEnable = false; |
| fillMode = FILL_SOLID; |
| shadingMode = SHADING_GOURAUD; |
| |
| depthCompareMode = DEPTH_LESS; |
| depthBufferEnable = true; |
| depthWriteEnable = true; |
| |
| alphaBlendEnable = false; |
| sourceBlendFactorState = BLEND_ONE; |
| destBlendFactorState = BLEND_ZERO; |
| blendOperationState = BLENDOP_ADD; |
| |
| separateAlphaBlendEnable = false; |
| sourceBlendFactorStateAlpha = BLEND_ONE; |
| destBlendFactorStateAlpha = BLEND_ZERO; |
| blendOperationStateAlpha = BLENDOP_ADD; |
| |
| cullMode = CULL_CLOCKWISE; |
| alphaReference = 0; |
| colorWriteMask[0] = 0x0000000F; |
| colorWriteMask[1] = 0x0000000F; |
| colorWriteMask[2] = 0x0000000F; |
| colorWriteMask[3] = 0x0000000F; |
| |
| ambientMaterialSource = MATERIAL_MATERIAL; |
| diffuseMaterialSource = MATERIAL_COLOR1; |
| specularMaterialSource = MATERIAL_COLOR2; |
| emissiveMaterialSource = MATERIAL_MATERIAL; |
| colorVertexEnable = true; |
| |
| fogEnable = false; |
| pixelFogMode = FOG_NONE; |
| vertexFogMode = FOG_NONE; |
| wBasedFog = false; |
| rangeFogEnable = false; |
| |
| indexedVertexBlendEnable = false; |
| vertexBlendMatrixCount = 0; |
| |
| pixelShader = 0; |
| vertexShader = 0; |
| |
| instanceID = 0; |
| |
| occlusionEnabled = false; |
| |
| pointSpriteEnable = false; |
| pointScaleEnable = false; |
| lineWidth = 1.0f; |
| |
| writeSRGB = false; |
| sampleMask = 0xFFFFFFFF; |
| } |
| |
| const float &Context::exp2Bias() |
| { |
| return bias; |
| } |
| |
| const Point &Context::getLightPosition(int light) |
| { |
| return worldLightPosition[light]; |
| } |
| |
| void Context::setGlobalMipmapBias(float bias) |
| { |
| this->bias = exp2(bias + 0.5f); |
| } |
| |
| void Context::setLightingEnable(bool lightingEnable) |
| { |
| this->lightingEnable = lightingEnable; |
| } |
| |
| void Context::setSpecularEnable(bool specularEnable) |
| { |
| Context::specularEnable = specularEnable; |
| } |
| |
| void Context::setLightEnable(int light, bool lightEnable) |
| { |
| Context::lightEnable[light] = lightEnable; |
| } |
| |
| void Context::setLightPosition(int light, Point worldLightPosition) |
| { |
| Context::worldLightPosition[light] = worldLightPosition; |
| } |
| |
| void Context::setAmbientMaterialSource(MaterialSource ambientMaterialSource) |
| { |
| Context::ambientMaterialSource = ambientMaterialSource; |
| } |
| |
| void Context::setDiffuseMaterialSource(MaterialSource diffuseMaterialSource) |
| { |
| Context::diffuseMaterialSource = diffuseMaterialSource; |
| } |
| |
| void Context::setSpecularMaterialSource(MaterialSource specularMaterialSource) |
| { |
| Context::specularMaterialSource = specularMaterialSource; |
| } |
| |
| void Context::setEmissiveMaterialSource(MaterialSource emissiveMaterialSource) |
| { |
| Context::emissiveMaterialSource = emissiveMaterialSource; |
| } |
| |
| void Context::setPointSpriteEnable(bool pointSpriteEnable) |
| { |
| Context::pointSpriteEnable = pointSpriteEnable; |
| } |
| |
| void Context::setPointScaleEnable(bool pointScaleEnable) |
| { |
| Context::pointScaleEnable = pointScaleEnable; |
| } |
| |
| bool Context::setDepthBufferEnable(bool depthBufferEnable) |
| { |
| bool modified = (Context::depthBufferEnable != depthBufferEnable); |
| Context::depthBufferEnable = depthBufferEnable; |
| return modified; |
| } |
| |
| bool Context::setAlphaBlendEnable(bool alphaBlendEnable) |
| { |
| bool modified = (Context::alphaBlendEnable != alphaBlendEnable); |
| Context::alphaBlendEnable = alphaBlendEnable; |
| return modified; |
| } |
| |
| bool Context::setSourceBlendFactor(BlendFactor sourceBlendFactor) |
| { |
| bool modified = (Context::sourceBlendFactorState != sourceBlendFactor); |
| Context::sourceBlendFactorState = sourceBlendFactor; |
| return modified; |
| } |
| |
| bool Context::setDestBlendFactor(BlendFactor destBlendFactor) |
| { |
| bool modified = (Context::destBlendFactorState != destBlendFactor); |
| Context::destBlendFactorState = destBlendFactor; |
| return modified; |
| } |
| |
| bool Context::setBlendOperation(BlendOperation blendOperation) |
| { |
| bool modified = (Context::blendOperationState != blendOperation); |
| Context::blendOperationState = blendOperation; |
| return modified; |
| } |
| |
| bool Context::setSeparateAlphaBlendEnable(bool separateAlphaBlendEnable) |
| { |
| bool modified = (Context::separateAlphaBlendEnable != separateAlphaBlendEnable); |
| Context::separateAlphaBlendEnable = separateAlphaBlendEnable; |
| return modified; |
| } |
| |
| bool Context::setSourceBlendFactorAlpha(BlendFactor sourceBlendFactorAlpha) |
| { |
| bool modified = (Context::sourceBlendFactorStateAlpha != sourceBlendFactorAlpha); |
| Context::sourceBlendFactorStateAlpha = sourceBlendFactorAlpha; |
| return modified; |
| } |
| |
| bool Context::setDestBlendFactorAlpha(BlendFactor destBlendFactorAlpha) |
| { |
| bool modified = (Context::destBlendFactorStateAlpha != destBlendFactorAlpha); |
| Context::destBlendFactorStateAlpha = destBlendFactorAlpha; |
| return modified; |
| } |
| |
| bool Context::setBlendOperationAlpha(BlendOperation blendOperationAlpha) |
| { |
| bool modified = (Context::blendOperationStateAlpha != blendOperationAlpha); |
| Context::blendOperationStateAlpha = blendOperationAlpha; |
| return modified; |
| } |
| |
| bool Context::setColorWriteMask(int index, int colorWriteMask) |
| { |
| bool modified = (Context::colorWriteMask[index] != colorWriteMask); |
| Context::colorWriteMask[index] = colorWriteMask; |
| return modified; |
| } |
| |
| bool Context::setWriteSRGB(bool sRGB) |
| { |
| bool modified = (Context::writeSRGB != sRGB); |
| Context::writeSRGB = sRGB; |
| return modified; |
| } |
| |
| void Context::setColorVertexEnable(bool colorVertexEnable) |
| { |
| Context::colorVertexEnable = colorVertexEnable; |
| } |
| |
| bool Context::fogActive() |
| { |
| if(!colorUsed()) return false; |
| |
| if(pixelShaderVersion() >= 0x0300) return false; |
| |
| return fogEnable; |
| } |
| |
| bool Context::pointSizeActive() |
| { |
| if(vertexShader) |
| { |
| return false; |
| } |
| |
| return isDrawPoint(true) && (input[PointSize] || (!preTransformed && pointScaleActive())); |
| } |
| |
| FogMode Context::pixelFogActive() |
| { |
| if(fogActive()) |
| { |
| return pixelFogMode; |
| } |
| |
| return FOG_NONE; |
| } |
| |
| bool Context::depthWriteActive() |
| { |
| if(!depthBufferActive()) return false; |
| |
| return depthWriteEnable; |
| } |
| |
| bool Context::alphaTestActive() |
| { |
| if(!alphaTestEnable) return false; |
| if(alphaCompareMode == ALPHA_ALWAYS) return false; |
| if(alphaReference == 0 && alphaCompareMode == ALPHA_GREATEREQUAL) return false; |
| |
| return true; |
| } |
| |
| bool Context::depthBufferActive() |
| { |
| return depthStencil && depthBufferEnable; |
| } |
| |
| bool Context::stencilActive() |
| { |
| return depthStencil && stencilEnable; |
| } |
| |
| bool Context::vertexLightingActive() |
| { |
| if(vertexShader) |
| { |
| return false; |
| } |
| |
| return lightingEnable && !preTransformed; |
| } |
| |
| bool Context::texCoordActive(int coordinate, int component) |
| { |
| bool hasTexture = pointSpriteActive(); |
| |
| if(vertexShader) |
| { |
| if(!preTransformed) |
| { |
| if(vertexShader->output[T0 + coordinate][component].usage == Shader::USAGE_TEXCOORD) |
| { |
| hasTexture = true; |
| } |
| } |
| else |
| { |
| hasTexture = true; // FIXME: Check vertex buffer streams |
| } |
| } |
| else |
| { |
| switch(texGen[coordinate]) |
| { |
| case TEXGEN_NONE: |
| hasTexture = true; |
| break; |
| case TEXGEN_PASSTHRU: |
| hasTexture = hasTexture || (component < input[TexCoord0 + textureStage[coordinate].texCoordIndex].count); |
| break; |
| case TEXGEN_NORMAL: |
| hasTexture = hasTexture || (component <= 2); |
| break; |
| case TEXGEN_POSITION: |
| hasTexture = hasTexture || (component <= 2); |
| break; |
| case TEXGEN_REFLECTION: |
| hasTexture = hasTexture || (component <= 2); |
| break; |
| case TEXGEN_SPHEREMAP: |
| hasTexture = hasTexture || (component <= 1); |
| break; |
| default: |
| ASSERT(false); |
| } |
| } |
| |
| bool project = isProjectionComponent(coordinate, component); |
| bool usesTexture = false; |
| |
| if(pixelShader) |
| { |
| usesTexture = pixelShader->usesTexture(coordinate, component) || project; |
| } |
| else |
| { |
| usesTexture = textureStage[coordinate].usesTexture() || project; |
| } |
| |
| return hasTexture && usesTexture; |
| } |
| |
| bool Context::texCoordActive(int coordinate) |
| { |
| return texCoordActive(coordinate, 0) || |
| texCoordActive(coordinate, 1) || |
| texCoordActive(coordinate, 2) || |
| texCoordActive(coordinate, 3); |
| } |
| |
| bool Context::isProjectionComponent(unsigned int coordinate, int component) |
| { |
| if(pixelShaderVersion() <= 0x0103 && coordinate < 8 && textureTransformProject[coordinate]) |
| { |
| if(textureTransformCount[coordinate] == 2) |
| { |
| if(component == 1) return true; |
| } |
| else if(textureTransformCount[coordinate] == 3) |
| { |
| if(component == 2) return true; |
| } |
| else if(textureTransformCount[coordinate] == 4 || textureTransformCount[coordinate] == 0) |
| { |
| if(component == 3) return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool Context::vertexSpecularActive() |
| { |
| return vertexLightingActive() && specularEnable && vertexNormalActive(); |
| } |
| |
| bool Context::vertexNormalActive() |
| { |
| if(vertexShader) |
| { |
| return false; |
| } |
| |
| return input[Normal]; |
| } |
| |
| bool Context::vertexLightActive(int i) |
| { |
| if(vertexShader) |
| { |
| return false; |
| } |
| |
| return lightingEnable && lightEnable[i]; |
| } |
| |
| MaterialSource Context::vertexDiffuseMaterialSourceActive() |
| { |
| if(vertexShader) |
| { |
| return MATERIAL_MATERIAL; |
| } |
| |
| if(diffuseMaterialSource == MATERIAL_MATERIAL || !colorVertexEnable || |
| (diffuseMaterialSource == MATERIAL_COLOR1 && !input[Color0]) || |
| (diffuseMaterialSource == MATERIAL_COLOR2 && !input[Color1])) |
| { |
| return MATERIAL_MATERIAL; |
| } |
| |
| return diffuseMaterialSource; |
| } |
| |
| MaterialSource Context::vertexSpecularMaterialSourceActive() |
| { |
| if(vertexShader) |
| { |
| return MATERIAL_MATERIAL; |
| } |
| |
| if(!colorVertexEnable || |
| (specularMaterialSource == MATERIAL_COLOR1 && !input[Color0]) || |
| (specularMaterialSource == MATERIAL_COLOR2 && !input[Color1])) |
| { |
| return MATERIAL_MATERIAL; |
| } |
| |
| return specularMaterialSource; |
| } |
| |
| MaterialSource Context::vertexAmbientMaterialSourceActive() |
| { |
| if(vertexShader) |
| { |
| return MATERIAL_MATERIAL; |
| } |
| |
| if(!colorVertexEnable || |
| (ambientMaterialSource == MATERIAL_COLOR1 && !input[Color0]) || |
| (ambientMaterialSource == MATERIAL_COLOR2 && !input[Color1])) |
| { |
| return MATERIAL_MATERIAL; |
| } |
| |
| return ambientMaterialSource; |
| } |
| |
| MaterialSource Context::vertexEmissiveMaterialSourceActive() |
| { |
| if(vertexShader) |
| { |
| return MATERIAL_MATERIAL; |
| } |
| |
| if(!colorVertexEnable || |
| (emissiveMaterialSource == MATERIAL_COLOR1 && !input[Color0]) || |
| (emissiveMaterialSource == MATERIAL_COLOR2 && !input[Color1])) |
| { |
| return MATERIAL_MATERIAL; |
| } |
| |
| return emissiveMaterialSource; |
| } |
| |
| bool Context::pointSpriteActive() |
| { |
| return isDrawPoint(true) && pointSpriteEnable; |
| } |
| |
| bool Context::pointScaleActive() |
| { |
| if(vertexShader) |
| { |
| return false; |
| } |
| |
| return isDrawPoint(true) && pointScaleEnable; |
| } |
| |
| bool Context::alphaBlendActive() |
| { |
| if(!alphaBlendEnable) |
| { |
| return false; |
| } |
| |
| if(!colorUsed()) |
| { |
| return false; |
| } |
| |
| bool colorBlend = !(blendOperation() == BLENDOP_SOURCE && sourceBlendFactor() == BLEND_ONE); |
| bool alphaBlend = separateAlphaBlendEnable ? !(blendOperationAlpha() == BLENDOP_SOURCE && sourceBlendFactorAlpha() == BLEND_ONE) : colorBlend; |
| |
| return colorBlend || alphaBlend; |
| } |
| |
| BlendFactor Context::sourceBlendFactor() |
| { |
| if(!alphaBlendEnable) return BLEND_ONE; |
| |
| switch(blendOperationState) |
| { |
| case BLENDOP_ADD: |
| case BLENDOP_SUB: |
| case BLENDOP_INVSUB: |
| return sourceBlendFactorState; |
| case BLENDOP_MIN: |
| return BLEND_ONE; |
| case BLENDOP_MAX: |
| return BLEND_ONE; |
| default: |
| ASSERT(false); |
| } |
| |
| return sourceBlendFactorState; |
| } |
| |
| BlendFactor Context::destBlendFactor() |
| { |
| if(!alphaBlendEnable) return BLEND_ZERO; |
| |
| switch(blendOperationState) |
| { |
| case BLENDOP_ADD: |
| case BLENDOP_SUB: |
| case BLENDOP_INVSUB: |
| return destBlendFactorState; |
| case BLENDOP_MIN: |
| return BLEND_ONE; |
| case BLENDOP_MAX: |
| return BLEND_ONE; |
| default: |
| ASSERT(false); |
| } |
| |
| return destBlendFactorState; |
| } |
| |
| BlendOperation Context::blendOperation() |
| { |
| if(!alphaBlendEnable) return BLENDOP_SOURCE; |
| |
| switch(blendOperationState) |
| { |
| case BLENDOP_ADD: |
| if(sourceBlendFactor() == BLEND_ZERO) |
| { |
| if(destBlendFactor() == BLEND_ZERO) |
| { |
| return BLENDOP_NULL; |
| } |
| else |
| { |
| return BLENDOP_DEST; |
| } |
| } |
| else if(sourceBlendFactor() == BLEND_ONE) |
| { |
| if(destBlendFactor() == BLEND_ZERO) |
| { |
| return BLENDOP_SOURCE; |
| } |
| else |
| { |
| return BLENDOP_ADD; |
| } |
| } |
| else |
| { |
| if(destBlendFactor() == BLEND_ZERO) |
| { |
| return BLENDOP_SOURCE; |
| } |
| else |
| { |
| return BLENDOP_ADD; |
| } |
| } |
| case BLENDOP_SUB: |
| if(sourceBlendFactor() == BLEND_ZERO) |
| { |
| return BLENDOP_NULL; // Negative, clamped to zero |
| } |
| else if(sourceBlendFactor() == BLEND_ONE) |
| { |
| if(destBlendFactor() == BLEND_ZERO) |
| { |
| return BLENDOP_SOURCE; |
| } |
| else |
| { |
| return BLENDOP_SUB; |
| } |
| } |
| else |
| { |
| if(destBlendFactor() == BLEND_ZERO) |
| { |
| return BLENDOP_SOURCE; |
| } |
| else |
| { |
| return BLENDOP_SUB; |
| } |
| } |
| case BLENDOP_INVSUB: |
| if(sourceBlendFactor() == BLEND_ZERO) |
| { |
| if(destBlendFactor() == BLEND_ZERO) |
| { |
| return BLENDOP_NULL; |
| } |
| else |
| { |
| return BLENDOP_DEST; |
| } |
| } |
| else if(sourceBlendFactor() == BLEND_ONE) |
| { |
| if(destBlendFactor() == BLEND_ZERO) |
| { |
| return BLENDOP_NULL; // Negative, clamped to zero |
| } |
| else |
| { |
| return BLENDOP_INVSUB; |
| } |
| } |
| else |
| { |
| if(destBlendFactor() == BLEND_ZERO) |
| { |
| return BLENDOP_NULL; // Negative, clamped to zero |
| } |
| else |
| { |
| return BLENDOP_INVSUB; |
| } |
| } |
| case BLENDOP_MIN: |
| return BLENDOP_MIN; |
| case BLENDOP_MAX: |
| return BLENDOP_MAX; |
| default: |
| ASSERT(false); |
| } |
| |
| return blendOperationState; |
| } |
| |
| BlendFactor Context::sourceBlendFactorAlpha() |
| { |
| if(!separateAlphaBlendEnable) |
| { |
| return sourceBlendFactor(); |
| } |
| else |
| { |
| switch(blendOperationStateAlpha) |
| { |
| case BLENDOP_ADD: |
| case BLENDOP_SUB: |
| case BLENDOP_INVSUB: |
| return sourceBlendFactorStateAlpha; |
| case BLENDOP_MIN: |
| return BLEND_ONE; |
| case BLENDOP_MAX: |
| return BLEND_ONE; |
| default: |
| ASSERT(false); |
| } |
| |
| return sourceBlendFactorStateAlpha; |
| } |
| } |
| |
| BlendFactor Context::destBlendFactorAlpha() |
| { |
| if(!separateAlphaBlendEnable) |
| { |
| return destBlendFactor(); |
| } |
| else |
| { |
| switch(blendOperationStateAlpha) |
| { |
| case BLENDOP_ADD: |
| case BLENDOP_SUB: |
| case BLENDOP_INVSUB: |
| return destBlendFactorStateAlpha; |
| case BLENDOP_MIN: |
| return BLEND_ONE; |
| case BLENDOP_MAX: |
| return BLEND_ONE; |
| default: |
| ASSERT(false); |
| } |
| |
| return destBlendFactorStateAlpha; |
| } |
| } |
| |
| BlendOperation Context::blendOperationAlpha() |
| { |
| if(!separateAlphaBlendEnable) |
| { |
| return blendOperation(); |
| } |
| else |
| { |
| switch(blendOperationStateAlpha) |
| { |
| case BLENDOP_ADD: |
| if(sourceBlendFactorAlpha() == BLEND_ZERO) |
| { |
| if(destBlendFactorAlpha() == BLEND_ZERO) |
| { |
| return BLENDOP_NULL; |
| } |
| else |
| { |
| return BLENDOP_DEST; |
| } |
| } |
| else if(sourceBlendFactorAlpha() == BLEND_ONE) |
| { |
| if(destBlendFactorAlpha() == BLEND_ZERO) |
| { |
| return BLENDOP_SOURCE; |
| } |
| else |
| { |
| return BLENDOP_ADD; |
| } |
| } |
| else |
| { |
| if(destBlendFactorAlpha() == BLEND_ZERO) |
| { |
| return BLENDOP_SOURCE; |
| } |
| else |
| { |
| return BLENDOP_ADD; |
| } |
| } |
| case BLENDOP_SUB: |
| if(sourceBlendFactorAlpha() == BLEND_ZERO) |
| { |
| return BLENDOP_NULL; // Negative, clamped to zero |
| } |
| else if(sourceBlendFactorAlpha() == BLEND_ONE) |
| { |
| if(destBlendFactorAlpha() == BLEND_ZERO) |
| { |
| return BLENDOP_SOURCE; |
| } |
| else |
| { |
| return BLENDOP_SUB; |
| } |
| } |
| else |
| { |
| if(destBlendFactorAlpha() == BLEND_ZERO) |
| { |
| return BLENDOP_SOURCE; |
| } |
| else |
| { |
| return BLENDOP_SUB; |
| } |
| } |
| case BLENDOP_INVSUB: |
| if(sourceBlendFactorAlpha() == BLEND_ZERO) |
| { |
| if(destBlendFactorAlpha() == BLEND_ZERO) |
| { |
| return BLENDOP_NULL; |
| } |
| else |
| { |
| return BLENDOP_DEST; |
| } |
| } |
| else if(sourceBlendFactorAlpha() == BLEND_ONE) |
| { |
| if(destBlendFactorAlpha() == BLEND_ZERO) |
| { |
| return BLENDOP_NULL; // Negative, clamped to zero |
| } |
| else |
| { |
| return BLENDOP_INVSUB; |
| } |
| } |
| else |
| { |
| if(destBlendFactorAlpha() == BLEND_ZERO) |
| { |
| return BLENDOP_NULL; // Negative, clamped to zero |
| } |
| else |
| { |
| return BLENDOP_INVSUB; |
| } |
| } |
| case BLENDOP_MIN: |
| return BLENDOP_MIN; |
| case BLENDOP_MAX: |
| return BLENDOP_MAX; |
| default: |
| ASSERT(false); |
| } |
| |
| return blendOperationStateAlpha; |
| } |
| } |
| |
| bool Context::indexedVertexBlendActive() |
| { |
| if(vertexShader) |
| { |
| return false; |
| } |
| |
| return indexedVertexBlendEnable; |
| } |
| |
| int Context::vertexBlendMatrixCountActive() |
| { |
| if(vertexShader) |
| { |
| return 0; |
| } |
| |
| return vertexBlendMatrixCount; |
| } |
| |
| bool Context::localViewerActive() |
| { |
| if(vertexShader) |
| { |
| return false; |
| } |
| |
| return localViewer; |
| } |
| |
| bool Context::normalizeNormalsActive() |
| { |
| if(vertexShader) |
| { |
| return false; |
| } |
| |
| return normalizeNormals; |
| } |
| |
| FogMode Context::vertexFogModeActive() |
| { |
| if(vertexShader || !fogActive()) |
| { |
| return FOG_NONE; |
| } |
| |
| return vertexFogMode; |
| } |
| |
| bool Context::rangeFogActive() |
| { |
| if(vertexShader || !fogActive()) |
| { |
| return false; |
| } |
| |
| return rangeFogEnable; |
| } |
| |
| TexGen Context::texGenActive(int stage) |
| { |
| if(vertexShader || !texCoordActive(stage)) |
| { |
| return TEXGEN_PASSTHRU; |
| } |
| |
| return texGen[stage]; |
| } |
| |
| int Context::textureTransformCountActive(int stage) |
| { |
| if(vertexShader || !texCoordActive(stage)) |
| { |
| return 0; |
| } |
| |
| return textureTransformCount[stage]; |
| } |
| |
| int Context::texCoordIndexActive(int stage) |
| { |
| if(vertexShader || !texCoordActive(stage)) |
| { |
| return stage; |
| } |
| |
| return textureStage[stage].texCoordIndex; |
| } |
| |
| bool Context::perspectiveActive() |
| { |
| if(!colorUsed()) |
| { |
| return false; |
| } |
| |
| if(!perspectiveCorrection) |
| { |
| return false; |
| } |
| |
| if(isDrawPoint(true)) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool Context::diffuseUsed() |
| { |
| return diffuseUsed(0) || diffuseUsed(1) || diffuseUsed(2) || diffuseUsed(3); |
| } |
| |
| bool Context::diffuseUsed(int component) |
| { |
| if(!colorUsed()) |
| { |
| return false; |
| } |
| |
| if(pixelShader) |
| { |
| return pixelShader->usesDiffuse(component); |
| } |
| |
| // Directly using the diffuse input color |
| for(int i = 0; i < 8; i++) |
| { |
| if(textureStage[i].isStageDisabled()) |
| { |
| break; |
| } |
| |
| if(textureStage[i].usesDiffuse()) |
| { |
| return true; |
| } |
| } |
| |
| // Using the current color (initialized to diffuse) before it's overwritten |
| for(int i = 0; i < 8; i++) |
| { |
| if(textureStage[i].usesCurrent() || textureStage[i].isStageDisabled()) // Current color contains diffuse before being overwritten |
| { |
| return true; |
| } |
| |
| if(textureStage[i].writesCurrent()) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool Context::diffuseActive() |
| { |
| return diffuseActive(0) || diffuseActive(1) || diffuseActive(2) || diffuseActive(3); |
| } |
| |
| bool Context::diffuseActive(int component) |
| { |
| if(!colorUsed()) |
| { |
| return false; |
| } |
| |
| // Vertex processor provides diffuse component |
| bool vertexDiffuse; |
| |
| if(vertexShader) |
| { |
| vertexDiffuse = vertexShader->output[D0][component].active(); |
| } |
| else if(!preTransformed) |
| { |
| vertexDiffuse = input[Color0] || lightingEnable; |
| } |
| else |
| { |
| vertexDiffuse = input[Color0]; |
| } |
| |
| // Pixel processor requires diffuse component |
| bool pixelDiffuse = diffuseUsed(component); |
| |
| return vertexDiffuse && pixelDiffuse; |
| } |
| |
| bool Context::specularUsed() |
| { |
| return Context::specularUsed(0) || Context::specularUsed(1) || Context::specularUsed(2) || Context::specularUsed(3); |
| } |
| |
| bool Context::specularUsed(int component) |
| { |
| if(!colorUsed()) |
| { |
| return false; |
| } |
| |
| if(pixelShader) |
| { |
| return pixelShader->usesSpecular(component); |
| } |
| |
| bool pixelSpecular = specularEnable; |
| |
| for(int i = 0; i < 8; i++) |
| { |
| if(textureStage[i].isStageDisabled()) break; |
| |
| pixelSpecular = pixelSpecular || textureStage[i].usesSpecular(); |
| } |
| |
| return pixelSpecular; |
| } |
| |
| bool Context::specularActive() |
| { |
| return specularActive(0) || specularActive(1) || specularActive(2) || specularActive(3); |
| } |
| |
| bool Context::specularActive(int component) |
| { |
| if(!colorUsed()) |
| { |
| return false; |
| } |
| |
| // Vertex processor provides specular component |
| bool vertexSpecular; |
| |
| if(!vertexShader) |
| { |
| vertexSpecular = input[Color1] || (lightingEnable && specularEnable); |
| } |
| else |
| { |
| vertexSpecular = vertexShader->output[D1][component].active(); |
| } |
| |
| // Pixel processor requires specular component |
| bool pixelSpecular = specularUsed(component); |
| |
| return vertexSpecular && pixelSpecular; |
| } |
| |
| bool Context::colorActive(int color, int component) |
| { |
| if(color == 0) |
| { |
| return diffuseActive(component); |
| } |
| else |
| { |
| return specularActive(component); |
| } |
| } |
| |
| bool Context::textureActive() |
| { |
| for(int i = 0; i < 8; i++) |
| { |
| if(textureActive(i)) |
| { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool Context::textureActive(int coordinate) |
| { |
| return textureActive(coordinate, 0) || textureActive(coordinate, 1) || textureActive(coordinate, 2) || textureActive(coordinate, 3); |
| } |
| |
| bool Context::textureActive(int coordinate, int component) |
| { |
| if(!colorUsed()) |
| { |
| return false; |
| } |
| |
| if(!texCoordActive(coordinate, component)) |
| { |
| return false; |
| } |
| |
| if(textureTransformProject[coordinate] && pixelShaderVersion() <= 0x0103) |
| { |
| if(textureTransformCount[coordinate] == 2) |
| { |
| if(component == 1) return true; |
| } |
| else if(textureTransformCount[coordinate] == 3) |
| { |
| if(component == 2) return true; |
| } |
| else if(textureTransformCount[coordinate] == 4 || textureTransformCount[coordinate] == 0) |
| { |
| if(component == 3) return true; |
| } |
| } |
| |
| if(!pixelShader) |
| { |
| bool texture = textureStage[coordinate].usesTexture(); |
| bool cube = sampler[coordinate].hasCubeTexture(); |
| bool volume = sampler[coordinate].hasVolumeTexture(); |
| |
| if(texture) |
| { |
| for(int i = coordinate; i >= 0; i--) |
| { |
| if(textureStage[i].stageOperation == TextureStage::STAGE_DISABLE) |
| { |
| return false; |
| } |
| } |
| } |
| |
| switch(component) |
| { |
| case 0: |
| return texture; |
| case 1: |
| return texture; |
| case 2: |
| return (texture && (cube || volume)); |
| case 3: |
| return false; |
| } |
| } |
| else |
| { |
| return pixelShader->usesTexture(coordinate, component); |
| } |
| |
| return false; |
| } |
| |
| unsigned short Context::pixelShaderVersion() const |
| { |
| return pixelShader ? pixelShader->getVersion() : 0x0000; |
| } |
| |
| unsigned short Context::vertexShaderVersion() const |
| { |
| return vertexShader ? vertexShader->getVersion() : 0x0000; |
| } |
| |
| int Context::getMultiSampleCount() const |
| { |
| return renderTarget[0] ? renderTarget[0]->getMultiSampleCount() : 1; |
| } |
| |
| int Context::getSuperSampleCount() const |
| { |
| return renderTarget[0] ? renderTarget[0]->getSuperSampleCount() : 1; |
| } |
| |
| Format Context::renderTargetInternalFormat(int index) |
| { |
| if(renderTarget[index]) |
| { |
| return renderTarget[index]->getInternalFormat(); |
| } |
| else |
| { |
| return FORMAT_NULL; |
| } |
| } |
| |
| int Context::colorWriteActive() |
| { |
| return colorWriteActive(0) | colorWriteActive(1) | colorWriteActive(2) | colorWriteActive(3); |
| } |
| |
| int Context::colorWriteActive(int index) |
| { |
| if(!renderTarget[index] || renderTarget[index]->getInternalFormat() == FORMAT_NULL) |
| { |
| return 0; |
| } |
| |
| if(blendOperation() == BLENDOP_DEST && destBlendFactor() == BLEND_ONE && |
| (!separateAlphaBlendEnable || (blendOperationAlpha() == BLENDOP_DEST && destBlendFactorAlpha() == BLEND_ONE))) |
| { |
| return 0; |
| } |
| |
| return colorWriteMask[index]; |
| } |
| |
| bool Context::colorUsed() |
| { |
| return colorWriteActive() || alphaTestActive() || (pixelShader && pixelShader->containsKill()); |
| } |
| } |