Browse Source

Working on better indirect model, new pluggable direct lighting model

Josh Engebretson 8 years ago
parent
commit
29ae425d5c

+ 1 - 0
Source/Atomic/Core/Thread.cpp

@@ -23,6 +23,7 @@
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
 #include "../Core/Thread.h"
 #include "../Core/Thread.h"
+#include "../IO/Log.h"
 
 
 #ifdef _WIN32
 #ifdef _WIN32
 #include <windows.h>
 #include <windows.h>

+ 4 - 0
Source/Atomic/Core/WorkQueue.cpp

@@ -428,11 +428,15 @@ void WorkQueue::HandleBeginFrame(StringHash eventType, VariantMap& eventData)
 
 
 void WorkQueue::TerminateThreads()
 void WorkQueue::TerminateThreads()
 {
 {
+    // this currently works on Windows, may work on Linux, hangs macOS on applicaiton exit
+#ifdef ATOMIC_PLATFORM_WINDOWS
+
     for (unsigned i = 0; i < threads_.Size(); ++i)
     for (unsigned i = 0; i < threads_.Size(); ++i)
         threads_[i]->Kill();
         threads_[i]->Kill();
 
 
     threads_.Clear();
     threads_.Clear();
 
 
+#endif
 }
 }
 
 
 // ATOMIC END
 // ATOMIC END

+ 10 - 0
Source/Atomic/Math/Random.cpp

@@ -58,4 +58,14 @@ float RandStandardNormal()
     return val;
     return val;
 }
 }
 
 
+// ATOMIC BEGIN
+
+float RandZeroOne()
+{
+    static float invRAND_MAX = 1.0f / RAND_MAX;
+    return float(rand()) * invRAND_MAX;
+}
+
+// ATOMIC END
+
 }
 }

+ 5 - 0
Source/Atomic/Math/Random.h

@@ -36,4 +36,9 @@ ATOMIC_API int Rand();
 /// Return a standard normal distributed number.
 /// Return a standard normal distributed number.
 ATOMIC_API float RandStandardNormal();
 ATOMIC_API float RandStandardNormal();
 
 
+// ATOMIC BEGIN
+/// Returns a random number between 0 and 1
+ATOMIC_API float RandZeroOne();
+// ATOMIC END
+
 }
 }

+ 33 - 0
Source/Atomic/Math/Vector3.h

@@ -407,6 +407,39 @@ public:
 
 
         return *this * (1.0f - t) + rhs * t;
         return *this * (1.0f - t) + rhs * t;
     }
     }
+
+    static void GetRandomInSphere( Vector3& result, const Vector3& center, float radius )
+    {
+        Vector3 dir;
+        GetRandomDirection(dir);
+        result = center + dir * radius;
+    }
+
+    static void GetRandomDirection( Vector3& result )
+    {
+        float  len;
+
+        do
+        {
+           result.x_ = (RandZeroOne() * 2.0f - 1.0f);
+           result.y_ = (RandZeroOne() * 2.0f - 1.0f);
+           result.z_ = (RandZeroOne() * 2.0f - 1.0f);
+           len   = result.Length();
+
+        } while( len > 1.0f );
+
+        result /= len;
+    }
+
+    static void GetRandomHemisphereDirection( Vector3& result, const Vector3& normal )
+    {
+        GetRandomDirection(result);
+
+        if( result.DotProduct(normal) < 0 ) {
+            result = -result;
+        }
+    }
+
     // ATOMIC END
     // ATOMIC END
     
     
     /// Return float data.
     /// Return float data.

+ 57 - 1
Source/AtomicGlow/Common/GlowSettings.h

@@ -67,6 +67,24 @@ namespace AtomicGlow
         float aoMin_;
         float aoMin_;
         float aoMultiply_;
         float aoMultiply_;
 
 
+        // NEW GI
+
+        // Number of photon passes.
+        int photonPassCount_;
+        // Maximum photon tracing depth (number of light bounces).
+        int photonBounceCount_;
+        // The minimum energy that photon should have to continue tracing.
+        float photonEnergyThreshold_;
+        // The reflected light maximum distance. All intersections above this value will be ignored.
+        float photonMaxDistance_;
+
+        // Number of final gather samples.
+        int finalGatherSamples_;
+        // Maximum distance to gather photons at.
+        float finalGatherDistance_;
+        // A radius of circle in which samples are gathered from photon map.
+        int finalGatherRadius_;
+
         GlowSettings()
         GlowSettings()
         {
         {
             SetDefaults();
             SetDefaults();
@@ -156,25 +174,63 @@ namespace AtomicGlow
                 case GLOW_PRESET_FAST_LOW_QUALITY:
                 case GLOW_PRESET_FAST_LOW_QUALITY:
                     lexelDensity_ = 0.16f;
                     lexelDensity_ = 0.16f;
                     nsamples_ = 16;
                     nsamples_ = 16;
-                    giEnabled_ = false;
+                    photonPassCount_          = 8;
+                    photonBounceCount_        = 1;
+                    photonEnergyThreshold_    = 0.05f;
+                    // fix me should be 10, bigger for massive cornell
+                    photonMaxDistance_        = 10;
+
+                    finalGatherSamples_       = 32;
+                    finalGatherDistance_      = 50;
+                    finalGatherRadius_        = 7;
                     break;
                     break;
+
                 case GLOW_PRESET_MEDIUM_QUALITY:
                 case GLOW_PRESET_MEDIUM_QUALITY:
                     lexelDensity_ = 0.32f;
                     lexelDensity_ = 0.32f;
                     nsamples_ = 64;
                     nsamples_ = 64;
                     giEnabled_ = true;
                     giEnabled_ = true;
                     giGranularity_ = 8;
                     giGranularity_ = 8;
+
+                    photonPassCount_          = 16;
+                    photonBounceCount_        = 3;
+                    photonEnergyThreshold_    = 0.05f;
+                    photonMaxDistance_        = 10;
+
+                    finalGatherSamples_       = 64;
+                    finalGatherDistance_      = 50;
+                    finalGatherRadius_        = 7;
                     break;
                     break;
+
                 case GLOW_PRESET_HIGH_QUALITY:
                 case GLOW_PRESET_HIGH_QUALITY:
                     lexelDensity_ = 0.5f;                    
                     lexelDensity_ = 0.5f;                    
                     giEnabled_ = true;
                     giEnabled_ = true;
                     nsamples_ = 256;
                     nsamples_ = 256;
                     giGranularity_ = 8;
                     giGranularity_ = 8;
+
+                    photonPassCount_          = 32;
+                    photonBounceCount_        = 3;
+                    photonEnergyThreshold_    = 0.05f;
+                    photonMaxDistance_        = 10;
+
+                    finalGatherSamples_       = 128;
+                    finalGatherDistance_      = 50;
+                    finalGatherRadius_        = 7;
                     break;
                     break;
+
                 case GLOW_PRESET_SLOW_EXTREME_QUALITY:
                 case GLOW_PRESET_SLOW_EXTREME_QUALITY:
                     lexelDensity_ = 0.65f;
                     lexelDensity_ = 0.65f;
                     nsamples_ = 256;
                     nsamples_ = 256;
                     giEnabled_ = true;
                     giEnabled_ = true;
                     giGranularity_ = 8;
                     giGranularity_ = 8;
+
+                    photonPassCount_          = 64;
+                    photonBounceCount_        = 4;
+                    photonEnergyThreshold_    = 0.05f;
+                    photonMaxDistance_        = 10;
+
+                    finalGatherSamples_       = 1024;
+                    finalGatherDistance_      = 50;
+                    finalGatherRadius_        = 7;
                     break;
                     break;
             }
             }
         }
         }

+ 320 - 279
Source/AtomicGlow/Kernel/BakeLight.cpp

@@ -1,4 +1,5 @@
 // Copyright (c) 2014-2017, THUNDERBEAST GAMES LLC All rights reserved
 // Copyright (c) 2014-2017, THUNDERBEAST GAMES LLC All rights reserved
