// Copyright (c) 2013- PPSSPP Project. // This program 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, version 2.0 or later versions. // This program 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 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "../GPUState.h" #include "Lighting.h" namespace Lighting { static inline Vec3f GetLightVec(u32 lparams[12], int light) { return Vec3(getFloat24(lparams[3 * light]), getFloat24(lparams[3 * light + 1]), getFloat24(lparams[3 * light + 2])); } void Process(VertexData& vertex, bool hasColor) { const int materialupdate = gstate.materialupdate & (hasColor ? 7 : 0); Vec3 vcol0 = vertex.color0.rgb().Cast() * Vec3::AssignToAll(1.0f / 255.0f); Vec3 mec = Vec3::FromRGB(gstate.getMaterialEmissive()); Vec3 mac = (materialupdate & 1) ? vcol0 : Vec3::FromRGB(gstate.getMaterialAmbientRGBA()); Vec3 final_color = mec + mac * Vec3::FromRGB(gstate.getAmbientRGBA()); Vec3 specular_color(0.0f, 0.0f, 0.0f); for (unsigned int light = 0; light < 4; ++light) { // Always calculate texture coords from lighting results if environment mapping is active // TODO: Should specular lighting should affect this, too? Doesn't in GLES. // This should be done even if lighting is disabled altogether. if (gstate.getUVGenMode() == GE_TEXMAP_ENVIRONMENT_MAP) { Vec3 L = GetLightVec(gstate.lpos, light); // In other words, L.Length2() == 0.0f means Dot({0, 0, 1}, worldnormal). float diffuse_factor = L.Length2() == 0.0f ? vertex.worldnormal.z : Dot(L.Normalized(), vertex.worldnormal); if (gstate.getUVLS0() == (int)light) vertex.texturecoords.s() = (diffuse_factor + 1.f) / 2.f; if (gstate.getUVLS1() == (int)light) vertex.texturecoords.t() = (diffuse_factor + 1.f) / 2.f; } } if (!gstate.isLightingEnabled()) return; for (unsigned int light = 0; light < 4; ++light) { if (!gstate.isLightChanEnabled(light)) continue; // L = vector from vertex to light source // TODO: Should transfer the light positions to world/view space for these calculations? Vec3 L = GetLightVec(gstate.lpos, light); if (!gstate.isDirectionalLight(light)) { L -= vertex.worldpos; } // TODO: Should this normalize (0, 0, 0) to (0, 0, 1)? float d = L.Normalize(); float att = 1.f; if (!gstate.isDirectionalLight(light)) { att = 1.f / Dot(GetLightVec(gstate.latt, light), Vec3f(1.0f, d, d * d)); if (att > 1.f) att = 1.f; if (att < 0.f) att = 0.f; } float spot = 1.f; if (gstate.isSpotLight(light)) { Vec3 dir = GetLightVec(gstate.ldir, light); float rawSpot = Dot(dir.Normalized(), L); float cutoff = getFloat24(gstate.lcutoff[light]); if (rawSpot >= cutoff) { float conv = getFloat24(gstate.lconv[light]); spot = pow(rawSpot, conv); } else { spot = 0.f; } } // ambient lighting Vec3 lac = Vec3::FromRGB(gstate.getLightAmbientColor(light)); final_color += lac * mac * att * spot; // diffuse lighting Vec3 ldc = Vec3::FromRGB(gstate.getDiffuseColor(light)); Vec3 mdc = (materialupdate & 2) ? vcol0 : Vec3::FromRGB(gstate.getMaterialDiffuse()); float diffuse_factor = Dot(L, vertex.worldnormal); if (gstate.isUsingPoweredDiffuseLight(light)) { float k = gstate.getMaterialSpecularCoef(); // TODO: Validate Tales of the World: Radiant Mythology (#2424.) // pow(0.0, 0.0) may be undefined, but the PSP seems to treat it as 1.0. if (diffuse_factor <= 0.0f && k == 0.0f) { diffuse_factor = 1.0f; } else { diffuse_factor = pow(diffuse_factor, k); } } if (diffuse_factor > 0.f) { final_color += ldc * mdc * diffuse_factor * att * spot; } if (gstate.isUsingSpecularLight(light)) { Vec3 H = L + Vec3(0.f, 0.f, 1.f); Vec3 lsc = Vec3::FromRGB(gstate.getSpecularColor(light)); Vec3 msc = (materialupdate & 4) ? vcol0 : Vec3::FromRGB(gstate.getMaterialSpecular()); float specular_factor = Dot(H.Normalized(), vertex.worldnormal); float k = gstate.getMaterialSpecularCoef(); specular_factor = pow(specular_factor, k); if (specular_factor > 0.f) { specular_color += lsc * msc * specular_factor * att * spot; } } } int maa = (materialupdate & 1) ? vertex.color0.a() : gstate.getMaterialAmbientA(); int final_alpha = (gstate.getAmbientA() * maa) / 255; if (gstate.isUsingSecondaryColor()) { Vec3 final_color_int = (final_color.Clamp(0.0f, 1.0f) * 255.0f).Cast(); vertex.color0 = Vec4(final_color_int, final_alpha); vertex.color1 = (specular_color.Clamp(0.0f, 1.0f) * 255.0f).Cast(); } else { Vec3 final_color_int = ((final_color + specular_color).Clamp(0.0f, 1.0f) * 255.0f).Cast(); vertex.color0 = Vec4(final_color_int, final_alpha); } } } // namespace