| // Copyright 2016 The SwiftShader Authors. All Rights Reserved. | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //    http://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | #include "Context.hpp" | 
 |  | 
 | #include "Primitive.hpp" | 
 | #include "Surface.hpp" | 
 | #include "Shader/PixelShader.hpp" | 
 | #include "Shader/VertexShader.hpp" | 
 | #include "Common/Memory.hpp" | 
 | #include "Common/Debug.hpp" | 
 |  | 
 | #include <string.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 leadingVertexFirst = false;         // Flat shading uses first vertex, else last | 
 | 	bool secondaryColor = false;             // Specular lighting is applied after texturing | 
 | 	bool colorsDefaultToZero = 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 < MAX_VERTEX_INPUTS; 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; | 
 |  | 
 | 		for(int i = 0; i < RENDERTARGETS; ++i) | 
 | 		{ | 
 | 			renderTarget[i] = nullptr; | 
 | 		} | 
 | 		depthBuffer = nullptr; | 
 | 		stencilBuffer = nullptr; | 
 |  | 
 | 		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; | 
 |  | 
 | 		rasterizerDiscard = false; | 
 |  | 
 | 		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; | 
 | 		frontFacingCCW = true; | 
 | 		alphaReference = 0.0f; | 
 |  | 
 | 		depthBias = 0.0f; | 
 | 		slopeDepthBias = 0.0f; | 
 |  | 
 | 		for(int i = 0; i < RENDERTARGETS; i++) | 
 | 		{ | 
 | 			colorWriteMask[i] = 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; | 
 | 		transformFeedbackQueryEnabled = false; | 
 | 		transformFeedbackEnabled = 0; | 
 |  | 
 | 		pointSpriteEnable = false; | 
 | 		pointScaleEnable = false; | 
 | 		lineWidth = 1.0f; | 
 |  | 
 | 		writeSRGB = false; | 
 | 		sampleMask = 0xFFFFFFFF; | 
 |  | 
 | 		colorLogicOpEnabled = false; | 
 | 		logicalOperation = LOGICALOP_COPY; | 
 | 	} | 
 |  | 
 | 	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; | 
 | 	} | 
 |  | 
 | 	bool Context::setColorLogicOpEnabled(bool enabled) | 
 | 	{ | 
 | 		bool modified = (Context::colorLogicOpEnabled != enabled); | 
 | 		Context::colorLogicOpEnabled = enabled; | 
 | 		return modified; | 
 | 	} | 
 |  | 
 | 	bool Context::setLogicalOperation(LogicalOperation logicalOperation) | 
 | 	{ | 
 | 		bool modified = (Context::logicalOperation != logicalOperation); | 
 | 		Context::logicalOperation = logicalOperation; | 
 | 		return modified; | 
 | 	} | 
 |  | 
 | 	void Context::setColorVertexEnable(bool colorVertexEnable) | 
 | 	{ | 
 | 		Context::colorVertexEnable = colorVertexEnable; | 
 | 	} | 
 |  | 
 | 	bool Context::fogActive() | 
 | 	{ | 
 | 		if(!colorUsed()) return false; | 
 |  | 
 | 		if(pixelShaderModel() >= 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(transparencyAntialiasing != TRANSPARENCY_NONE) return true; | 
 | 		if(!alphaTestEnable) return false; | 
 | 		if(alphaCompareMode == ALPHA_ALWAYS) return false; | 
 | 		if(alphaReference == 0.0f && alphaCompareMode == ALPHA_GREATEREQUAL) return false; | 
 |  | 
 | 		return true; | 
 | 	} | 
 |  | 
 | 	bool Context::depthBufferActive() | 
 | 	{ | 
 | 		return depthBuffer && depthBufferEnable; | 
 | 	} | 
 |  | 
 | 	bool Context::stencilActive() | 
 | 	{ | 
 | 		return stencilBuffer && 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->getOutput(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(pixelShaderModel() <= 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; | 
 | 	} | 
 |  | 
 | 	LogicalOperation Context::colorLogicOp() | 
 | 	{ | 
 | 		return colorLogicOpEnabled ? logicalOperation : LOGICALOP_COPY; | 
 | 	} | 
 |  | 
 | 	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->getOutput(C0, 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->getOutput(C1, 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] && pixelShaderModel() <= 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::pixelShaderModel() const | 
 | 	{ | 
 | 		return pixelShader ? pixelShader->getShaderModel() : 0x0000; | 
 | 	} | 
 |  | 
 | 	unsigned short Context::vertexShaderModel() const | 
 | 	{ | 
 | 		return vertexShader ? vertexShader->getShaderModel() : 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; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	bool Context::colorWriteActive() | 
 | 	{ | 
 | 		for (int i = 0; i < RENDERTARGETS; i++) | 
 | 		{ | 
 | 			if (colorWriteActive(i)) | 
 | 			{ | 
 | 				return true; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		return false; | 
 | 	} | 
 |  | 
 | 	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()); | 
 | 	} | 
 | } |