+// Portions Copyright (c) 2015 Dmitry Sovetov
 // Copyright 2009-2017 Intel Corporation
 // Copyright 2009-2017 Intel Corporation
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -35,443 +36,483 @@
 namespace AtomicGlow
 namespace AtomicGlow
 {
 {
 
 
-const float LIGHT_ANGLE_EPSILON = 0.001f;
-
-// http://www.altdevblogaday.com/2012/05/03/generating-uniformly-distributed-points-on-sphere/
-static inline void GetRandomDirection(Vector3& result)
+BakeLight::BakeLight(Context* context, SceneBaker* sceneBaker) : BakeNode(context, sceneBaker),
+    color_(Color::WHITE),
+    intensity_( 0.0f ),
+    castsShadow_( false )
 {
 {
-    float z = 2.0f * rand() / RAND_MAX - 1.0f;
-    float t = 2.0f * rand() / RAND_MAX * 3.14f;
-    float r = sqrt(1.0f - z * z);
-    result.x_ = r * (float) cos(t);
-    result.y_ = r * (float) sin(t);
-    result.z_ = z;
 }
 }
 
 
-BakeLight::BakeLight(Context* context, SceneBaker* sceneBaker) : BakeNode(context, sceneBaker),
-    range_(0.0f)
+BakeLight::~BakeLight()
 {
 {
 }
 }
 
 
-BakeLight::~BakeLight()
+LightCutoff* BakeLight::GetCutoffModel( void ) const
 {
 {
+    return cutoffModel_;
+}
 
 
+void BakeLight::SetCutoffModel( LightCutoff* value )
+{
+    cutoffModel_ = value;
 }
 }
 
 
-// Zone Lights
+LightInfluence* BakeLight::GetInfluenceModel( void ) const
+{
+    return influenceModel_;
+}
 
 
-ZoneBakeLight::ZoneBakeLight(Context* context, SceneBaker* sceneBaker) : BakeLight(context, sceneBaker)
+void BakeLight::SetInfluenceModel( LightInfluence* value )
 {
 {
+    influenceModel_ = value;
 }
 }
 
 
-ZoneBakeLight::~ZoneBakeLight()
+/*
+LightVertexGenerator* BakeLight::vertexGenerator( void ) const
 {
 {
+    return vertexGenerator_;
+}
 
 
+// ** BakeLight::setVertexGenerator
+void BakeLight::setVertexGenerator( LightVertexGenerator* value )
+{
+    delete m_vertexGenerator;
+    m_vertexGenerator = value;
 }
 }
+*/
 
 
-void ZoneBakeLight::Light(LightRay* lightRay)
+LightAttenuation* BakeLight::GetAttenuationModel( void ) const
 {
 {
-    LightRay::SamplePoint& source = lightRay->samplePoint_;
+    return attenuationModel_;
+}
 
 
-    if (source.normal == Vector3::ZERO)
-        return;
+void BakeLight::SetAttenuationModel( LightAttenuation* value )
+{
+    attenuationModel_ = value;
+}
 
 
-    RTCScene scene = sceneBaker_->GetEmbreeScene()->GetRTCScene();
+PhotonEmitter* BakeLight::GetPhotonEmitter( void ) const
+{
+    return photonEmitter_;
+}
 
 
-    const Color& color = zone_->GetAmbientColor();
+void BakeLight::SetPhotonEmitter( PhotonEmitter* value )
+{
+    photonEmitter_ = value;
+}
 
 
-    Vector3 rad(color.r_, color.g_, color.b_);
+const Vector3& BakeLight::GetPosition( void ) const
+{
+    return position_;
+}
 
 
-    if (!GlobalGlowSettings.aoEnabled_)
-    {
-        source.bakeMesh->ContributeRadiance(lightRay, rad, GLOW_LIGHTMODE_AMBIENT);
-        return;
-    }
+void BakeLight::SetPosition( const Vector3& value )
+{
+    position_ = value;
+}
 
 
+const Color& BakeLight::GetColor( void ) const
+{
+    return color_;
+}
 
 
-    // TODO: AO using ray packets/streams
+void BakeLight::SetColor( const Color& value )
+{
+    color_ = value;
+}
 
 
-    RTCRay& ray = lightRay->rtcRay_;
+float BakeLight::GetIntensity( void ) const
+{
+    return intensity_;
+}
 
 
-    unsigned nsamples = GlobalGlowSettings.nsamples_;
+void BakeLight::SetIntensity( float value )
+{
+    intensity_ = value;
+}
 
 
-    // this needs to be based on model/scale likely?
-    float aoDepth = GlobalGlowSettings.aoDepth_;
+bool BakeLight::GetCastsShadow( void ) const
+{
+    return castsShadow_;
+}
 
 
-    // smallest percent of ao value to use
-    float aoMin = GlobalGlowSettings.aoMin_;
+void BakeLight::SetCastsShadow( bool value )
+{
+    castsShadow_ = value;
+}
 
 
-    // brightness control
-    float multiply = GlobalGlowSettings.aoMultiply_;
+BakeLight* BakeLight::CreateZoneLight( SceneBaker* baker, const Color& color)
+{
+    BakeLight* light = new BakeLight(baker->GetContext(), baker);
+    light->SetCastsShadow( false );
+    light->SetColor( color );
+    light->SetIntensity(1.0f);
 
 
-    // Shoot rays through the differential hemisphere.
-    int nhits = 0;
-    float avgDepth = 0.0f;
-    for (unsigned nsamp = 0; nsamp < nsamples; nsamp++)
+    if (GlobalGlowSettings.aoEnabled_)
     {
     {
-        Vector3 rayDir;
-        GetRandomDirection(rayDir);
-
-        float dotp = source.normal.x_ * rayDir.x_ +
-                     source.normal.y_ * rayDir.y_ +
-                     source.normal.z_ * rayDir.z_;
-
-        if (dotp < 0.1f)
-        {
-            continue;
-        }
-
-        float variance = 0.0f;//nsamples <= 32 ? 0.0f : aoDepth * ((float) rand() / (float) RAND_MAX) * 0.25f;
+        light->SetInfluenceModel( new AmbientOcclusionInfluence( light ) );
+    }
 
 
-        float depth = aoDepth + variance;
+    return light;
+}
 
 
-        lightRay->SetupRay(source.position, rayDir, .001f, depth);
+BakeLight* BakeLight::CreatePointLight( SceneBaker* baker, const Vector3& position, float radius, const Color& color, float intensity, bool castsShadow )
+{
+    BakeLight* light = new BakeLight(baker->GetContext(), baker);
 
 
-        rtcOccluded(scene, ray);
+    light->SetInfluenceModel( new LightInfluence( light ) );
+    light->SetAttenuationModel( new LinearLightAttenuation( light, radius ) );
+    light->SetCutoffModel( new LightCutoff( light ) );
+    light->SetPhotonEmitter( new PhotonEmitter( light ) );
 
 
-        if (ray.geomID != RTC_INVALID_GEOMETRY_ID)
-        {
-            avgDepth += Min<float>(ray.tfar, aoDepth);
-            nhits++;
-        }
-    }
+    light->SetCastsShadow( castsShadow );
+    light->SetPosition( position );
+    light->SetColor( color );
+    light->SetIntensity( intensity );
 
 
-    if (nhits)// && (nsamples <= 32 ? true : nhits > 4))
-    {
-        avgDepth /= float(nhits);
-        avgDepth /= aoDepth;
+    return light;
+}
 
 
-        avgDepth = Clamp<float>(avgDepth, 0.1f, 1.0f) * 100.0f;
-        avgDepth *= avgDepth;
-        float ao = avgDepth / 10000.0f;
+BakeLight* BakeLight::CreateSpotLight( SceneBaker* baker, const Vector3& position, const Vector3& direction, float cutoff, float radius, const Color& color, float intensity, bool castsShadow )
+{
+    BakeLight* light = new BakeLight(baker->GetContext(), baker);
 
 
-        ao = aoMin + ao/2.0f;
-        ao *= multiply;
-        ao = Clamp<float>(ao, aoMin, 1.0f);
+    light->SetInfluenceModel( new LightInfluence( light ) );
+    light->SetAttenuationModel( new LinearLightAttenuation( light, radius ) );
+    light->SetCutoffModel( new LightSpotCutoff( light, direction, cutoff, 1.0f ) );
+    light->SetPhotonEmitter( new PhotonEmitter( light ) );
 
 
-        rad *= ao;
-    }
+    light->SetCastsShadow( castsShadow );
+    light->SetPosition( position );
+    light->SetColor( color );
+    light->SetIntensity( intensity );
 
 
-    source.bakeMesh->ContributeRadiance(lightRay, rad, GLOW_LIGHTMODE_AMBIENT);
+    return light;
 }
 }
 
 
-void ZoneBakeLight::SetZone(Zone* zone)
+BakeLight* BakeLight::CreateDirectionalLight( SceneBaker* baker, const Vector3& direction, const Color& color, float intensity, bool castsShadow )
 {
 {
-    node_ = zone->GetNode();
-    zone_ = zone;
-}
+    BakeLight* light = new BakeLight(baker->GetContext(), baker);
 
 
+    light->SetInfluenceModel( new DirectionalLightInfluence( light, direction ) );
+    light->SetPhotonEmitter( new DirectionalPhotonEmitter( light, direction ) );
+    light->SetCastsShadow( castsShadow );
+    light->SetColor( color );
+    light->SetIntensity( intensity );
 
 
-// Directional Lights
+    return light;
+}
 
 
-DirectionalBakeLight::DirectionalBakeLight(Context* context, SceneBaker* sceneBaker) : BakeLight(context, sceneBaker)
+/*
+BakeLight* BakeLight::CreateAreaLight( SceneBaker* baker, const Mesh* mesh, const Vector3& position, const Rgb& color, float intensity, bool castsShadow )
 {
 {
+    Light* light = new Light;
+
+    light->SetInfluence( new LightInfluence( light ) );
+    light->SetAttenuation( new LinearLightAttenuation( light, mesh->bounds().volume() ) );
+    light->SetPhotonEmitter( new PhotonEmitter( light ) );
+    light->SetCutoff( new LightCutoff( light ) );
+    //light->SetVertexGenerator( new FaceLightVertexGenerator( mesh, true, 3 ) );
+    //light->SetVertexGenerator( new FaceLightVertexGenerator( mesh, false, 0 ) );
+    light->SetVertexGenerator( new FaceLightVertexGenerator( mesh, true, 0 ) );
+    //  light->SetVertexGenerator( new LightVertexGenerator( mesh ) );
+    light->SetCastsShadow( castsShadow );
+    light->SetPosition( position );
+    light->SetColor( color );
+    light->SetIntensity( intensity );
+
+    light->GetVertexGenerator()->Generate();
+
+    return light;
 }
 }
+*/
+
+// ------------------------------------------------------- LightInfluence --------------------------------------------------------- //
 
 
-DirectionalBakeLight::~DirectionalBakeLight()
+LightInfluence::LightInfluence( const BakeLight* light ) :
+    light_( light )
 {
 {
 
 
 }
 }
 
 
-void DirectionalBakeLight::Light(LightRay* lightRay)
+float LightInfluence::Calculate(LightRay* lightRay, const Vector3& light, float& distance ) const
 {
 {
-    RTCScene scene = sceneBaker_->GetEmbreeScene()->GetRTCScene();
-
     LightRay::SamplePoint& source = lightRay->samplePoint_;
     LightRay::SamplePoint& source = lightRay->samplePoint_;
-    RTCRay& ray = lightRay->rtcRay_;
 
 
-    float angle = direction_.DotProduct(source.normal);
+    Vector3 direction = light - source.position;
+    distance = direction.Length();
+    direction.Normalize();
 
 
-    if (angle < 0.0f)
-        return;
+    // ** Calculate Lambert's cosine law intensity
+    float intensity = GetLambert( direction, source.normal );
 
 
-    lightRay->SetupRay(source.position, direction_);
+    if( intensity <= 0.001f ) {
+        return 0.0f;
+    }
 
 
-    rtcOccluded(scene, ray);
+    // ** Cast shadow to point
+    if( light_->GetCastsShadow() )
+    {
+        LightRay::SamplePoint& source = lightRay->samplePoint_;
 
 
-    // obstructed?  TODO: glass, etc
-    if (ray.geomID != RTC_INVALID_GEOMETRY_ID)
-        return;
+        // clean this mess up
+        RTCScene scene = source.bakeMesh->GetSceneBaker()->GetEmbreeScene()->GetRTCScene();
 
 
-    Vector3 rad(color_.r_, color_.g_, color_.b_);
+        lightRay->SetupRay(source.position, direction, .001f, distance);
 
 
-    rad*=angle;
+        rtcOccluded(scene, lightRay->rtcRay_);
 
 
-    source.bakeMesh->ContributeRadiance(lightRay, rad);
+        // obstructed?  TODO: glass, etc
+        if (lightRay->rtcRay_.geomID != RTC_INVALID_GEOMETRY_ID)
+        {
+            return 0.0f;
+        }
+    }
 
 
+    return intensity;
 }
 }
 
 
-void DirectionalBakeLight::SetLight(Atomic::Light* light)
+float LightInfluence::GetLambert( const Vector3& direction, const Vector3& normal )
 {
 {
-    node_ = light->GetNode();
-
-    color_ = light->GetColor();
-
-    direction_ = -node_->GetWorldDirection();
-    direction_.Normalize();
-
+    float dp = direction.DotProduct(normal);
+    return dp < 0.0f ? 0.0f : dp;
 }
 }
 
 
-// Point Lights
+// --------------------------------------------------- DirectionalLightInfluence -------------------------------------------------- //
 
 
-PointBakeLight::PointBakeLight(Context* context, SceneBaker* sceneBaker) : BakeLight(context, sceneBaker)
-{
-}
-
-PointBakeLight::~PointBakeLight()
+DirectionalLightInfluence::DirectionalLightInfluence( const BakeLight* light, const Vector3& direction )
+    : LightInfluence( light ), direction_( direction )
 {
 {
 
 
 }
 }
 
 
-void PointBakeLight::Light(LightRay* lightRay)
+float DirectionalLightInfluence::Calculate(LightRay* lightRay, const Vector3& light, float& distance ) const
 {
 {
-    RTCScene scene = sceneBaker_->GetEmbreeScene()->GetRTCScene();
-
     LightRay::SamplePoint& source = lightRay->samplePoint_;
     LightRay::SamplePoint& source = lightRay->samplePoint_;
-    RTCRay& ray = lightRay->rtcRay_;
-
-    Vector3 dir = position_ - source.position;
 
 
-    float dist = dir.Length();
+    float intensity = GetLambert( -direction_, source.normal );
 
 
-    if (range_ <= 0.0f || dist >= range_)
-        return;
-
-    dir.Normalize();
-
-    float dot = dir.DotProduct(source.normal);
-
-    if (dot < 0.0f)
-        return;
-
-    lightRay->SetupRay(source.position, dir, .001f, dist);
-
-    rtcOccluded(scene, ray);
-
-    // obstructed?  TODO: glass, etc
-    if (ray.geomID != RTC_INVALID_GEOMETRY_ID)
-        return;
-
-    Vector3 rad(color_.r_, color_.g_, color_.b_);
-
-    // lightOverBright 1.2 for example will pop light,
-    // needs to be configurable per light, maybe with brightness modifer
-    float lightOverBright = 1.0f;
-
-    rad *= Max<float> (1.0f - ( dist * lightOverBright / range_), 0.0f);
-    rad *= dot;
-
-    // EXPERIMENTAL: if GI is enabled, dim point light a bit
+    if( intensity <= 0.001f )
+    {
+        return 0.0f;
+    }
 
 
-    if (GlobalGlowSettings.giEnabled_)
+    // ** Cast shadow to point
+    if( light_->GetCastsShadow() )
     {
     {
-        rad *= 0.75f;
+        // FIXME: tracer
+        // intensity *= tracer->traceSegment( point, point - m_direction * 1000, rt::HitUseAlpha ) ? 0.0f : 1.0f;
     }
     }
 
 
+    return intensity;
+}
 
 
+// --------------------------------------------------- AmbientOcclusionInfluence -------------------------------------------------- //
 
 
-    if (rad.Length() > M_EPSILON)
-        source.bakeMesh->ContributeRadiance(lightRay, rad);
+AmbientOcclusionInfluence::AmbientOcclusionInfluence( const BakeLight* light )
+    : LightInfluence( light )
+{
 
 
 }
 }
 
 
-void PointBakeLight::SetLight(Atomic::Light* light)
+float AmbientOcclusionInfluence::Calculate(LightRay* lightRay, const Vector3& light, float& distance ) const
 {
 {
-    node_ = light->GetNode();
 
 
-    color_ = light->GetColor();
-    position_ = node_->GetWorldPosition();
+    LightRay::SamplePoint& source = lightRay->samplePoint_;
 
 
-    range_ = light->GetRange();
+    if (source.normal == Vector3::ZERO)
+        return 1.0f;
 
 
-}
+    // clean this mess up
+    RTCScene scene = source.bakeMesh->GetSceneBaker()->GetEmbreeScene()->GetRTCScene();
 
 
-// Bounce Lights
+    const Color& color = light_->GetColor();
 
 
-Mutex BounceBakeLight::sortMutex_;
+    Vector3 rad(color.r_, color.g_, color.b_);
 
 
-BounceBakeLight::BounceBakeLight(Context* context, SceneBaker* sceneBaker) : BakeLight(context, sceneBaker)
-{
-}
+    if (!GlobalGlowSettings.aoEnabled_)
+    {
+        return 1.0f;
+    }
 
 
-BounceBakeLight::~BounceBakeLight()
-{
+    // TODO: AO using ray packets/streams
 
 
-}
+    RTCRay& ray = lightRay->rtcRay_;
 
 
-static Vector3 compareBouncePoint;
-static inline bool CompareBounceSamples(const BounceSample* lhs, const BounceSample* rhs)
-{
-    Vector3 v1 =  lhs->position_ - compareBouncePoint;
-    Vector3 v2 =  rhs->position_ - compareBouncePoint;
-    return v1.LengthSquared() < v2.LengthSquared();
-}
+    unsigned nsamples = GlobalGlowSettings.nsamples_;
 
 
-void BounceBakeLight::Light(LightRay* lightRay)
-{
-    RTCScene scene = sceneBaker_->GetEmbreeScene()->GetRTCScene();
-    LightRay::SamplePoint& source = lightRay->samplePoint_;
-    RTCRay& ray = lightRay->rtcRay_;
+    // this needs to be based on model/scale likely?
+    float aoDepth = GlobalGlowSettings.aoDepth_;
 
 
-    const float maxDist = 5.0f;
-    const float maxDistSq = maxDist * maxDist;
+    // smallest percent of ao value to use
+    float aoMin = GlobalGlowSettings.aoMin_;
 
 
-    const BounceSample* b;
-    PODVector<const BounceSample*> samples;
+    // brightness control
+    float multiply = GlobalGlowSettings.aoMultiply_;
 
 
-    for (int i = 0; i < bounceSamples_.Size(); i++)
+    // Shoot rays through the differential hemisphere.
+    int nhits = 0;
+    float avgDepth = 0.0f;
+    for (unsigned nsamp = 0; nsamp < nsamples; nsamp++)
     {
     {
-        b = &bounceSamples_[i];
+        Vector3 rayDir;
+        Vector3::GetRandomHemisphereDirection(rayDir, source.normal);
 
 
-        // don't light self
-        if (source.bakeMesh == bakeMesh_)
+        float dotp = source.normal.x_ * rayDir.x_ +
+                source.normal.y_ * rayDir.y_ +
+                source.normal.z_ * rayDir.z_;
+
+        if (dotp < 0.1f)
         {
         {
-            for (int j = 0; j < GLOW_MAX_BOUNCE_SAMPLE_TRIANGLES; j++)
-            {
-                if (b->triIndex_[j] == -1)
-                    break;
+            continue;
+        }
+
+        float variance = 0.0f;//nsamples <= 32 ? 0.0f : aoDepth * ((float) rand() / (float) RAND_MAX) * 0.25f;
 
 
-                if (b->triIndex_[j] == source.triangle)
-                    return;
+        float depth = aoDepth + variance;
 
 
-            }
-        }
+        lightRay->SetupRay(source.position, rayDir, .001f, depth);
 
 
-        Vector3 dir =  b->position_ - source.position;
+        rtcOccluded(scene, ray);
 
 
-        if (dir.LengthSquared() > maxDistSq)
+        if (ray.geomID != RTC_INVALID_GEOMETRY_ID)
         {
         {
-            continue;
+            avgDepth += Min<float>(ray.tfar, aoDepth);
+            nhits++;
         }
         }
+    }
 
 
-        dir.Normalize();
+    if (nhits)// && (nsamples <= 32 ? true : nhits > 4))
+    {
+        avgDepth /= float(nhits);
+        avgDepth /= aoDepth;
 
 
-        if (dir.DotProduct(source.normal) < M_EPSILON)
-        {
-            continue;
-        }
+        avgDepth = Clamp<float>(avgDepth, 0.1f, 1.0f) * 100.0f;
+        avgDepth *= avgDepth;
+        float ao = avgDepth / 10000.0f;
 
 
-        samples.Push(b);
+        ao = aoMin + ao/2.0f;
+        ao *= multiply;
+        ao = Clamp<float>(ao, aoMin, 1.0f);
 
 
+        return ao;
     }
     }
 
 
-    if (!samples.Size())
-        return;
+    return 1.0f;
+}
 
 
-    sortMutex_.Acquire();
+// --------------------------------------------------------- LightCutoff ---------------------------------------------------------- //
 
 
-    compareBouncePoint = source.position;
+LightCutoff::LightCutoff( const BakeLight* light ) : light_( light )
+{
 
 
-    Sort(samples.Begin(), samples.End(), CompareBounceSamples);
+}
 
 
-    sortMutex_.Release();
+float LightCutoff::Calculate( const Vector3& point ) const
+{
+    return 1.0f;
+}
 
 
+float LightCutoff::GetCutoffForDirection( const Vector3& direction ) const
+{
+    return 1.0f;
+}
 
 
-    int bestIndex = -1;
-    float bestDist = M_INFINITY;
-    for (unsigned i = 0; i < samples.Size(); i++)
-    {
-        b = samples[i];
+// ------------------------------------------------------- LightSpotCutoff -------------------------------------------------------- //
 
 
-        Vector3 dir = b->position_ - source.position;
-        float dist = dir.Length();
+LightSpotCutoff::LightSpotCutoff( const BakeLight* light, const Vector3& direction, float cutoff, float exponent ) :
+    LightCutoff( light ), direction_( direction ), cutoff_( cutoff ), exponent_( exponent )
+{
 
 
-        if (dist < bestDist)
-        {
-            dir.Normalize();
+}
 
 
-            lightRay->SetupRay(source.position, dir, 0.01f, dist * 1.01f);
+float LightSpotCutoff::Calculate( const Vector3& point ) const
+{
+    Vector3 dir = point - light_->GetPosition();
+    dir.Normalize();
+    return GetCutoffForDirection( dir );
+}
 
 
-            rtcIntersect(scene, ray);
+float LightSpotCutoff::GetCutoffForDirection( const Vector3& direction ) const
+{
+    float value = direction.DotProduct(direction_);
 
 
-            if (ray.geomID != bakeMesh_->GetGeomID())
-            {
-                continue;
-            }
+    if( value <= cutoff_ ) {
+        return 0.0f;
+    }
 
 
-            // backface
-            if ( (ray.Ng[0] * ray.dir[0]) +
-                 (ray.Ng[1] * ray.dir[1]) +
-                 (ray.Ng[2] * ray.dir[2]) < 0.0f)
-            {
-                continue;
-            }
+    value = (1.0f - (1.0f - value) * 1.0f / (1.0f - cutoff_));
 
 
-            bool found = false;
-            for (int j = 0; j < GLOW_MAX_BOUNCE_SAMPLE_TRIANGLES; j++)
-            {
-                if (b->triIndex_[j] == -1)
-                    break;
+    if( fabs( 1.0f - exponent_ ) > 0.01f ) {
+        value = powf( value, exponent_ );
+    }
 
 
-                if (b->triIndex_[j] == ray.primID)
-                {
-                    found = true;
-                    break;
-                }
-            }
+    return value;
+}
 
 
-            if (!found)
-                continue;
+// ------------------------------------------------------- LightAttenuation ------------------------------------------------------- //
 
 
-            bestIndex = i;
-            bestDist = dist;
+LightAttenuation::LightAttenuation( const BakeLight* light ) : light_( light )
+{
 
 
-            break;
-        }
+}
 
 
-    }
+// ---------------------------------------------------- LinearLightAttenuation ---------------------------------------------------- //
 
 
-    if (bestIndex == -1)
-        return;
 
 
-    b = samples[bestIndex];
+LinearLightAttenuation::LinearLightAttenuation( const BakeLight* light, float radius, float constant, float linear, float quadratic )
+    : LightAttenuation( light ), radius_( radius ), constant_( constant ), linear_( linear ), quadratic_( quadratic )
+{
 
 
-    // weighted average of src color and radiance for bounce sample
-    Vector3 rad = b->srcColor_ + (b->radiance_/b->hits_);
-    rad /= 2.0f;
+}
 
 
-    float d = 1.0f - Clamp<float>(bestDist / maxDist, 0.01f, 1.0f);
 
 
-    rad *= d;
+float LinearLightAttenuation::Calculate( float distance ) const
+{
+    if (fabs(radius_) < 0.01f)
+        return 0.0f;
 
 
-    rad *= 0.15f;
+    float r = distance / radius_;
+    return Max<float>( 1.0f / (1.0f + constant_ + linear_ * r + quadratic_ * r * r), 0.0f );
+}
 
 
-    if (rad.x_ < 0.0f || rad.y_ < 0.0f || rad.z_ < 0.0f)
-    {
-        ATOMIC_LOGWARNING("BounceBakeLight::Light - negative rad factor");
-        return;
-    }
+// -------------------------------------------------------- PhotonEmitter --------------------------------------------------------- //
 
 
-    source.bakeMesh->ContributeRadiance(lightRay, rad, GLOW_LIGHTMODE_INDIRECT);
+PhotonEmitter::PhotonEmitter( const BakeLight* light ) :
+    light_( light )
+{
 
 
 }
 }
 
 
-void BounceBakeLight::SetBounceSamples(const PODVector<BounceSample>& bounceSamples)
+int PhotonEmitter::GetPhotonCount( void ) const
 {
 {
-    bounceSamples_ = bounceSamples;
+    return static_cast<int>( light_->GetIntensity() * 25000 );
 }
 }
 
 
-void BounceBakeLight::SetBakeMesh(BakeMesh* bakeMesh)
+void PhotonEmitter::Emit( SceneBaker* sceneBaker, Vector3& position, Vector3& direction ) const
 {
 {
-    bakeMesh_ = bakeMesh;
-    node_ = bakeMesh->GetNode();
+    position  = light_->GetPosition();
+    Vector3::GetRandomDirection(direction);
 
 
-    color_ = Color::MAGENTA;
-    range_ = -1.0f;
-    position_ = Vector3::ZERO;
 }
 }
 
 
-void BounceBakeLight::SetLight(Atomic::Light* light)
-{
-    node_ = light->GetNode();
+// --------------------------------------------------- DirectinalPhotonEmitter ---------------------------------------------------- //
 
 
-    color_ = light->GetColor();
-    position_ = node_->GetWorldPosition();
+DirectionalPhotonEmitter::DirectionalPhotonEmitter( const BakeLight* light, const Vector3& direction ) :
+    PhotonEmitter( light ), direction_( direction )
+{
+    plane_.Define(direction.Normalized(), light->GetPosition()); //  = Plane::calculate( direction, light->position() );
+}
 
 
-    range_ = light->GetRange();
+/*
+void DirectionalPhotonEmitter::Emit( const Scene* scene, Vector3& position, Vector3& direction ) const
+{
+    const Bounds& bounds = scene->bounds();
 
 
+    position  = m_plane * bounds.randomPointInside() - m_direction * 5;
+    direction = m_direction;
 }
 }
-
+*/
 
 
 
 
 }
 }

+ 328 - 48
Source/AtomicGlow/Kernel/BakeLight.h

@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2017, THUNDERBEAST GAMES LLC All rights reserved
 // Copyright (c) 2014-2017, THUNDERBEAST GAMES LLC All rights reserved
-// Copyright 2009-2017 Intel Corporation
+// Portions Copyright (c) 2015 Dmitry Sovetov
+// Portions Copyright 2009-2017 Intel Corporation
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -23,12 +24,13 @@
 #pragma once
 #pragma once
 
 
 #include <Atomic/Core/Mutex.h>
 #include <Atomic/Core/Mutex.h>
+#include <Atomic/Math/Plane.h>
 #include "BakeNode.h"
 #include "BakeNode.h"
 
 
 namespace Atomic
 namespace Atomic
 {
 {
-    class Light;
-    class Zone;
+class Light;
+class Zone;
 }
 }
 
 
 using namespace Atomic;
 using namespace Atomic;
@@ -38,6 +40,221 @@ namespace AtomicGlow
 
 
 class LightRay;
 class LightRay;
 class BakeMesh;
 class BakeMesh;
+class BakeLight;
+
+/// Light cuttoff is used for configuring the light source influence direction (omni, directional, spotlight).
+/// LightCutoff class is a base for all cutoff models and represents an omni-directional light.
+class LightCutoff : public RefCounted
+{
+    ATOMIC_REFCOUNTED(LightCutoff)
+
+public:
+
+    /// Constructs a LightCutoff instance.
+    LightCutoff( const BakeLight* light );
+    virtual             ~LightCutoff( void ) {}
+
+    /// Calculates a light influence to a given point.
+    virtual float       Calculate( const Vector3& point ) const;
+
+    /// Calculates a light cutoff for direction.
+    virtual float       GetCutoffForDirection( const Vector3& direction ) const;
+
+protected:
+
+    /// Parent light instance.
+    const BakeLight*        light_;
+};
+
+/// LightSpotCutoff is used for spot lights.
+class LightSpotCutoff : public LightCutoff
+{
+
+    ATOMIC_REFCOUNTED(LightSpotCutoff)
+
+public:
+
+    // Constructs a LightSpotCutoff instance
+    // param light Parent light source.
+    // param direction Spot light direction.
+    // param cutoff The cutoff value represents the maximum angle between the light direction and the light to pixel vector.
+    // param exponent The cutoff exponent.
+    LightSpotCutoff( const BakeLight* light, const Vector3& direction, float cutoff, float exponent );
+    virtual ~LightSpotCutoff( void ) {}
+
+    /// Calculates a light influence for a spot light.
+    virtual float Calculate( const Vector3& point ) const;
+
+    /// Calculates a light cutoff for direction.
+    virtual float GetCutoffForDirection( const Vector3& direction ) const;
+
+private:
+
+    /// Light direction.
+    Vector3 direction_;
+    /// Light cutoff.
+    float cutoff_;
+    /// Light spot exponent.
+    float exponent_;
+};
+
+/// Light distance attenuation model.
+class LightAttenuation : public RefCounted
+{
+    ATOMIC_REFCOUNTED(LightAttenuation)
+
+public:
+
+    /// Constructs a LightAttenuation instance.
+    LightAttenuation( const BakeLight* light );
+    virtual ~LightAttenuation( void ) {}
+
+    /// Calculates light attenuation by distance.
+    /// An attenuation factor is a value in range [0;1], where 0 means
+    /// that light is attenuated to zero, and 1 means no attenuation at all.
+    virtual float Calculate( float distance ) const { return 0.0f; }
+
+protected:
+
+    /// Parent light source.
+    const BakeLight* light_;
+};
+
+/// Linear light attenuation model.
+class LinearLightAttenuation : public LightAttenuation
+{
+
+    ATOMIC_REFCOUNTED(LinearLightAttenuation)
+
+public:
+    /// Constructs a LinearLightAttenuation instance.
+    LinearLightAttenuation( const BakeLight* light, float radius, float constant = 0.0f, float linear = 0.0f, float quadratic = 25.0f );
+
+    virtual float Calculate( float distance ) const;
+
+private:
+
+    // Light influence radius.
+    float radius_;
+    // Constant light attenuation factor.
+    float constant_;
+    // Linear light attenuation factor.
+    float linear_;
+    // Quadratic light attenuation factor.
+    float quadratic_;
+};
+
+/// Light to point influence model.
+class LightInfluence : public RefCounted
+{
+
+    ATOMIC_REFCOUNTED(LightInfluence)
+
+public:
+
+    /// Constructs a LightInfluence instance.
+    LightInfluence( const BakeLight* light );
+
+    virtual ~LightInfluence( void ) {}
+
+    /// Calculates omni light influence to a given point.
+    virtual float Calculate(LightRay* lightRay, const Vector3 &light, float& distance ) const;
+
+    /// Calculates a light influence by a Lambert's cosine law.
+    static float GetLambert( const Vector3& direction, const Vector3& normal );
+
+protected:
+
+    /// Parent light instance.
+    const BakeLight* light_;
+};
+
+///  Directional light influence model.
+class DirectionalLightInfluence : public LightInfluence
+{
+    ATOMIC_REFCOUNTED(DirectionalLightInfluence)
+
+public:
+
+    /// Constructs a DirectionalLightInfluence instance.
+    DirectionalLightInfluence( const BakeLight* light, const Vector3& direction );
+
+    /// Calculates a directional light influence.
+    virtual float Calculate( LightRay* lightRay, const Vector3& light, float& distance ) const;
+
+private:
+
+    /// Light source direction.
+    Vector3 direction_;
+};
+
+///  Ambient occlusion influence model.
+class AmbientOcclusionInfluence : public LightInfluence
+{
+    ATOMIC_REFCOUNTED(AmbientOcclusionInfluence)
+
+public:
+
+    /// Constructs a AmbientOcclusionInfluence instance.
+    AmbientOcclusionInfluence( const BakeLight* light);
+
+    /// Calculates a ambient occlusion light influence.
+    virtual float Calculate( LightRay* lightRay, const Vector3& light, float& distance ) const;
+
+private:
+
+};
+
+
+
+/// Photon emitter is used to determine an amount of photons to be emitted by a given light.
+class PhotonEmitter : public RefCounted
+{
+    ATOMIC_REFCOUNTED(PhotonEmitter)
+
+public:
+
+    // Constructs a new PhotonEmitter instance.
+    PhotonEmitter( const BakeLight* light );
+    virtual ~PhotonEmitter( void ) {}
+
+    // Calculates an amount of photons to be emitted.
+    virtual int GetPhotonCount( void ) const;
+
+    // Emits a new photon.
+    virtual void Emit( SceneBaker* scene, Vector3& position, Vector3& direction ) const;
+
+protected:
+
+    // Parent light source.
+    const BakeLight* light_;
+};
+
+/// Directional photon emitter emits photons with a given direction.
+class DirectionalPhotonEmitter : public PhotonEmitter
+{
+
+    ATOMIC_REFCOUNTED(DirectionalPhotonEmitter)
+
+public:
+
+    // Constructs a new DirectionalPhotonEmitter instance.
+    // param light Parent light source.
+    // param direction Emission direction.
+    DirectionalPhotonEmitter( const BakeLight* light, const Vector3& direction );
+
+    /// Emits a new photon.
+    // virtual void Emit( const Scene* scene, Vector3& position, Vector3& direction ) const;
+
+private:
+
+    /// Emission direction.
+    Vector3 direction_;
+
+    /// Emission plane.
+    Plane plane_;
+};
+
 
 
 class BakeLight : public BakeNode
 class BakeLight : public BakeNode
 {
 {
@@ -48,24 +265,119 @@ class BakeLight : public BakeNode
     BakeLight(Context* context, SceneBaker *sceneBaker);
     BakeLight(Context* context, SceneBaker *sceneBaker);
     virtual ~BakeLight();
     virtual ~BakeLight();
 
 
+    /*
     virtual void SetLight(Atomic::Light* light) = 0;
     virtual void SetLight(Atomic::Light* light) = 0;
-
     virtual void Light(LightRay* ray) = 0;
     virtual void Light(LightRay* ray) = 0;
+    */
 
 
-protected:
+    // Returns an light influence model.
+    LightInfluence* GetInfluenceModel( void ) const;
 
 
-    // Common light properties
-    Color color_;
-    Vector3 position_;
-    Vector3 direction_;
+    // Sets an light influence model.
+    void SetInfluenceModel( LightInfluence* value );
+
+    // Returns an attenuation model.
+    LightAttenuation* GetAttenuationModel( void ) const;
+
+    // Sets an attenuation model.
+    void SetAttenuationModel( LightAttenuation* value );
+
+    // Returns a light vertex generator.
+    // LightVertexGenerator* vertexGenerator( void ) const;
+
+    // Sets an light vertex generator.
+    // void SetVertexGenerator( LightVertexGenerator* value );
+
+    // Returns an cutoff model.
+    LightCutoff* GetCutoffModel( void ) const;
+
+    // Sets an cutoff model.
+    void SetCutoffModel( LightCutoff* value );
+
+    // Returns a photon emitter.
+    PhotonEmitter* GetPhotonEmitter( void ) const;
+
+    // Sets a photon emitter.
+    // By default each light has a photon emitter, light sources with no photon emitter
+    // set won't make any influence to a global illumination.
+    void SetPhotonEmitter( PhotonEmitter* value );
+
+    // Returns a light position.
+    const Vector3& GetPosition( void ) const;
+
+    // Sets a light position.
+    void SetPosition( const Vector3& value );
+
+    // Returns a light color.
+    const Color& GetColor( void ) const;
+
+    // Sets a light color.
+    void SetColor( const Color& value );
+
+    // Returns a light intensity.
+    float GetIntensity( void ) const;
+
+    // Sets a light intensity.
+    void SetIntensity( float value );
+
+    // Returns true if this light casts shadow otherwise false.
+    bool GetCastsShadow( void ) const;
+
+    // Sets a castShadow flag.
+    void SetCastsShadow( bool value );
 
 
-    float range_;
+    // Direct Lighting
 
 
+    void DirectLight(LightRay* lightRay);
+
+    // Creates a Zone light instance.
+    static BakeLight* CreateZoneLight( SceneBaker* baker, const Color& color = Color::WHITE );
+
+    // Creates a point light instance.
+    static BakeLight* CreatePointLight( SceneBaker* baker, const Vector3& position, float radius, const Color& color = Color::WHITE, float intensity = 1.0f, bool castsShadow = true );
+
+    // Creates a spot light instance.
+    static BakeLight* CreateSpotLight( SceneBaker* baker, const Vector3& position, const Vector3& direction, float cutoff, float radius, const Color& color = Color::WHITE, float intensity = 1.0f, bool castsShadow = true );
+
+    // Creates a directional light instance.
+    static BakeLight* CreateDirectionalLight( SceneBaker* baker, const Vector3& direction, const Color& color = Color::WHITE, float intensity = 1.0f, bool castsShadow = true );
+
+    // Creates an area light instance.
+    // static BakeLight* CreateAreaLight( SceneBaker* baker, const Mesh* mesh, const Vector3& position, const Color& color = Color::WHITE, float intensity = 1.0f, bool castsShadow = true );
 
 
 private:
 private:
 
 
+    // Light position.
+    Vector3 position_;
+
+    // Light color.
+    Color color_;
+
+    // Light intensity.
+    float intensity_;
+
+    // Casts shadow flag.
+    bool castsShadow_;
+
+    // Light cutoff model.
+    SharedPtr<LightCutoff> cutoffModel_;
+
+    // Light attenuation model.
+    SharedPtr<LightAttenuation>  attenuationModel_;
+
+    // Light influence model.
+    SharedPtr<LightInfluence> influenceModel_;
+
+    // Light source photon emitter.
+    SharedPtr<PhotonEmitter> photonEmitter_;
+
+    // Light vertex sampler.
+    // SharedPtr<LightVertexGenerator> vertexGenerator_;
+
 };
 };
 
 
+/*
+
 // Zone ambient, etc
 // Zone ambient, etc
 class ZoneBakeLight : public BakeLight
 class ZoneBakeLight : public BakeLight
 {
 {
@@ -77,7 +389,7 @@ class ZoneBakeLight : public BakeLight
     virtual ~ZoneBakeLight();
     virtual ~ZoneBakeLight();
 
 
     void Light(LightRay* lightRay);
     void Light(LightRay* lightRay);
-    void SetLight(Atomic::Light* light) {}
+    void SetLight(Atomic::BakeLight* light) {}
 
 
     void SetZone(Zone* zone);
     void SetZone(Zone* zone);
 
 
@@ -96,14 +408,14 @@ class DirectionalBakeLight : public BakeLight
 
 
     public:
     public:
 
 
-    DirectionalBakeLight(Context* context, SceneBaker* sceneBaker);
+        DirectionalBakeLight(Context* context, SceneBaker* sceneBaker);
     virtual ~DirectionalBakeLight();
     virtual ~DirectionalBakeLight();
 
 
     void Set(const Vector3& direction, const Vector3& radiance, float cosAngle);
     void Set(const Vector3& direction, const Vector3& radiance, float cosAngle);
 
 
     void Light(LightRay* lightRay);
     void Light(LightRay* lightRay);
 
 
-    void SetLight(Atomic::Light* light);
+    void SetLight(Atomic::BakeLight* light);
 
 
 
 
 protected:
 protected:
@@ -119,53 +431,21 @@ class PointBakeLight : public BakeLight
 
 
     public:
     public:
 
 
-    PointBakeLight(Context* context, SceneBaker* sceneBaker);
+        PointBakeLight(Context* context, SceneBaker* sceneBaker);
     virtual ~PointBakeLight();
     virtual ~PointBakeLight();
 
 
     void Light(LightRay* lightRay);
     void Light(LightRay* lightRay);
 
 
-    void SetLight(Atomic::Light* light);
-
-protected:
-
-
-private:
-
-};
-
-// BakeMesh Bounce Lighting
-class BounceBakeLight : public BakeLight
-{
-    ATOMIC_OBJECT(BounceBakeLight, BakeLight)
-
-    public:
-
-    BounceBakeLight(Context* context, SceneBaker* sceneBaker);
-    virtual ~BounceBakeLight();
-
-    void Light(LightRay* lightRay);
-
-    void SetLight(Atomic::Light* light);
-
-    void SetBakeMesh(BakeMesh* bakeMesh);
-
-    void SetBounceSamples(const PODVector<BounceSample>& bounceSamples);
-
-    unsigned GetNumBounceSamples() const { return bounceSamples_.Size(); }
+    void SetLight(Atomic::BakeLight* light);
 
 
 protected:
 protected:
 
 
 
 
 private:
 private:
 
 
-    static Mutex sortMutex_;
-
-    PODVector<BounceSample> bounceSamples_;
-
-    WeakPtr<BakeMesh> bakeMesh_;
-
 };
 };
 
 
+*/
 
 
 
 
 }
 }

