/* This file is part of jpcsp. Jpcsp is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Jpcsp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Jpcsp. If not, see <http://www.gnu.org/licenses/>. */ package jpcsp.graphics.RE; import jpcsp.graphics.GeCommands; import jpcsp.graphics.Uniforms; import jpcsp.settings.Settings; /** * @author gid15 * * This RenderingEngine class implements the required logic * to use the OpenGL fixed-function pipeline, i.e. without shaders. * * This class is implemented as a Proxy, forwarding the non-relevant calls * to the proxy. * * When this class splits one call into multiple calls, they are sent to the * complete RenderingEngine pipeline, not just the proxy, taking advantage * of other RenderingEngines up in the pipeline (e.g. the proxy class removing * redundant calls). */ public class REFixedFunction extends BaseRenderingEngineFunction { private ShaderProgram stencilShaderProgram; public REFixedFunction(IRenderingEngine proxy) { super(proxy); } @Override public void setTextureFunc(int func, boolean alphaUsed, boolean colorDoubled) { if (colorDoubled || !alphaUsed) { // GL_RGB_SCALE is only used in OpenGL when GL_TEXTURE_ENV_MODE is GL_COMBINE // See http://www.opengl.org/sdk/docs/man/xhtml/glTexEnv.xml switch (func) { case GeCommands.TFUNC_FRAGMENT_DOUBLE_TEXTURE_EFECT_MODULATE: // Cv = Cp * Cs // Av = Ap * As func = RE_TEXENV_COMBINE; re.setTexEnv(RE_TEXENV_COMBINE_RGB, RE_TEXENV_MODULATE); re.setTexEnv(RE_TEXENV_SRC0_RGB, RE_TEXENV_TEXTURE); re.setTexEnv(RE_TEXENV_OPERAND0_RGB, RE_TEXENV_SRC_COLOR); re.setTexEnv(RE_TEXENV_SRC1_RGB, RE_TEXENV_PREVIOUS); re.setTexEnv(RE_TEXENV_OPERAND1_RGB, RE_TEXENV_SRC_COLOR); if (alphaUsed) { re.setTexEnv(RE_TEXENV_COMBINE_ALPHA, RE_TEXENV_MODULATE); re.setTexEnv(RE_TEXENV_SRC0_ALPHA, RE_TEXENV_TEXTURE); re.setTexEnv(RE_TEXENV_OPERAND0_ALPHA, RE_TEXENV_SRC_ALPHA); re.setTexEnv(RE_TEXENV_SRC1_ALPHA, RE_TEXENV_PREVIOUS); re.setTexEnv(RE_TEXENV_OPERAND1_ALPHA, RE_TEXENV_SRC_ALPHA); } else { re.setTexEnv(RE_TEXENV_COMBINE_ALPHA, RE_TEXENV_REPLACE); re.setTexEnv(RE_TEXENV_SRC0_ALPHA, RE_TEXENV_PREVIOUS); re.setTexEnv(RE_TEXENV_OPERAND0_ALPHA, RE_TEXENV_SRC_ALPHA); } break; case GeCommands.TFUNC_FRAGMENT_DOUBLE_TEXTURE_EFECT_DECAL: func = RE_TEXENV_COMBINE; // Cv = Cs * As + Cp * (1 - As) // Av = Ap if (!alphaUsed) { // DECAL mode with ignored Alpha is always using // the equivalent of Alpha = 1.0 on PSP. // Simplified version when As == 1: // Cv = Cs re.setTexEnv(RE_TEXENV_COMBINE_RGB, RE_TEXENV_REPLACE); re.setTexEnv(RE_TEXENV_SRC0_RGB, RE_TEXENV_TEXTURE); re.setTexEnv(RE_TEXENV_OPERAND0_RGB, RE_TEXENV_SRC_COLOR); } else { re.setTexEnv(RE_TEXENV_COMBINE_RGB, RE_TEXENV_INTERPOLATE); re.setTexEnv(RE_TEXENV_SRC0_RGB, RE_TEXENV_TEXTURE); re.setTexEnv(RE_TEXENV_OPERAND0_RGB, RE_TEXENV_SRC_COLOR); re.setTexEnv(RE_TEXENV_SRC1_RGB, RE_TEXENV_PREVIOUS); re.setTexEnv(RE_TEXENV_OPERAND1_RGB, RE_TEXENV_SRC_COLOR); re.setTexEnv(RE_TEXENV_SRC2_RGB, RE_TEXENV_TEXTURE); re.setTexEnv(RE_TEXENV_OPERAND2_RGB, RE_TEXENV_SRC_ALPHA); } re.setTexEnv(RE_TEXENV_COMBINE_ALPHA, RE_TEXENV_REPLACE); re.setTexEnv(RE_TEXENV_SRC0_ALPHA, RE_TEXENV_PREVIOUS); re.setTexEnv(RE_TEXENV_OPERAND0_ALPHA, RE_TEXENV_SRC_ALPHA); break; case GeCommands.TFUNC_FRAGMENT_DOUBLE_TEXTURE_EFECT_BLEND: // Cv = Cc * Cs + Cp * (1 - Cs) // Av = As * Ap func = RE_TEXENV_COMBINE; re.setTexEnv(RE_TEXENV_COMBINE_RGB, RE_TEXENV_INTERPOLATE); re.setTexEnv(RE_TEXENV_SRC0_RGB, RE_TEXENV_CONSTANT); re.setTexEnv(RE_TEXENV_OPERAND0_RGB, RE_TEXENV_SRC_COLOR); re.setTexEnv(RE_TEXENV_SRC1_RGB, RE_TEXENV_PREVIOUS); re.setTexEnv(RE_TEXENV_OPERAND1_RGB, RE_TEXENV_SRC_COLOR); re.setTexEnv(RE_TEXENV_SRC2_RGB, RE_TEXENV_TEXTURE); re.setTexEnv(RE_TEXENV_OPERAND2_RGB, RE_TEXENV_SRC_COLOR); if (alphaUsed) { re.setTexEnv(RE_TEXENV_COMBINE_ALPHA, RE_TEXENV_MODULATE); re.setTexEnv(RE_TEXENV_SRC0_ALPHA, RE_TEXENV_TEXTURE); re.setTexEnv(RE_TEXENV_OPERAND0_ALPHA, RE_TEXENV_SRC_ALPHA); re.setTexEnv(RE_TEXENV_SRC1_ALPHA, RE_TEXENV_PREVIOUS); re.setTexEnv(RE_TEXENV_OPERAND1_ALPHA, RE_TEXENV_SRC_ALPHA); } else { re.setTexEnv(RE_TEXENV_COMBINE_ALPHA, RE_TEXENV_REPLACE); re.setTexEnv(RE_TEXENV_SRC0_ALPHA, RE_TEXENV_PREVIOUS); re.setTexEnv(RE_TEXENV_OPERAND0_ALPHA, RE_TEXENV_SRC_ALPHA); } break; case GeCommands.TFUNC_FRAGMENT_DOUBLE_TEXTURE_EFECT_REPLACE: // Cv = Cs // Av = As func = RE_TEXENV_COMBINE; re.setTexEnv(RE_TEXENV_COMBINE_RGB, RE_TEXENV_REPLACE); re.setTexEnv(RE_TEXENV_SRC0_RGB, RE_TEXENV_TEXTURE); re.setTexEnv(RE_TEXENV_OPERAND0_RGB, RE_TEXENV_SRC_COLOR); if (alphaUsed) { re.setTexEnv(RE_TEXENV_COMBINE_ALPHA, RE_TEXENV_REPLACE); re.setTexEnv(RE_TEXENV_SRC0_ALPHA, RE_TEXENV_TEXTURE); re.setTexEnv(RE_TEXENV_OPERAND0_ALPHA, RE_TEXENV_SRC_ALPHA); } else { re.setTexEnv(RE_TEXENV_COMBINE_ALPHA, RE_TEXENV_REPLACE); re.setTexEnv(RE_TEXENV_SRC0_ALPHA, RE_TEXENV_PREVIOUS); re.setTexEnv(RE_TEXENV_OPERAND0_ALPHA, RE_TEXENV_SRC_ALPHA); } break; case GeCommands.TFUNC_FRAGMENT_DOUBLE_TEXTURE_EFECT_ADD: // Cv = Cp + Cs // Av = Ap * As func = RE_TEXENV_COMBINE; re.setTexEnv(RE_TEXENV_COMBINE_RGB, RE_TEXENV_ADD); re.setTexEnv(RE_TEXENV_SRC0_RGB, RE_TEXENV_TEXTURE); re.setTexEnv(RE_TEXENV_OPERAND0_RGB, RE_TEXENV_SRC_COLOR); re.setTexEnv(RE_TEXENV_SRC1_RGB, RE_TEXENV_PREVIOUS); re.setTexEnv(RE_TEXENV_OPERAND1_RGB, RE_TEXENV_SRC_COLOR); if (alphaUsed) { re.setTexEnv(RE_TEXENV_COMBINE_ALPHA, RE_TEXENV_MODULATE); re.setTexEnv(RE_TEXENV_SRC0_ALPHA, RE_TEXENV_TEXTURE); re.setTexEnv(RE_TEXENV_OPERAND0_ALPHA, RE_TEXENV_SRC_ALPHA); re.setTexEnv(RE_TEXENV_SRC1_ALPHA, RE_TEXENV_PREVIOUS); re.setTexEnv(RE_TEXENV_OPERAND1_ALPHA, RE_TEXENV_SRC_ALPHA); } else { re.setTexEnv(RE_TEXENV_COMBINE_ALPHA, RE_TEXENV_REPLACE); re.setTexEnv(RE_TEXENV_SRC0_ALPHA, RE_TEXENV_PREVIOUS); re.setTexEnv(RE_TEXENV_OPERAND0_ALPHA, RE_TEXENV_SRC_ALPHA); } break; } } super.setTextureFunc(func, alphaUsed, colorDoubled); } @Override public void disableFlag(int flag) { if (canUpdateFlag(flag)) { super.disableFlag(flag); } } @Override public void enableFlag(int flag) { if (canUpdateFlag(flag)) { super.enableFlag(flag); } } @Override public void enableVertexAttribArray(int id) { // This call is used only by Shader } @Override public void disableVertexAttribArray(int id) { // This call is used only by Shader } @Override public boolean canNativeClut(int textureAddress, boolean textureSwizzle) { // Shaders are required for native clut return false; } @Override public boolean setCopyRedToAlpha(boolean copyRedToAlpha) { if (copyRedToAlpha) { // The stencil index is now available in the red channel of the stencil texture. // We need to copy it to the alpha channel of the GE texture. // As I've not found how to perform this copy from one channel into another channel // using the OpenGL fixed pipeline functionality, // we use a small fragment shader program. if (stencilShaderProgram == null) { if (!re.isShaderAvailable()) { log.info("Shaders are not available on your computer. They are required to save stencil information into the GE texture. Saving of the stencil information has been disabled."); return false; } // The fragment shader is just copying the stencil texture red channel to the GE texture alpha channel String fragmentShaderSource = "uniform sampler2D tex;" + "void main() {" + " gl_FragColor.a = texture2DProj(tex, gl_TexCoord[0].xyz).r;" + "}"; int shaderId = re.createShader(IRenderingEngine.RE_FRAGMENT_SHADER); boolean compiled = re.compilerShader(shaderId, fragmentShaderSource); if (!compiled) { log.error(String.format("Cannot compile shader required for storing stencil information into the GE texture: %s", re.getShaderInfoLog(shaderId))); return false; } int stencilShaderProgramId = re.createProgram(); re.attachShader(stencilShaderProgramId, shaderId); boolean linked = re.linkProgram(stencilShaderProgramId); if (!linked) { log.error(String.format("Cannot link shader required for storing stencil information into the GE texture: %s", re.getProgramInfoLog(stencilShaderProgramId))); return false; } Uniforms.tex.allocateId(re, stencilShaderProgramId); stencilShaderProgram = new ShaderProgram(); stencilShaderProgram.setProgramId(re, stencilShaderProgramId); if (!Settings.getInstance().readBool("emu.useshaders")) { log.info("Shaders are disabled in the Jpcsp video settings. However a small shader program is required to implement the saving of the Stencil information into the GE texture. This small shader program will still be used even though the shaders are disabled in the settings. This was just for your information, you do not need to take special actions."); } } stencilShaderProgram.use(re); re.setUniform(Uniforms.tex.getId(stencilShaderProgram.getProgramId()), REShader.ACTIVE_TEXTURE_NORMAL); re.checkAndLogErrors("setUniform"); } else { // Disable the shader program re.useProgram(0); } return super.setCopyRedToAlpha(copyRedToAlpha); } @Override public void setFogDist(float end, float scale) { if (end != 0f && scale != 0f) { float glEnd = end; float glStart = end - (1f / scale); if (scale < 0f) { // OpenGL need positive values for glStart & glEnd glEnd = -glEnd; glStart = -glStart; } super.setFogDist(glStart, glEnd); } } }