// // KRPointLight.cpp // KREngine // // Created by Kearwood Gilbert on 12-04-05. // Copyright (c) 2012 Kearwood Software. All rights reserved. // #include "KREngine-common.h" #include "KRPointLight.h" #include "KRCamera.h" #include "KRContext.h" #include "KRStockGeometry.h" /* static */ void KRPointLight::InitNodeInfo(KrNodeInfo* nodeInfo) { KRLight::InitNodeInfo(nodeInfo); // No additional members } KRPointLight::KRPointLight(KRScene &scene, std::string name) : KRLight(scene, name) { m_sphereVertices = NULL; m_cVertices = 0; } KRPointLight::~KRPointLight() { if(m_sphereVertices) { delete m_sphereVertices; m_cVertices = 0; } } std::string KRPointLight::getElementName() { return "point_light"; } AABB KRPointLight::getBounds() { float influence_radius = m_decayStart - sqrt(m_intensity * 0.01f) / sqrt(KRLIGHT_MIN_INFLUENCE); if(influence_radius < m_flareOcclusionSize) { influence_radius = m_flareOcclusionSize; } return AABB::Create(Vector3::Create(-influence_radius), Vector3::Create(influence_radius), getModelMatrix()); } void KRPointLight::render(KRCamera *pCamera, std::vector &point_lights, std::vector &directional_lights, std::vector&spot_lights, const KRViewport &viewport, KRNode::RenderPass renderPass) { if(m_lod_visible <= LOD_VISIBILITY_PRESTREAM) return; KRLight::render(pCamera, point_lights, directional_lights, spot_lights, viewport, renderPass); bool bVisualize = renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT && pCamera->settings.bShowDeferred; if(renderPass == KRNode::RENDER_PASS_DEFERRED_LIGHTS || bVisualize) { // Lights are rendered on the second pass of the deferred renderer std::vector this_light; this_light.push_back(this); Vector3 light_position = getLocalTranslation(); float influence_radius = m_decayStart - sqrt(m_intensity * 0.01f) / sqrt(KRLIGHT_MIN_INFLUENCE); Matrix4 sphereModelMatrix = Matrix4(); sphereModelMatrix.scale(influence_radius); sphereModelMatrix.translate(light_position.x, light_position.y, light_position.z); if(viewport.visible(getBounds())) { // Cull out any lights not within the view frustrum Vector3 view_light_position = Matrix4::Dot(viewport.getViewMatrix(), light_position); bool bInsideLight = view_light_position.sqrMagnitude() <= (influence_radius + pCamera->settings.getPerspectiveNearZ()) * (influence_radius + pCamera->settings.getPerspectiveNearZ()); KRPipeline *pShader = getContext().getPipelineManager()->getPipeline(bVisualize ? "visualize_overlay" : (bInsideLight ? "light_point_inside" : "light_point"), pCamera, this_light, std::vector(), std::vector(), 0, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, renderPass); if(getContext().getPipelineManager()->selectPipeline(*pCamera, pShader, viewport, sphereModelMatrix, this_light, std::vector(), std::vector(), 0, renderPass, Vector3::Zero(), 0.0f, Vector4::Zero())) { pShader->setUniform(KRPipeline::KRENGINE_UNIFORM_LIGHT_COLOR, m_color); pShader->setUniform(KRPipeline::KRENGINE_UNIFORM_LIGHT_INTENSITY, m_intensity * 0.01f); pShader->setUniform(KRPipeline::KRENGINE_UNIFORM_LIGHT_DECAY_START, getDecayStart()); pShader->setUniform(KRPipeline::KRENGINE_UNIFORM_LIGHT_CUTOFF, KRLIGHT_MIN_INFLUENCE); pShader->setUniform(KRPipeline::KRENGINE_UNIFORM_LIGHT_POSITION, light_position); if(bVisualize) { // Enable additive blending GLDEBUG(glEnable(GL_BLEND)); GLDEBUG(glBlendFunc(GL_ONE, GL_ONE)); } // Disable z-buffer write GLDEBUG(glDepthMask(GL_FALSE)); if(bInsideLight) { // Disable z-buffer test GLDEBUG(glDisable(GL_DEPTH_TEST)); // Render a full screen quad m_pContext->getMeshManager()->bindVBO(&m_pContext->getMeshManager()->KRENGINE_VBO_DATA_2D_SQUARE_VERTICES, 1.0f); GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); } else { #if GL_OES_vertex_array_object GLDEBUG(glBindVertexArrayOES(0)); #endif m_pContext->getMeshManager()->configureAttribs(1 << KRMesh::KRENGINE_ATTRIB_VERTEX); // Render sphere of light's influence generateMesh(); // Enable z-buffer test GLDEBUG(glEnable(GL_DEPTH_TEST)); GLDEBUG(glDepthFunc(GL_LEQUAL)); GLDEBUG(glDepthRangef(0.0, 1.0)); GLDEBUG(glVertexAttribPointer(KRMesh::KRENGINE_ATTRIB_VERTEX, 3, GL_FLOAT, 0, 0, m_sphereVertices)); GLDEBUG(glDrawArrays(GL_TRIANGLES, 0, m_cVertices)); } } if(bVisualize) { // Enable alpha blending GLDEBUG(glEnable(GL_BLEND)); GLDEBUG(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); } } } } void KRPointLight::generateMesh() { // Create a triangular facet approximation to a sphere // Based on algorithm from Paul Bourke: http://paulbourke.net/miscellaneous/sphere_cylinder/ int iterations = 3; int facet_count = (int)(pow(4, iterations) * 8); if(m_cVertices != facet_count * 3) { if(m_sphereVertices) { free(m_sphereVertices); m_sphereVertices = NULL; } m_cVertices = facet_count * 3; class Facet3 { public: Facet3() { } ~Facet3() { } Vector3 p1; Vector3 p2; Vector3 p3; }; std::vector f = std::vector(facet_count); int i,it; float a; Vector3 p[6] = { Vector3::Create(0,0,1), Vector3::Create(0,0,-1), Vector3::Create(-1,-1,0), Vector3::Create(1,-1,0), Vector3::Create(1,1,0), Vector3::Create(-1,1,0) }; Vector3 pa,pb,pc; int nt = 0,ntold; /* Create the level 0 object */ a = 1.0f / sqrtf(2.0f); for (i=0;i<6;i++) { p[i].x *= a; p[i].y *= a; } f[0].p1 = p[0]; f[0].p2 = p[3]; f[0].p3 = p[4]; f[1].p1 = p[0]; f[1].p2 = p[4]; f[1].p3 = p[5]; f[2].p1 = p[0]; f[2].p2 = p[5]; f[2].p3 = p[2]; f[3].p1 = p[0]; f[3].p2 = p[2]; f[3].p3 = p[3]; f[4].p1 = p[1]; f[4].p2 = p[4]; f[4].p3 = p[3]; f[5].p1 = p[1]; f[5].p2 = p[5]; f[5].p3 = p[4]; f[6].p1 = p[1]; f[6].p2 = p[2]; f[6].p3 = p[5]; f[7].p1 = p[1]; f[7].p2 = p[3]; f[7].p3 = p[2]; nt = 8; /* Bisect each edge and move to the surface of a unit sphere */ for (it=0;it