+ 21 - 159
Source/AtomicGlow/Kernel/BakeMesh.cpp

@@ -42,9 +42,6 @@ BakeMesh::BakeMesh(Context* context, SceneBaker *sceneBaker) : BakeNode(context,
     numTriangles_(0),
     numTriangles_(0),
     radianceHeight_(0),
     radianceHeight_(0),
     radianceWidth_(0),
     radianceWidth_(0),
-    bounceWidth_(0),
-    bounceHeight_(0),
-    bounceGranularity_(0),
     embreeGeomID_(RTC_INVALID_GEOMETRY_ID),
     embreeGeomID_(RTC_INVALID_GEOMETRY_ID),
     numWorkItems_(0),
     numWorkItems_(0),
     ambientColor_(Color::BLACK)
     ambientColor_(Color::BLACK)
@@ -83,12 +80,12 @@ bool BakeMesh::FillLexelsCallback(void* param, int x, int y, const Vector3& bary
 
 
 bool BakeMesh::LightPixel(ShaderData* shaderData, int x, int y, const Vector3& barycentric,const Vector3& dx, const Vector3& dy, float coverage)
 bool BakeMesh::LightPixel(ShaderData* shaderData, int x, int y, const Vector3& barycentric,const Vector3& dx, const Vector3& dy, float coverage)
 {
 {
+
     if (x >= radianceWidth_ || y >= radianceHeight_)
     if (x >= radianceWidth_ || y >= radianceHeight_)
         return true;
         return true;
 
 
     meshMutex_.Acquire();
     meshMutex_.Acquire();
     bool accept = radiancePassAccept_[y * radianceWidth_ + x];
     bool accept = radiancePassAccept_[y * radianceWidth_ + x];
-    const Vector3& rad = radiance_[y * radianceWidth_ + x];
 
 
     // check whether we've already lit this pixel
     // check whether we've already lit this pixel
     if (accept)
     if (accept)
@@ -203,74 +200,6 @@ void BakeMesh::ContributeRadiance(const LightRay* lightRay, const Vector3& radia
         radiance_[radY * radianceWidth_ + radX] += radiance;
         radiance_[radY * radianceWidth_ + radX] += radiance;
     }
     }
 
 
-    if (lightMode == GLOW_LIGHTMODE_AMBIENT || !GlobalGlowSettings.giEnabled_)
-        return;
-
-    // setup GI
-
-    // TODO: filter on < radiance + bounce settings
-    // OPTIMIZE: We can exit here if last bounce pass
-
-    // this should be replaced with a GetBounceColor which uses UV0 diffuse
-    // though takes into account additional settings, materials, etc
-    Color uv0Color;
-    // TODO: need to factor in alpha baked on BakeMaterial, not UV color from diffuse texture
-    if (!GetUV0Color(source.triangle, source.barycentric, uv0Color))// || uv0Color.a_ < 0.5f)
-    {
-        return;
-    }
-
-    if (uv0Color.ToVector3().Length() < 0.15f)
-    {
-        return;
-    }
-
-    int iX = (source.radianceX) & ~(bounceGranularity_-1);
-    int iY = (source.radianceY) & ~(bounceGranularity_-1);
-
-    iX /= bounceGranularity_;
-    iY /= bounceGranularity_;
-
-    assert(iY * bounceWidth_ + iX < bounceWidth_ * bounceHeight_);
-
-    if (bounceSamples_.Null())
-    {
-        bounceSamples_ = new BounceSample[bounceWidth_ * bounceHeight_];
-
-        for (unsigned j = 0; j < bounceWidth_ * bounceHeight_; j++)
-        {
-            bounceSamples_[j].Reset();
-        }
-    }
-
-    BounceSample& b = bounceSamples_[iY * bounceWidth_ + iX];
-
-    for (unsigned i = 0 ; i < GLOW_MAX_BOUNCE_SAMPLE_TRIANGLES; i++)
-    {
-        // check if already in use by another triangle
-        if (b.triIndex_[i] != -1 && b.triIndex_[i] != source.triangle)
-            continue;
-
-        if (b.triIndex_[i] == -1)
-        {
-            b.triIndex_[i] = source.triangle;
-            b.position_ = source.position;
-            b.radiance_ = radiance;
-            b.srcColor_ = uv0Color.ToVector3();
-            b.hits_ = 1;
-            //ATOMIC_LOGINFOF("+ Tri: %i, Pos: %s, Rad: %s, BTri: %i", b.triIndex_, b.position_.ToString().CString(), b.radiance_.ToString().CString(), i);
-            return;
-        }
-
-        //ATOMIC_LOGINFOF("- Tri: %i, Pos: %s, Rad: %s, BTri: %i", b.triIndex_, b.position_.ToString().CString(), b.radiance_.ToString().CString(), i);
-
-        b.position_ += source.position;
-        b.radiance_ += radiance;
-        b.srcColor_ += uv0Color.ToVector3();
-        b.hits_++;
-
-        break;
-    }
 }
 }
 
 
 void BakeMesh::GenerateRadianceMap()
 void BakeMesh::GenerateRadianceMap()
@@ -281,18 +210,6 @@ void BakeMesh::GenerateRadianceMap()
     radianceMap_ = new RadianceMap(context_, this);
     radianceMap_ = new RadianceMap(context_, this);
 }
 }
 
 
