package rajawali.materials; import java.util.List; import rajawali.Camera; import rajawali.lights.ALight; import rajawali.lights.DirectionalLight; import rajawali.lights.PointLight; import rajawali.lights.SpotLight; import rajawali.math.Number3D; import rajawali.renderer.RajawaliRenderer; import android.graphics.Color; import android.opengl.GLES20; public abstract class AAdvancedMaterial extends AMaterial { protected static final int MAX_LIGHTS = RajawaliRenderer.getMaxLights(); public static final String M_FOG_VERTEX_VARS = "\n#ifdef FOG_ENABLED\n" + " uniform float uFogNear;\n" + " uniform float uFogFar;\n" + " uniform bool uFogEnabled;\n" + " varying float vFogDensity;\n" + "#endif\n"; public static final String M_FOG_VERTEX_DENSITY = "\n#ifdef FOG_ENABLED\n" + " vFogDensity = 0.0;\n" + " if (uFogEnabled == true){\n" + " vFogDensity = (gl_Position.z - uFogNear) / (uFogFar - uFogNear);\n" + " vFogDensity = clamp(vFogDensity, 0.0, 1.0);\n" + " }\n" + "#endif\n"; public static final String M_FOG_FRAGMENT_VARS = "\n#ifdef FOG_ENABLED\n" + " uniform vec3 uFogColor;\n" + " varying float vFogDensity;\n" + "#endif\n"; public static final String M_FOG_FRAGMENT_COLOR = "\n#ifdef FOG_ENABLED\n" + " gl_FragColor.rgb = mix(gl_FragColor.rgb, uFogColor, vFogDensity);\n" + "#endif\n"; public static final String M_SKELETAL_ANIM_VERTEX_VARS = "\n#ifdef SKELETAL_ANIM\n" + "uniform mat4 uBoneMatrix[%NUM_JOINTS%];\n" + "attribute vec4 vBoneIndex1;\n" + "attribute vec4 vBoneWeight1;\n" + " #ifdef VERTEX_WEIGHT_8\n" + " attribute vec4 vBoneIndex2;\n" + " attribute vec4 vBoneWeight2;\n" + " #endif\n" + "#endif\n"; public static final String M_SKELETAL_ANIM_VERTEX_MATRIX = "\n#ifdef SKELETAL_ANIM\n" + "mat4 TransformedMatrix = (vBoneWeight1.x * uBoneMatrix[int(vBoneIndex1.x)]) + \n" + " (vBoneWeight1.y * uBoneMatrix[int(vBoneIndex1.y)]) + \n" + " (vBoneWeight1.z * uBoneMatrix[int(vBoneIndex1.z)]) + \n" + " (vBoneWeight1.w * uBoneMatrix[int(vBoneIndex1.w)]);\n" + " #ifdef VERTEX_WEIGHT_8\n" + " TransformedMatrix = TransformedMatrix + (vBoneWeight2.x * uBoneMatrix[int(vBoneIndex2.x)]) + \n" + " (vBoneWeight2.y * uBoneMatrix[int(vBoneIndex2.y)]) + \n" + " (vBoneWeight2.z * uBoneMatrix[int(vBoneIndex2.z)]) + \n" + " (vBoneWeight2.w * uBoneMatrix[int(vBoneIndex2.w)]);\n" + " #endif\n" + "#endif\n"; /** * @deprecated Replaced by {@link #M_FOG_VERTEX_DENSITY} */ @Deprecated public static final String M_FOG_VERTEX_DEPTH = M_FOG_VERTEX_DENSITY; /** * @deprecated No longer needed (density calculation moved to * {@link #M_FOG_VERTEX_DENSITY} in the vertex shader) */ @Deprecated public static final String M_FOG_FRAGMENT_CALC = ""; protected int muNormalMatrixHandle; protected int muAmbientColorHandle; protected int muAmbientIntensityHandle; protected int muFogColorHandle; protected int muFogNearHandle; protected int muFogFarHandle; protected int muFogEnabledHandle; protected int[] muLightColorHandles; protected int[] muLightPowerHandles; protected int[] muLightPositionHandles; protected int[] muLightDirectionHandles; protected int[] muLightAttenuationHandles; protected int[] muSpotCutoffAngleHandles; protected int[] muSpotFalloffHandles; protected float[] mNormalMatrix; protected float[] mTmp, mTmp2; protected float[] mAmbientColor, mAmbientIntensity; protected float[] mFogColor; protected float mFogNear, mFogFar; protected boolean mFogEnabled; // -- skeletal animation private int mvBoneIndex1Handle; private int mvBoneWeight1Handle; private int mvBoneIndex2Handle; private int mvBoneWeight2Handle; private int muBoneMatrixHandle; private int mNumJoints; private int mMaxWeights; protected android.graphics.Matrix mTmpNormalMatrix = new android.graphics.Matrix(); protected android.graphics.Matrix mTmpMvMatrix = new android.graphics.Matrix(); public AAdvancedMaterial() { super(); } public AAdvancedMaterial(int vertex_resID, int fragment_resID) { this(RawMaterialLoader.fetch(vertex_resID), RawMaterialLoader.fetch(fragment_resID), AMaterial.NONE); } public AAdvancedMaterial(int vertex_resID, int fragment_resID, boolean isAnimated) { this(RawMaterialLoader.fetch(vertex_resID), RawMaterialLoader.fetch(fragment_resID), isAnimated); } public AAdvancedMaterial(String vertexShader, String fragmentShader) { this(vertexShader, fragmentShader, AMaterial.NONE); } public AAdvancedMaterial(String vertexShader, String fragmentShader, boolean isAnimated) { this(vertexShader, fragmentShader, isAnimated ? AMaterial.VERTEX_ANIMATION : AMaterial.NONE); } public AAdvancedMaterial(int vertex_resID, int fragment_resID, int parameters) { this(RawMaterialLoader.fetch(vertex_resID), RawMaterialLoader.fetch(fragment_resID), parameters); } public AAdvancedMaterial(String vertexShader, String fragmentShader, int parameters) { super(vertexShader, fragmentShader, parameters); mNormalMatrix = new float[9]; mTmp = new float[9]; mTmp2 = new float[9]; mAmbientColor = new float[] {.2f, .2f, .2f, 1}; mAmbientIntensity = new float[] { .3f, .3f, .3f, 1 }; if(RajawaliRenderer.isFogEnabled()) mFogColor = new float[] { .8f, .8f, .8f }; } @Override public void setLights(List<ALight> lights) { if(lights.size() != mLights.size() && lights.size() != 0) { super.setLights(lights); setShaders(mUntouchedVertexShader, mUntouchedFragmentShader); } else if(lights.size() != 0) { boolean same = true; for(int i=0; i<lights.size(); ++i) if(lights.get(i) != mLights.get(i)) same = false; if(!same) { super.setLights(lights); setShaders(mUntouchedVertexShader, mUntouchedFragmentShader); } } else { super.setLights(lights); } } @Override public void setLightParams() { for(int i=0; i<mLights.size(); ++i) { ALight light = mLights.get(i); GLES20.glUniform3fv(muLightColorHandles[i], 1, light.getColor(), 0); GLES20.glUniform1f(muLightPowerHandles[i], light.getPower()); GLES20.glUniform3fv(muLightPositionHandles[i], 1, light.getPositionArray(), 0); if(light.getLightType() == ALight.DIRECTIONAL_LIGHT) GLES20.glUniform3fv(muLightDirectionHandles[i], 1, ((DirectionalLight)light).getDirection(), 0); else if(light.getLightType() == ALight.SPOT_LIGHT) { GLES20.glUniform3fv(muLightDirectionHandles[i], 1, ((SpotLight)light).getDirection(), 0); GLES20.glUniform4fv(muLightAttenuationHandles[i], 1, ((SpotLight)light).getAttenuation(), 0); GLES20.glUniform1f(muSpotCutoffAngleHandles[i], ((SpotLight)light).getCutoffAngle()); GLES20.glUniform1f(muSpotFalloffHandles[i], ((SpotLight)light).getFalloff()); } else GLES20.glUniform4fv(muLightAttenuationHandles[i], 1, ((PointLight)light).getAttenuation(), 0); } } public void setAmbientColor(float[] color) { mAmbientColor = color; } public void setAmbientColor(Number3D color) { setAmbientColor(color.x, color.y, color.z, 1); } public void setAmbientColor(float r, float g, float b, float a) { setAmbientColor(new float[] { r, g, b, a }); } public void setAmbientColor(int color) { setAmbientColor(new float[] { Color.red(color) / 255f, Color.green(color) / 255f, Color.blue(color) / 255f, Color.alpha(color) / 255f }); } public void setAmbientIntensity(float[] intensity) { mAmbientIntensity = intensity; } public void setAmbientIntensity(float intensity) { mAmbientIntensity[0] = intensity; mAmbientIntensity[1] = intensity; mAmbientIntensity[2] = intensity; mAmbientIntensity[3] = 1; } public void setAmbientIntensity(float r, float g, float b, float a) { setAmbientIntensity(new float[] { r, g, b, a }); } public void setFogColor(int color) { mFogColor[0] = Color.red(color) / 255f; mFogColor[1] = Color.green(color) / 255f; mFogColor[2] = Color.blue(color) / 255f; } public void setFogNear(float near) { mFogNear = near; } public void setFogFar(float far) { mFogFar = far; } public void setFogEnabled(boolean enabled) { mFogEnabled = enabled; } public void setNumJoints(int numJoints) { mNumJoints = numJoints; } public void setMaxWeights(int maxWeights) { mMaxWeights = maxWeights; } public void setBone1Indexes(final int boneIndex1BufferHandle) { if(checkValidHandle(boneIndex1BufferHandle, "bone indexes 1 data")){ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, boneIndex1BufferHandle); GLES20.glEnableVertexAttribArray(mvBoneIndex1Handle); GLES20.glVertexAttribPointer(mvBoneIndex1Handle, 4, GLES20.GL_FLOAT, false, 0, 0); } } public void setBone2Indexes(final int boneIndex2BufferHandle) { if(checkValidHandle(boneIndex2BufferHandle, "bone indexes 2 data")){ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, boneIndex2BufferHandle); GLES20.glEnableVertexAttribArray(mvBoneIndex2Handle); GLES20.glVertexAttribPointer(mvBoneIndex2Handle, 4, GLES20.GL_FLOAT, false, 0, 0); } } public void setBone1Weights(final int boneWeights1BufferHandle) { if(checkValidHandle(boneWeights1BufferHandle, "bone weights 1 data")){ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, boneWeights1BufferHandle); GLES20.glEnableVertexAttribArray(mvBoneWeight1Handle); GLES20.glVertexAttribPointer(mvBoneWeight1Handle, 4, GLES20.GL_FLOAT, false, 0, 0); } } public void setBone2Weights(final int boneWeights2BufferHandle) { if(checkValidHandle(boneWeights2BufferHandle, "bone weights 2 data")){ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, boneWeights2BufferHandle); GLES20.glEnableVertexAttribArray(mvBoneWeight2Handle); GLES20.glVertexAttribPointer(mvBoneWeight2Handle, 4, GLES20.GL_FLOAT, false, 0, 0); } } public void setBoneMatrix(float[] boneMatrix) { if(checkValidHandle(muBoneMatrixHandle, null)) GLES20.glUniformMatrix4fv(muBoneMatrixHandle, mNumJoints, false, boneMatrix, 0); } @Override public void useProgram() { super.useProgram(); GLES20.glUniform4fv(muAmbientColorHandle, 1, mAmbientColor, 0); GLES20.glUniform4fv(muAmbientIntensityHandle, 1, mAmbientIntensity, 0); if(mFogEnabled) { GLES20.glUniform3fv(muFogColorHandle, 1, mFogColor, 0); GLES20.glUniform1f(muFogNearHandle, mFogNear); GLES20.glUniform1f(muFogFarHandle, mFogFar); GLES20.glUniform1i(muFogEnabledHandle, mFogEnabled == true ? GLES20.GL_TRUE : GLES20.GL_FALSE); } } @Override public void setCamera(Camera camera) { super.setCamera(camera); if(camera.isFogEnabled()) { setFogColor(camera.getFogColor()); setFogNear(camera.getFogNear()); setFogFar(camera.getFogFar()); setFogEnabled(true); } else { setFogEnabled(false); } } @Override public void setShaders(String vertexShader, String fragmentShader) { vertexShader = replaceShaderVars(vertexShader); fragmentShader = replaceShaderVars(fragmentShader); StringBuffer lightVars = new StringBuffer(); int numLights = mLights.size(); for(int i=0; i<numLights; ++i) { lightVars.append("uniform vec3 uLightColor").append(i).append(";\n"); lightVars.append("uniform float uLightPower").append(i).append(";\n"); lightVars.append("uniform int uLightType").append(i).append(";\n"); lightVars.append("uniform vec3 uLightPosition").append(i).append(";\n"); lightVars.append("uniform vec3 uLightDirection").append(i).append(";\n"); lightVars.append("uniform vec4 uLightAttenuation").append(i).append(";\n"); lightVars.append("varying float vAttenuation").append(i).append(";\n"); lightVars.append("uniform float uSpotExponent").append(i).append(";\n"); lightVars.append("uniform float uSpotCutoffAngle").append(i).append(";\n"); lightVars.append("uniform float uSpotFalloff").append(i).append(";\n"); } vertexShader = vertexShader.replace("%LIGHT_VARS%", lightVars.toString()); if(mSkeletalAnimationEnabled) { if(mMaxWeights > 4) vertexShader = "\n#define VERTEX_WEIGHT_8\n" + vertexShader; vertexShader = vertexShader.replace("%NUM_JOINTS%", Integer.toString(mNumJoints)); } fragmentShader = fragmentShader.replace("%LIGHT_VARS%", lightVars.toString()); super.setShaders(vertexShader, fragmentShader); muNormalMatrixHandle = getUniformLocation("uNMatrix"); muAmbientColorHandle = getUniformLocation("uAmbientColor"); muAmbientIntensityHandle = getUniformLocation("uAmbientIntensity"); muLightAttenuationHandles = new int[numLights]; muLightColorHandles = new int[numLights]; muLightDirectionHandles = new int[numLights]; muLightPositionHandles = new int[numLights]; muLightPowerHandles = new int[numLights]; muSpotCutoffAngleHandles = new int[numLights]; muSpotFalloffHandles = new int[numLights]; for(int i=0; i<mLights.size(); ++i) { muLightColorHandles[i] = getUniformLocation("uLightColor" + i); muLightPowerHandles[i] = getUniformLocation("uLightPower" + i); muLightPositionHandles[i] = getUniformLocation("uLightPosition" + i); muLightDirectionHandles[i] = getUniformLocation("uLightDirection" + i); muLightAttenuationHandles[i] = getUniformLocation("uLightAttenuation" + i); muSpotCutoffAngleHandles[i] = getUniformLocation("uSpotCutoffAngle" + i); muSpotFalloffHandles[i] = getUniformLocation("uSpotFalloff" + i); } if(RajawaliRenderer.isFogEnabled()) { muFogColorHandle = getUniformLocation("uFogColor"); muFogNearHandle = getUniformLocation("uFogNear"); muFogFarHandle = getUniformLocation("uFogFar"); muFogEnabledHandle = getUniformLocation("uFogEnabled"); } if(mSkeletalAnimationEnabled) { mvBoneIndex1Handle = getAttribLocation("vBoneIndex1"); mvBoneWeight1Handle = getAttribLocation("vBoneWeight1"); if(mMaxWeights>4){//TODO check if maxWeights > 8 -> throw exception mvBoneIndex2Handle = getAttribLocation("vBoneIndex2"); mvBoneWeight2Handle = getAttribLocation("vBoneWeight2"); } muBoneMatrixHandle = getUniformLocation("uBoneMatrix"); } } @Override public void setModelMatrix(float[] modelMatrix) { super.setModelMatrix(modelMatrix); mTmp2[0] = modelMatrix[0]; mTmp2[1] = modelMatrix[1]; mTmp2[2] = modelMatrix[2]; mTmp2[3] = modelMatrix[4]; mTmp2[4] = modelMatrix[5]; mTmp2[5] = modelMatrix[6]; mTmp2[6] = modelMatrix[8]; mTmp2[7] = modelMatrix[9]; mTmp2[8] = modelMatrix[10]; mTmpMvMatrix.setValues(mTmp2); mTmpNormalMatrix.reset(); mTmpMvMatrix.invert(mTmpNormalMatrix); mTmpNormalMatrix.getValues(mTmp); mTmp2[0] = mTmp[0]; mTmp2[1] = mTmp[3]; mTmp2[2] = mTmp[6]; mTmp2[3] = mTmp[1]; mTmp2[4] = mTmp[4]; mTmp2[5] = mTmp[7]; mTmp2[6] = mTmp[2]; mTmp2[7] = mTmp[5]; mTmp2[8] = mTmp[8]; mTmpNormalMatrix.setValues(mTmp2); mTmpNormalMatrix.getValues(mNormalMatrix); GLES20.glUniformMatrix3fv(muNormalMatrixHandle, 1, false, mNormalMatrix, 0); } public void destroy() { super.destroy(); muLightColorHandles = null; muLightPowerHandles = null; muLightPositionHandles = null; muLightDirectionHandles = null; muLightAttenuationHandles = null; muSpotCutoffAngleHandles = null; muSpotFalloffHandles = null; mNormalMatrix = null; mTmp = null; mTmp2 = null; mAmbientColor = null; mAmbientIntensity = null; mFogColor = null; mTmpNormalMatrix = null; mTmpMvMatrix = null; } private final String replaceShaderVars(String shader) { if (shader.contains("%FOG_VERTEX_VARS%")) shader = shader.replace("%FOG_VERTEX_VARS%", M_FOG_VERTEX_VARS); if (shader.contains("M_FOG_VERTEX_DENSITY")) shader = shader.replace("M_FOG_VERTEX_DENSITY", M_FOG_VERTEX_DENSITY); if (shader.contains("%FOG_FRAGMENT_VARS%")) shader = shader.replace("%FOG_FRAGMENT_VARS%", M_FOG_FRAGMENT_VARS); if (shader.contains("M_FOG_FRAGMENT_COLOR")) shader = shader.replace("M_FOG_FRAGMENT_COLOR", M_FOG_FRAGMENT_COLOR); if (shader.contains("%SKELETAL_ANIM_VERTEX_VARS%")) shader = shader.replace("%SKELETAL_ANIM_VERTEX_VARS%", M_SKELETAL_ANIM_VERTEX_VARS); if (shader.contains("M_SKELETAL_ANIM_VERTEX_MATRIX")) shader = shader.replace("M_SKELETAL_ANIM_VERTEX_MATRIX", M_SKELETAL_ANIM_VERTEX_MATRIX); return shader; } }