-void BakeMesh::ResetBounce()
-{
-    if (bounceSamples_.Null())
-        return;
-
-    for (unsigned i = 0; i < bounceWidth_ * bounceHeight_; i++)
-    {
-        bounceSamples_[i].Reset();
-    }
-
-}
-
 
 
 void BakeMesh::Light(GlowLightMode mode)
 void BakeMesh::Light(GlowLightMode mode)
 {
 {
@@ -419,6 +336,8 @@ void BakeMesh::Preprocess()
         }
         }
 
 
         rtcUnmapBuffer(scene, embreeGeomID_, RTC_INDEX_BUFFER);
         rtcUnmapBuffer(scene, embreeGeomID_, RTC_INDEX_BUFFER);
+
+        sceneBaker_->GetEmbreeScene()->RegisterBakeMesh(embreeGeomID_, this);
     }
     }
 
 
     float lmScale = staticModel_->GetLightmapScale();
     float lmScale = staticModel_->GetLightmapScale();
@@ -477,12 +396,7 @@ void BakeMesh::Preprocess()
     // If GI is enabled, setup bounce metrics
     // If GI is enabled, setup bounce metrics
     if (GlobalGlowSettings.giEnabled_)
     if (GlobalGlowSettings.giEnabled_)
     {
     {
-
-        bounceGranularity_ = GlobalGlowSettings.giGranularity_;
-        bounceWidth_ = (radianceWidth_ + bounceGranularity_) & ~(bounceGranularity_-1);
-        bounceHeight_ = (radianceHeight_ + bounceGranularity_) & ~(bounceGranularity_-1);
-        bounceWidth_ /= bounceGranularity_;
-        bounceHeight_ /= bounceGranularity_;
+        photonMap_ = new PhotonMap(radianceWidth_, radianceHeight_);
     }
     }
 }
 }
 
 
@@ -603,6 +517,21 @@ bool BakeMesh::SetStaticModel(StaticModel* staticModel)
 
 
 }
 }
 
 
+void BakeMesh::GetST(int triIndex, int channel, const Vector3& barycentric, Vector2& st) const
+{
+    if (triIndex < 0 || triIndex >= numTriangles_)
+        return;
+
+    const MMTriangle* tri = &triangles_[triIndex];
+
+    const Vector2& st0 = channel == 0 ? vertices_[tri->indices_[0]].uv0_ : vertices_[tri->indices_[0]].uv1_;
+    const Vector2& st1 = channel == 0 ? vertices_[tri->indices_[1]].uv0_ : vertices_[tri->indices_[1]].uv1_;
+    const Vector2& st2 = channel == 0 ? vertices_[tri->indices_[2]].uv0_ : vertices_[tri->indices_[2]].uv1_;
+
+    st = barycentric.z_*st0 + barycentric.x_*st1 + barycentric.y_*st2;
+
+}
+
 bool BakeMesh::GetUV0Color(int triIndex, const Vector3& barycentric, Color& colorOut) const
 bool BakeMesh::GetUV0Color(int triIndex, const Vector3& barycentric, Color& colorOut) const
 {
 {
     colorOut = Color::BLACK;
     colorOut = Color::BLACK;
@@ -619,13 +548,9 @@ bool BakeMesh::GetUV0Color(int triIndex, const Vector3& barycentric, Color& colo
         return false;
         return false;
     }
     }
 
 
-    const Vector2& st0 = vertices_[tri->indices_[0]].uv0_;
-    const Vector2& st1 = vertices_[tri->indices_[1]].uv0_;
-    const Vector2& st2 = vertices_[tri->indices_[2]].uv0_;
-
-    const float u = barycentric.x_, v = barycentric.y_, w = barycentric.z_;
+    Vector2 st;
 
 
-    const Vector2 st = w*st0 + u*st1 + v*st2;
+    GetST(triIndex, 0, barycentric, st);
 
 
     int x = diffuse->GetWidth() * st.x_;
     int x = diffuse->GetWidth() * st.x_;
     int y = diffuse->GetHeight() * st.y_;
     int y = diffuse->GetHeight() * st.y_;
@@ -641,69 +566,6 @@ bool BakeMesh::GetUV0Color(int triIndex, const Vector3& barycentric, Color& colo
     return true;
     return true;
 }
 }
 
 
-BounceBakeLight* BakeMesh::GenerateBounceBakeLight()
-{
-    if (bounceSamples_.Null())
-        return 0;
-
-    BounceSample b;
-
-    PODVector<BounceSample> bounceSamples;
-
-    for (int y = 0; y < bounceHeight_; y++)
-    {
-        for (int x = 0; x < bounceWidth_; x++)
-        {
-            const BounceSample& bSrc = bounceSamples_[y * bounceWidth_ + x];
-
-            if (bSrc.triIndex_[0] == -1)
-                continue;
-
-            b = bSrc;
-
-            // average of positions
-            b.position_ = bSrc.position_ / bSrc.hits_;
-
-            // average of colors
-            b.srcColor_ = bSrc.srcColor_ / bSrc.hits_;
-
-            if (b.srcColor_.Length() < 0.15f)
-                continue;
-
-            b.radiance_ = bSrc.radiance_;
-
-            // TODO: proper falloff
-            b.radiance_ *= 0.4f;
-
-            if ((b.radiance_/b.hits_).Length() < 0.05)
-                continue;
-
-            bounceSamples.Push(b);
-
-            const bool generateDebugNodes = false;
-            if (generateDebugNodes)
-            {
-                node_->CreateChild(ToString("Bounce Pass:%i x:%i y:%i", sceneBaker_->GetCurrentGIBounce(), x, y))->SetWorldPosition(b.position_);
-            }
-        }
-    }
-
-    if (!bounceSamples.Size())
-    {
-        ResetBounce();
-        return 0;
-    }
-
-    BounceBakeLight* bakeLight = new BounceBakeLight(context_, sceneBaker_);
-
-    bakeLight->SetBakeMesh(this);
-    bakeLight->SetBounceSamples(bounceSamples);
-
-    ResetBounce();
-
-    return bakeLight;
-}
-
 void BakeMesh::IntersectFilter(void* ptr, RTCRay& ray)
 void BakeMesh::IntersectFilter(void* ptr, RTCRay& ray)
 {
 {
     CommonFilter(static_cast<BakeMesh*>(ptr), ray);
     CommonFilter(static_cast<BakeMesh*>(ptr), ray);

+ 9 - 7
Source/AtomicGlow/Kernel/BakeMesh.h

@@ -31,6 +31,7 @@
 #include "BakeModel.h"
 #include "BakeModel.h"
 #include "BakeNode.h"
 #include "BakeNode.h"
 #include "RadianceMap.h"
 #include "RadianceMap.h"
+#include "Photons.h"
 
 
 namespace AtomicGlow
 namespace AtomicGlow
 {
 {
@@ -149,8 +150,14 @@ class BakeMesh : public BakeNode
 
 
     bool GetUV0Color(int triIndex, const Vector3 &barycentric, Color& colorOut) const;
     bool GetUV0Color(int triIndex, const Vector3 &barycentric, Color& colorOut) const;
 
 
+    void GetST(int triIndex, int channel, const Vector3& barycentric, Vector2& st) const;
+
     const Color& GetAmbientColor() const { return ambientColor_; }
     const Color& GetAmbientColor() const { return ambientColor_; }
 
 
+    PhotonMap* GetPhotonMap() const { return photonMap_; }
+
+    void SetPhotonMap(PhotonMap* photonMap) { photonMap_ = photonMap; }
+
 private:
 private:
 
 
     struct ShaderData
     struct ShaderData
@@ -169,8 +176,6 @@ private:
 
 
     bool LightPixel(ShaderData* shaderData, int x, int y, const Vector3& barycentric,const Vector3& dx, const Vector3& dy, float coverage);
     bool LightPixel(ShaderData* shaderData, int x, int y, const Vector3& barycentric,const Vector3& dx, const Vector3& dy, float coverage);
 
 
-    void ResetBounce();
-
     // mesh geometry, in world space
     // mesh geometry, in world space
 
 
     BoundingBox boundingBox_;
     BoundingBox boundingBox_;
@@ -196,15 +201,12 @@ private:
     SharedArrayPtr<bool> radiancePassAccept_;
     SharedArrayPtr<bool> radiancePassAccept_;
     // radiance -> triangle contributor
     // radiance -> triangle contributor
     SharedArrayPtr<int> radianceTriIndices_;
     SharedArrayPtr<int> radianceTriIndices_;
-    SharedArrayPtr<BounceSample> bounceSamples_;
+
+    SharedPtr<PhotonMap> photonMap_;
 
 
     unsigned radianceHeight_;
     unsigned radianceHeight_;
     unsigned radianceWidth_;
     unsigned radianceWidth_;
 
 
-    unsigned bounceWidth_;
-    unsigned bounceHeight_;
-    unsigned bounceGranularity_;
-
     unsigned embreeGeomID_;
     unsigned embreeGeomID_;
 
 
     // multithreading
     // multithreading

+ 12 - 1
Source/AtomicGlow/Kernel/EmbreeScene.h

@@ -46,10 +46,21 @@ class EmbreeScene : public Object
 
 
     RTCScene GetRTCScene() const { return rtcScene_; }
     RTCScene GetRTCScene() const { return rtcScene_; }
 
 
+    void RegisterBakeMesh(unsigned geomID, BakeMesh* mesh)
+    {
+        meshMapLookup_[geomID] = mesh;
+    }
+
+    BakeMesh* GetBakeMesh(unsigned geomID) const
+    {
+        return meshMapLookup_[geomID] ? meshMapLookup_[geomID]->Get() : 0;
+    }
+
+
 private:
 private:
 
 
     // embree geomID -> MeshMap
     // embree geomID -> MeshMap
-    HashMap<unsigned, BakeMesh*> meshMapLookup_;
+    HashMap<unsigned, WeakPtr<BakeMesh>> meshMapLookup_;
 
 
     RTCDevice rtcDevice_;
     RTCDevice rtcDevice_;
     RTCScene rtcScene_;
     RTCScene rtcScene_;

+ 1 - 28
Source/AtomicGlow/Kernel/GlowTypes.h

@@ -28,8 +28,6 @@ using namespace Atomic;
 namespace AtomicGlow
 namespace AtomicGlow
 {
 {
 
 
-const unsigned GLOW_MAX_BOUNCE_SAMPLE_TRIANGLES = 8;
-
 enum GlowLightMode
 enum GlowLightMode
 {
 {
     GLOW_LIGHTMODE_UNDEFINED,
     GLOW_LIGHTMODE_UNDEFINED,
@@ -39,31 +37,6 @@ enum GlowLightMode
     GLOW_LIGHTMODE_COMPLETE
     GLOW_LIGHTMODE_COMPLETE
 };
 };
 
 
-struct BounceSample
-{
-    int triIndex_[GLOW_MAX_BOUNCE_SAMPLE_TRIANGLES];
-    Vector3 position_;
-    Vector3 radiance_;
-    Vector3 srcColor_;
-    int hits_;
-
-    BounceSample()
-    {
-        Reset();
-    }
-
-    void Reset()
-    {
-        for (int i = 0; i < GLOW_MAX_BOUNCE_SAMPLE_TRIANGLES; i++)
-        {
-            triIndex_[i] = -1;
-        }
-
-        Vector3 v(-1, -1, -1);
-        position_ = radiance_ = v;
-        srcColor_ = Vector3::ZERO;
-        hits_ = 0;
-    }
-};
+const float LIGHT_ANGLE_EPSILON = 0.001f;
 
 
 }
 }

+ 279 - 0
Source/AtomicGlow/Kernel/Photons.cpp

@@ -0,0 +1,279 @@
+//
+// Copyright (c) 2017, THUNDERBEAST GAMES LLC All rights reserved
+// Copyright (c) 2015 Dmitry Sovetov
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "EmbreeScene.h"
+#include "SceneBaker.h"
+#include "Photons.h"
+#include "BakeMesh.h"
+
+namespace AtomicGlow
+{
+
+
+PhotonMap::PhotonMap(int width, int height) :
+    width_(width),
+    height_(height)
+{
+
+    if (width_ > 0 && height_ > 0)
+    {
+        photons_ = new Photon[width_ * height_];
+
+        for (int i = 0; i < width_ * height_; i++)
+        {
+            photons_[i].Reset();
+        }
+    }
+
+}
+
+PhotonMap::~PhotonMap()
+{
+
+}
+
+void PhotonMap::Gather( int radius )
+{
+    for( int y = 0; y < height_; y++ )
+    {
+        for( int x = 0; x < width_; x++ )
+        {
+            photons_[y * width_ + x].gathered_ = Gather(x, y, radius);
+        }
+    }
+}
+
+Color PhotonMap::Gather( int x, int y, int radius ) const
+{
+    Color color;
+    int photons = 0;
+
+    for( int j = y - radius; j <= y + radius; j++ )
+    {
+        for( int i = x - radius; i <= x + radius; i++ )
+        {
+            if( i < 0 || j < 0 || i >= width_ || j >= height_ )
+            {
+                continue;
+            }
+
+            const Photon& photon = photons_[j * width_ + i];
+
+            float distance = sqrtf( static_cast<float>( (x - i) * (x - i) + (y - j) * (y - j) ) );
+
+            if( distance > radius ) {
+                continue;
+            }
+
+            if (photon.photons_)
+            {
+                color += photon.color_;
+                photons += photon.photons_;
+            }
+        }
+    }
+
+    if( photons == 0 )
+    {
+        return Color::BLACK;
+    }
+
+    float divisor = 1.0f / static_cast<float>( photons );
+
+    color.r_ *= divisor;
+    color.g_ *= divisor;
+    color.b_ *= divisor;
+    color.a_ *= divisor;
+
+    return color;
+}
+
+
+Photons::Photons( SceneBaker* sceneBaker, int passCount, int maxDepth, float energyThreshold, float maxDistance )
+    : sceneBaker_( SharedPtr<SceneBaker>(sceneBaker) ),
+      passCount_( passCount ),
+      maxDepth_( maxDepth ),
+      energyThreshold_( energyThreshold ),
+      maxDistance_( maxDistance ),
+      photonCount_( 0 )
+{
+
+}
+
+int Photons::Emit( const Vector<SharedPtr<BakeLight>>& bakeLights )
+{
+    // TODO: depending on performance, photon emission may need to be threaded
+
+    for( int j = 0; j < passCount_; j++ )
+    {
+        Vector<SharedPtr<BakeLight>>::ConstIterator itr = bakeLights.Begin();
+
+        while (itr != bakeLights.End())
+        {
+            BakeLight* bakeLight = *itr;
+
+            if( !bakeLight->GetPhotonEmitter() )
+            {
+                itr++;
+                continue;
+            }
+
+            EmitPhotons( bakeLight );
+
+            itr++;
+        }
+    }
+
+    return photonCount_;
+}
+
+void Photons::EmitPhotons( const BakeLight* light )
+{
+    PhotonEmitter* emitter = light->GetPhotonEmitter();
+    Vector3 direction;
+    Vector3 position;
+
+    for( int i = 0, n = emitter->GetPhotonCount(); i < n; i++ )
+    {
+        // ** Emit photon
+        emitter->Emit( sceneBaker_, position, direction );
+
+        // ** Calculate light cutoff
+        float cut = 1.0f;
+
+        if( const LightCutoff* cutoff = light->GetCutoffModel() )
+        {
+            cut = cutoff->GetCutoffForDirection( direction );
+        }
+
+        if( cut <= 0.0f )
+        {
+            continue;
+        }
+
+        Trace( light->GetAttenuationModel(), position, direction, light->GetColor() * light->GetIntensity() * cut, 0 );
+    }
+
+}
+
+void Photons::Trace(const LightAttenuation* attenuation, const Vector3& position, const Vector3& direction, const Color& color, int depth , unsigned lastGeomID, unsigned lastPrimID)
+{
+    // ** Maximum depth or energy threshold exceeded
+    if( depth > maxDepth_ || color.Luma() < energyThreshold_ )
+    {
+        return;
+    }
+
+    // clean this mess up
+    RTCScene scene = sceneBaker_->GetEmbreeScene()->GetRTCScene();
+
+    lightRay_.SetupRay(position, direction, .001f, maxDistance_);
+
+    RTCRay& ray = lightRay_.rtcRay_;
+
+    rtcIntersect(scene, ray);
+
+    // The photon didn't hit anything
+    if (ray.geomID == RTC_INVALID_GEOMETRY_ID)
+    {
+        return;
+    }
+
+    // self hit
+    if (ray.geomID == lastGeomID && ray.primID == lastPrimID)
+    {
+        return;
+    }
+
+    Vector3 hitPosition = position + (direction * ray.tfar);
+
+    Vector3 hitNormal(ray.Ng[0], ray.Ng[1], ray.Ng[2]);
+    hitNormal.Normalize();
+
+    // ** Energy attenuation after a photon has passed the traced segment
+    float att = 1.0f;
+    if( attenuation ) {
+        /*att =*/ attenuation->Calculate( (position - hitPosition).Length() );
+    }
+
+    // ** Energy after reflection
+    float influence = LightInfluence::GetLambert( direction, hitNormal ) * att;
+
+    if (influence <= 0.0f)
+    {
+        return;
+    }
+
+    BakeMesh* bakeMesh = sceneBaker_->GetEmbreeScene()->GetBakeMesh(ray.geomID);
+
+    Color hitColor;
+    Vector3 bary(ray.u, ray.v, 1.0f-ray.u-ray.v);
+
+    // TODO: alpha mask support?
+    if (!bakeMesh || !bakeMesh->GetUV0Color(ray.primID, bary, hitColor))
+    {
+        return;
+    }
+
+    // ** Final photon color
+    hitColor.r_ *= (color.r_ * influence);
+    hitColor.g_ *= (color.g_ * influence);
+    hitColor.b_ *= (color.b_ * influence);
+
+    Vector2 st;
+    bakeMesh->GetST(ray.primID, 1, bary, st);
+
+    // ** Store photon energy
+    Store( bakeMesh->GetPhotonMap(), hitColor, st );
+
+    // ** Keep tracing
+    Vector3 dir;
+    Vector3::GetRandomHemisphereDirection(dir, hitNormal);
+    Trace( attenuation,hitPosition, dir, hitColor, depth + 1, ray.geomID, ray.primID );
+}
+
+void Photons::Store( PhotonMap* photonmap, const Color& color, const Vector2& uv )
+{
+    if( !photonmap ) {
+        return;
+    }
+
+    if (color.r_ < 0.01f && color.g_ < 0.01f && color.b_ < 0.01f)
+    {
+        // filter out tiny adds
+        return;
+    }
+
+    PhotonMap::Photon* photon = photonmap->GetPhoton( uv );
+
+    if( !photon ) {
+        return;
+    }
+
+    photon->color_ += color;
+    photon->photons_++;
+
+    photonCount_++;
+}
+
+}

+ 152 - 0
Source/AtomicGlow/Kernel/Photons.h

@@ -0,0 +1,152 @@
+//
+// Copyright (c) 2017, THUNDERBEAST GAMES LLC All rights reserved
+// Copyright (c) 2015 Dmitry Sovetov
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include <Atomic/Container/RefCounted.h>
+#include <Atomic/Container/ArrayPtr.h>
+
+#include "LightRay.h"
+#include "BakeLight.h"
+
+using namespace Atomic;
+
+namespace AtomicGlow
+{
+
+class SceneBaker;
+
+class PhotonMap : public RefCounted
+{
+    ATOMIC_REFCOUNTED(PhotonMap)
+
+public:
+
+    struct Photon
+    {
+      Color gathered_;
+      Color color_;
+      int photons_;
+
+      void Reset()
+      {
+        color_ = gathered_ = Color::BLACK;
+        photons_ = 0;
+      }
+    };
+
+    PhotonMap(int width, int height);
+    virtual ~PhotonMap();
+
+    Photon* GetPhoton(const Vector2& uv)
+    {
+        int x = static_cast<int>( uv.x_ * (width_  - 1) );
+        int y = static_cast<int>( uv.y_ * (height_ - 1) );
+
+        if (x < 0 || x >= width_)
+            return 0;
+
+        if (y < 0 || y >= height_)
+            return 0;
+
+        return &photons_[y * width_ + x];
+
+    }
+
+    void Gather( int radius );
+
+    Color Gather( int x, int y, int radius ) const;
+
+private:
+
+    int width_;
+    int height_;
+
+    SharedArrayPtr<Photon> photons_;
+
+};
+
+class Photons : public RefCounted
+{
+
+    ATOMIC_REFCOUNTED(Photon)
+
+public:
+
+    // Constructs a Photons instance.
+    // param scene Scene to be baked.
+    // param passCount Amount of photon passes.
+    // param maxDepth Maximum photon tracing depth (number of light bounces).
+    // param energyThreshold The minimum energy that photon should have to continue tracing.
+    // param maxDistance The reflected light maximum distance. All intersections above this value will be ignored.
+    Photons( SceneBaker* sceneBaker, int passCount, int maxDepth, float energyThreshold, float maxDistance );
+
+    // Emits photons from all scene lights, returning number of photons emitted
+    virtual int Emit( const Vector<SharedPtr<BakeLight>>& bakeLights );
+
+private:
+
+    // Emits photons from a given light.
+    void EmitPhotons( const BakeLight* light );
+
+    // Traces a photon path for a given depth.
+    /*
+     Traces a photon path for a given depth. Each time the photon bounces the
+     reflected light is stored to a photon map.
+
+     \param attenuation Light attenuation model.
+     \param position Photon's start position.
+     \param direction Photon's direction.
+     \param color Photon's color.
+     \param energy Photon's energy.
+     \param depth Current trace depth.
+     */
+    void Trace(const LightAttenuation* attenuation, const Vector3& position, const Vector3& direction, const Color& color, int depth, unsigned lastGeomID = RTC_INVALID_GEOMETRY_ID, unsigned lastPrimID = RTC_INVALID_GEOMETRY_ID );
+
+    //Stores a photon bounce.
+    void Store( PhotonMap* photonmap, const Color& color, const Vector2& uv );
+
+private:
+
+    // Parent scene.
+    SharedPtr<SceneBaker> sceneBaker_;
+
+    // Amount of photon passes, at each pass there will be emitted N photons.
+    int passCount_;
+
+    // Maximum tracing depth.
+    int maxDepth_;
+
+    // Photon energy threshold.
+    float energyThreshold_;
+
+    // Maximum intersection distance
+    float maxDistance_;
+
+    // Total amount of photons stored.
+    int photonCount_;
+
+    LightRay lightRay_;
+};
+
+}

+ 240 - 35
Source/AtomicGlow/Kernel/SceneBaker.cpp

@@ -34,12 +34,14 @@
 #include <Atomic/Graphics/Light.h>
 #include <Atomic/Graphics/Light.h>
 #include <Atomic/Graphics/StaticModel.h>
 #include <Atomic/Graphics/StaticModel.h>
 
 
+#include "LightRay.h"
 #include "BakeModel.h"
 #include "BakeModel.h"
 #include "BakeMesh.h"
 #include "BakeMesh.h"
 #include "BakeLight.h"
 #include "BakeLight.h"
 #include "EmbreeScene.h"
 #include "EmbreeScene.h"
 #include "LightMapPacker.h"
 #include "LightMapPacker.h"
 #include "SceneBaker.h"
 #include "SceneBaker.h"
+#include "Photons.h"
 
 
 namespace AtomicGlow
 namespace AtomicGlow
 {
 {
@@ -138,12 +140,17 @@ bool SceneBaker::GenerateLightmaps()
 
 
 }
 }
 
 
-void SceneBaker::TraceRay(LightRay* lightRay, const PODVector<BakeLight*>& bakeLights_)
+void SceneBaker::TraceRay(LightRay* lightRay, const PODVector<BakeLight*>& bakeLights)
 {
 {
-    for (unsigned i = 0; i < bakeLights_.Size(); i++)
+    if (currentLightMode_ == GLOW_LIGHTMODE_DIRECT)
     {
     {
-        bakeLights_[i]->Light(lightRay);
+        DirectLight(lightRay, bakeLights);
     }
     }
+    else
+    {
+        IndirectLight(lightRay);
+    }
+
 }
 }
 
 
 bool SceneBaker::LightDirect()
 bool SceneBaker::LightDirect()
@@ -170,39 +177,52 @@ bool SceneBaker::LightDirect()
 
 
 void SceneBaker::LightDirectFinish()
 void SceneBaker::LightDirectFinish()
 {
 {
-    bakeLights_.Clear();
+
 }
 }
 
 
-bool SceneBaker::LightGI()
+bool SceneBaker::EmitPhotons()
 {
 {
-    // Indirect Lighting
-    currentLightMode_ = GLOW_LIGHTMODE_INDIRECT;
+    Photons photons(this, GlobalGlowSettings.photonPassCount_, GlobalGlowSettings.photonBounceCount_,
+                          GlobalGlowSettings.photonEnergyThreshold_, GlobalGlowSettings.photonMaxDistance_);
 
 
-    if (!GlobalGlowSettings.giEnabled_ || currentGIPass_ >= GlobalGlowSettings.giMaxBounces_)
+    int numPhotons = photons.Emit(bakeLights_);
+
+    ATOMIC_LOGINFOF("SceneBaker::EmitPhotons() - %i photons emitted", numPhotons);
+
+    if (!numPhotons)
     {
     {
         return false;
         return false;
     }
     }
 
 
-    ATOMIC_LOGINFOF("GI Pass #%i of %i", currentGIPass_ + 1, GlobalGlowSettings.giMaxBounces_);
 
 
-    BounceBakeLight* blight;
-    unsigned totalBounceSamples = 0;
-
-    for (unsigned i = 0; i < bakeMeshes_.Size(); i++)
+    for( unsigned i = 0; i < bakeMeshes_.Size(); i++ )
     {
     {
-        BakeMesh* mesh = bakeMeshes_[i];
-        blight = mesh->GenerateBounceBakeLight();
-        if (blight)
+        if( PhotonMap* photons = bakeMeshes_[i]->GetPhotonMap() )
         {
         {
-            bakeLights_.Push(SharedPtr<BakeLight>(blight));
-            totalBounceSamples += blight->GetNumBounceSamples();
+            photons->Gather( GlobalGlowSettings.finalGatherRadius_ );
         }
         }
     }
     }
 
 
-    if (!bakeLights_.Size())
+    return true;
+}
+
+bool SceneBaker::LightGI()
+{
+    // Indirect Lighting
+    currentLightMode_ = GLOW_LIGHTMODE_INDIRECT;
+
+    // We currently only need one GI pass
+    if (!GlobalGlowSettings.giEnabled_ || currentGIPass_ >= 1)
+    {
         return false;
         return false;
+    }
+
+    ATOMIC_LOGINFOF("GI Pass #%i of %i", currentGIPass_ + 1, 1);
 
 
-    ATOMIC_LOGINFOF("IMPORTANT: Optimize allocation of samples! Indirect lighting with %u bounce lights and %u samples", bakeLights_.Size(), totalBounceSamples);
+    bool photons = EmitPhotons();
+
+    if (!photons)
+        return false;
 
 
     for (unsigned i = 0; i < bakeMeshes_.Size(); i++)
     for (unsigned i = 0; i < bakeMeshes_.Size(); i++)
     {
     {
@@ -216,13 +236,7 @@ bool SceneBaker::LightGI()
 
 
 void SceneBaker::LightGIFinish()
 void SceneBaker::LightGIFinish()
 {
 {
-    bakeLights_.Clear();
-
     currentGIPass_++;
     currentGIPass_++;
-
-    if (currentGIPass_ >= GlobalGlowSettings.giMaxBounces_)
-    {
-    }
 }
 }
 
 
 
 
@@ -247,7 +261,13 @@ bool SceneBaker::Light(const GlowLightMode lightMode)
         if (!LightGI())
         if (!LightGI())
         {
         {
             currentLightMode_ = GLOW_LIGHTMODE_COMPLETE;
             currentLightMode_ = GLOW_LIGHTMODE_COMPLETE;
-            ATOMIC_LOGINFO("Cycle: GI - no work to be done");
+
+            // We currently only need one GI pass
+            if (GlobalGlowSettings.giEnabled_ && currentGIPass_ == 0)
+            {
+                ATOMIC_LOGINFO("Cycle: GI - no work to be done");
+            }
+
             return false;
             return false;
         }
         }
 
 
@@ -337,9 +357,9 @@ bool SceneBaker::LoadScene(const String& filename)
             continue;
             continue;
 
 
         zones.Push(zone);;
         zones.Push(zone);;
-        SharedPtr<ZoneBakeLight> zlight(new ZoneBakeLight(context_, this));
-        zlight->SetZone(zone);
-        bakeLights_.Push(zlight);
+
+        BakeLight* bakeLight = BakeLight::CreateZoneLight(this, zone->GetAmbientColor());
+        bakeLights_.Push(SharedPtr<BakeLight>(bakeLight));
     }
     }
 
 
     // Lights
     // Lights
@@ -348,22 +368,24 @@ bool SceneBaker::LoadScene(const String& filename)
 
 
     for (unsigned i = 0; i < lightNodes.Size(); i++)
     for (unsigned i = 0; i < lightNodes.Size(); i++)
     {
     {
-        Atomic::Light* light = lightNodes[i]->GetComponent<Atomic::Light>();
+        Node* lightNode = lightNodes[i];
+        Atomic::Light* light = lightNode->GetComponent<Atomic::Light>();
 
 
-        if (!light->GetNode()->IsEnabled()|| !light->IsEnabled())
+        if (!lightNode->IsEnabled() || !light->IsEnabled())
             continue;
             continue;
 
 
         if (light->GetLightType() == LIGHT_DIRECTIONAL)
         if (light->GetLightType() == LIGHT_DIRECTIONAL)
         {
         {
+            /*
             SharedPtr<DirectionalBakeLight> dlight(new DirectionalBakeLight(context_, this));
             SharedPtr<DirectionalBakeLight> dlight(new DirectionalBakeLight(context_, this));
             dlight->SetLight(light);
             dlight->SetLight(light);
             bakeLights_.Push(dlight);
             bakeLights_.Push(dlight);
+            */
         }
         }
         else if (light->GetLightType() == LIGHT_POINT)
         else if (light->GetLightType() == LIGHT_POINT)
         {
         {
-            SharedPtr<PointBakeLight> dlight(new PointBakeLight(context_, this));
-            dlight->SetLight(light);
-            bakeLights_.Push(dlight);
+            BakeLight* bakeLight = BakeLight::CreatePointLight(this, lightNode->GetWorldPosition(), light->GetRange(), light->GetColor(), 1.0f, light->GetCastShadows());
+            bakeLights_.Push(SharedPtr<BakeLight>(bakeLight));
         }
         }
 
 
     }
     }
@@ -453,4 +475,187 @@ bool SceneBaker::LoadScene(const String& filename)
     return Preprocess();
     return Preprocess();
 }
 }
 
 
+// DIRECT LIGHT
+
+void SceneBaker::DirectLight( LightRay* lightRay, const PODVector<BakeLight*>& bakeLights )
+{
+    for (unsigned i = 0; i < bakeLights.Size(); i++)
+    {
+        BakeLight* bakeLight = bakeLights[i];
+
+        Color influence;
+
+        // if (light->GetVertexGenerator())
+        //{
+        // influence = DirectLightFromPointSet( lightRay, bakeLight );
+        //}
+        //else
+        {
+            influence = DirectLightFromPoint( lightRay, bakeLight );
+        }
+
+        if (influence.r_ || influence.g_ || influence.b_ )
+        {
+            lightRay->samplePoint_.bakeMesh->ContributeRadiance(lightRay, influence.ToVector3());
+        }
+
+    }
+}
+
+Color SceneBaker::DirectLightFromPoint( LightRay* lightRay, const BakeLight* light ) const
+{
+    float influence = DirectLightInfluenceFromPoint( lightRay, light->GetPosition(), light );
+
+    if( influence > 0.0f )
+    {
+        return light->GetColor() * light->GetIntensity() * influence;
+    }
+
+    return Color::BLACK;
+}
+
+Color SceneBaker::DirectLightFromPointSet( LightRay* lightRay, const BakeLight* light ) const
+{
+    /*
+    LightVertexGenerator* vertexGenerator = light->vertexGenerator();
+
+    // ** No light vertices generated - just exit
+    if( vertexGenerator->vertexCount() == 0 ) {
+        return Rgb( 0, 0, 0 );
+    }
+
+    const LightVertexBuffer& vertices = vertexGenerator->vertices();
+    Rgb                      color    = Rgb( 0, 0, 0 );
+
+    for( int i = 0, n = vertexGenerator->vertexCount(); i < n; i++ ) {
+        const LightVertex&  vertex    = vertices[i];
+        float               influence = influenceFromPoint( lumel, vertex.m_position + light->position(), light );
+
+        // ** We have a non-zero light influence - add a light color to final result
+        if( influence > 0.0f ) {
+            color += light->color() * light->intensity() * influence;
+        }
+    }
+
+    return color / static_cast<float>( vertexGenerator->vertexCount() );
+    */
+
+    return Color::BLACK;
+}
+
+// ** DirectLight::influenceFromPoint
+float SceneBaker::DirectLightInfluenceFromPoint(LightRay* lightRay, const Vector3 &point, const BakeLight* light ) const
+{
+    float inf       = 1.0f;
+    float att       = 1.0f;
+    float cut       = 1.0f;
+    float distance  = 0.0f;
+
+    // Calculate light influence.
+    if( const LightInfluence* influence = light->GetInfluenceModel() )
+    {
+        inf = influence->Calculate( lightRay, point, distance );
+    }
+
+    // Calculate light cutoff.
+    if( const LightCutoff* cutoff = light->GetCutoffModel() )
+    {
+        cut = cutoff->Calculate( lightRay->samplePoint_.position);
+    }
+
+    // Calculate light attenuation
+    if( const LightAttenuation* attenuation = light->GetAttenuationModel() )
+    {
+        att = attenuation->Calculate( distance );
+    }
+
+    // Return final influence
+    return inf * att * cut;
+}
+
+// INDIRECT LIGHT
+
+void SceneBaker::IndirectLight( LightRay* lightRay)
+{
+    BakeMesh* bakeMesh = 0;
+    LightRay::SamplePoint& source = lightRay->samplePoint_;
+
+    Vector3 gathered;
+
+    int nsamples = GlobalGlowSettings.finalGatherSamples_;
+    float maxDistance = GlobalGlowSettings.finalGatherDistance_; // , settings.m_finalGatherRadius, settings.m_skyColor, settings.m_ambientColor
+
+    for( int k = 0; k <nsamples; k++ )
+    {
+        Vector3 dir;
+        Vector3::GetRandomHemisphereDirection(dir, source.normal);
+
+        float influence = Max<float>( source.normal.DotProduct(dir), 0.0f );
+
+        if (influence > 1.0f)
+        {
+            // ATOMIC_LOGINFO("This shouldn't happen");
+        }
+
+        RTCRay& ray = lightRay->rtcRay_;
+        lightRay->SetupRay(source.position, dir, .001f, maxDistance);
+
+        rtcIntersect(GetEmbreeScene()->GetRTCScene(), ray);
+
+        if (ray.geomID == RTC_INVALID_GEOMETRY_ID)
+        {
+            // gathered += skyColor * influence + ambientColor;
+            continue;
+        }               
+
+        bakeMesh = GetEmbreeScene()->GetBakeMesh(ray.geomID);
+
+        if (!bakeMesh)
+        {
+            continue;
+        }
+
+        if (bakeMesh == source.bakeMesh && ray.primID == source.triangle)
+        {
+            // do not self light
+            continue;
+        }
+
+        const BakeMesh::MMTriangle* tri = bakeMesh->GetTriangle(ray.primID);
+
+        // TODO: interpolate normal, if artifacting
+        if( dir.DotProduct(tri->normal_) >= 0.0f )
+        {
+            continue;
+        }
+
+        PhotonMap* photonMap = bakeMesh->GetPhotonMap();
+
+        if (!photonMap)
+        {
+            continue;
+        }
+
+        Vector3 bary(ray.u, ray.v, 1.0f-ray.u-ray.v);
+        Vector2 st;
+        bakeMesh->GetST(ray.primID, 1, bary, st);
+
+        PhotonMap::Photon* photon = photonMap->GetPhoton(Vector2(ray.u, ray.v));
+
+        if (!photon || !photon->photons_)
+            continue;
+
+        gathered += photon->gathered_.ToVector3() * influence;// + ambientColor;
+
+    }
+
+    gathered /= static_cast<float>( nsamples );
+
+    if (gathered.x_ >= 0.01f || gathered.y_ >= 0.01f || gathered.z_ >= 0.01f )
+    {
+        source.bakeMesh->ContributeRadiance(lightRay, gathered, GLOW_LIGHTMODE_INDIRECT);
+    }
+
+}
+
 }
 }

+ 14 - 2
Source/AtomicGlow/Kernel/SceneBaker.h

@@ -56,7 +56,7 @@ class SceneBaker : public Object
 
 
     void QueryLights(const BoundingBox& bbox, PODVector<BakeLight*>& lights);
     void QueryLights(const BoundingBox& bbox, PODVector<BakeLight*>& lights);
 
 
-    void TraceRay(LightRay* lightRay, const PODVector<AtomicGlow::BakeLight *>& bakeLights_);
+    void TraceRay(LightRay* lightRay, const PODVector<AtomicGlow::BakeLight *>& bakeLights);
 
 
     Scene* GetScene() const { return scene_; }
     Scene* GetScene() const { return scene_; }
 
 
@@ -68,7 +68,7 @@ class SceneBaker : public Object
 
 
     bool GetStandaloneMode() const { return standaloneMode_; }
     bool GetStandaloneMode() const { return standaloneMode_; }
 
 
-    void SetStandaloneMode(bool standaloneMode) { standaloneMode_ = standaloneMode; }
+    void SetStandaloneMode(bool standaloneMode) { standaloneMode_ = standaloneMode; }    
 
 
 private:
 private:
 
 
@@ -88,6 +88,18 @@ private:
 
 
     bool SaveLitScene();
     bool SaveLitScene();
 
 
+    // Direct lighting
+
+    void DirectLight( LightRay* lightRay, const PODVector<BakeLight*>& bakeLights);
+    Color DirectLightFromPoint( LightRay* lightRay, const BakeLight* light ) const;
+    Color DirectLightFromPointSet( LightRay* lightRay, const BakeLight* light ) const;
+    float DirectLightInfluenceFromPoint(LightRay* lightRay, const Vector3 &point, const BakeLight *light ) const;
+
+    // New GI
+
+    bool EmitPhotons();
+    void IndirectLight(LightRay* lightRay);
+
     SharedPtr<Scene> scene_;
     SharedPtr<Scene> scene_;
     SharedPtr<EmbreeScene> embreeScene_;
     SharedPtr<EmbreeScene> embreeScene_;