summaryrefslogtreecommitdiff
path: root/Runtime/Camera/LightManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/Camera/LightManager.cpp')
-rw-r--r--Runtime/Camera/LightManager.cpp635
1 files changed, 635 insertions, 0 deletions
diff --git a/Runtime/Camera/LightManager.cpp b/Runtime/Camera/LightManager.cpp
new file mode 100644
index 0000000..eaf5d89
--- /dev/null
+++ b/Runtime/Camera/LightManager.cpp
@@ -0,0 +1,635 @@
+#include "UnityPrefix.h"
+#include "LightManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Light.h"
+#include "UnityScene.h"
+#include "CullResults.h"
+#include "BaseRenderer.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Math/SphericalHarmonics.h"
+#include "Runtime/Shaders/ShaderKeywords.h"
+#include "Runtime/Camera/LightProbes.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+static ShaderKeyword kKeywordVertexLight = keywords::Create("VERTEXLIGHT_ON");
+
+static LightManager* s_LightManager = NULL;
+
+LightManager& GetLightManager()
+{
+ DebugAssert (s_LightManager != NULL);
+ return *s_LightManager;
+}
+
+void LightManager::InitializeClass()
+{
+ DebugAssert (s_LightManager == NULL);
+ s_LightManager = new LightManager();
+}
+
+void LightManager::CleanupClass()
+{
+ DebugAssert (s_LightManager != NULL);
+ delete s_LightManager;
+ s_LightManager = NULL;
+}
+
+
+
+
+static float CalculateLightIntensityAtPoint (const Light& light, const Vector3f& position)
+{
+ float lum = light.GetColor().GreyScaleValue() * light.GetIntensity();
+
+ if (light.GetType () == kLightDirectional)
+ {
+ if (light.GetShadows() != kShadowNone)
+ return lum * 16.0f;
+ else
+ return lum;
+ }
+ else
+ {
+ float sqrDistance = SqrMagnitude (position - light.GetWorldPosition());
+ float atten = light.AttenuateApprox (sqrDistance);
+ return lum * atten;
+ }
+}
+
+
+static ColorRGBAf CalculateLightColorAtPointForSH (const Light& light, const AABB& bounds, const AABB& localBounds, float scale)
+{
+ float lum = light.GetIntensity();
+
+ if (light.GetType () == kLightDirectional)
+ {
+ return lum * GammaToActiveColorSpace (light.GetColor());
+ }
+ else
+ {
+ // objects larger than lights should be affected less
+ float objectSizeSqr = SqrMagnitude(localBounds.GetExtent() * scale);
+ float lightSizeSqr = light.GetRange() * light.GetRange();
+ if (objectSizeSqr > lightSizeSqr)
+ {
+ lum *= lightSizeSqr / objectSizeSqr;
+ }
+
+ Vector3f pos = bounds.GetCenter();
+ // don't ever treat SH lights as coming closer to center than at object bounds
+ float sqrDistance = std::max (SqrMagnitude (pos - light.GetWorldPosition()), objectSizeSqr);
+ float atten = light.AttenuateApprox (sqrDistance);
+ return (lum * atten) * GammaToActiveColorSpace (light.GetColor());
+ }
+}
+
+
+LightManager::LightManager ()
+: m_LastMainLight(NULL)
+{
+}
+
+
+LightManager::~LightManager () {
+}
+
+
+
+
+void LightManager::SetupVertexLights (int lightCount, const ActiveLight* const* lights)
+{
+ GfxDevice& device = GetGfxDevice();
+
+ int maxVertexLights = gGraphicsCaps.maxLights;
+ AssertIf(lightCount > maxVertexLights);
+ lightCount = std::min(lightCount, maxVertexLights); // just a safeguard, should not normally happen
+
+ // setup vertex lights
+ for( int i = 0; i < lightCount; ++i )
+ lights[i]->light->SetupVertexLight (i, lights[i]->visibilityFade);
+
+ // disable rest of lights
+ device.DisableLights( lightCount );
+}
+
+void SetSHConstants (const float sh[9][3], BuiltinShaderParamValues& params)
+{
+ Vector4f vCoeff[3];
+
+ static const float s_fSqrtPI = ((float)sqrtf(kPI));
+ const float fC0 = 1.0f/(2.0f*s_fSqrtPI);
+ const float fC1 = (float)sqrt(3.0f)/(3.0f*s_fSqrtPI);
+ const float fC2 = (float)sqrt(15.0f)/(8.0f*s_fSqrtPI);
+ const float fC3 = (float)sqrt(5.0f)/(16.0f*s_fSqrtPI);
+ const float fC4 = 0.5f*fC2;
+
+ int iC;
+ for( iC=0; iC<3; iC++ )
+ {
+ vCoeff[iC].x = -fC1*sh[3][iC];
+ vCoeff[iC].y = -fC1*sh[1][iC];
+ vCoeff[iC].z = fC1*sh[2][iC];
+ vCoeff[iC].w = fC0*sh[0][iC] - fC3*sh[6][iC];
+ }
+
+ params.SetVectorParam(kShaderVecSHAr, vCoeff[0]);
+ params.SetVectorParam(kShaderVecSHAg, vCoeff[1]);
+ params.SetVectorParam(kShaderVecSHAb, vCoeff[2]);
+
+ for( iC=0; iC<3; iC++ )
+ {
+ vCoeff[iC].x = fC2*sh[4][iC];
+ vCoeff[iC].y = -fC2*sh[5][iC];
+ vCoeff[iC].z = 3.0f*fC3*sh[6][iC];
+ vCoeff[iC].w = -fC2*sh[7][iC];
+ }
+
+ params.SetVectorParam(kShaderVecSHBr, vCoeff[0]);
+ params.SetVectorParam(kShaderVecSHBg, vCoeff[1]);
+ params.SetVectorParam(kShaderVecSHBb, vCoeff[2]);
+
+ vCoeff[0].x = fC4*sh[8][0];
+ vCoeff[0].y = fC4*sh[8][1];
+ vCoeff[0].z = fC4*sh[8][2];
+ vCoeff[0].w = 1.0f;
+
+ params.SetVectorParam(kShaderVecSHC, vCoeff[0]);
+}
+
+void LightManager::SetupForwardBaseLights (const ForwardLightsBlock& lights)
+{
+ BuiltinShaderParamValues& params = GetGfxDevice().GetBuiltinParamValues();
+
+ // Setup SH constants
+ SetSHConstants (lights.sh, params);
+
+ // Setup per-vertex light constants
+ Assert (lights.vertexLightCount <= 4);
+ Vector4f packedPosX;
+ Vector4f packedPosY;
+ Vector4f packedPosZ;
+ Vector4f packedAtten;
+ Vector4f lightColors[4];
+
+ const ActiveLight* const* vertexLights = lights.GetLights() + lights.addLightCount;
+ for (int i = 0; i < lights.vertexLightCount; ++i)
+ {
+ Light& light = *vertexLights[i]->light;
+ Vector3f pos = light.GetWorldPosition();
+ float intensity = light.GetIntensity() * 2.0f;
+ if (i == 0 && lights.lastAddLightBlend != 1.0f)
+ intensity *= (1.0f-lights.lastAddLightBlend);
+ else if (i == lights.vertexLightCount-1)
+ intensity *= lights.lastVertexLightBlend;
+ ColorRGBAf color = GammaToActiveColorSpace (light.GetColor()) * intensity;
+ float attenSq = Light::CalcQuadFac (light.GetRange());
+
+ packedPosX.GetPtr()[i] = pos.x;
+ packedPosY.GetPtr()[i] = pos.y;
+ packedPosZ.GetPtr()[i] = pos.z;
+ packedAtten.GetPtr()[i] = attenSq;
+ lightColors[i].Set (color.r, color.g, color.b, color.a);
+ }
+ for (int i = lights.vertexLightCount; i < kMaxForwardVertexLights; ++i)
+ {
+ packedPosX.GetPtr()[i] = 0;
+ packedPosY.GetPtr()[i] = 0;
+ packedPosZ.GetPtr()[i] = 0;
+ packedAtten.GetPtr()[i] = 1;
+ lightColors[i].Set (0,0,0,0);
+ }
+ if (lights.vertexLightCount)
+ {
+ params.SetVectorParam(kShaderVecVertexLightPosX0, packedPosX);
+ params.SetVectorParam(kShaderVecVertexLightPosY0, packedPosY);
+ params.SetVectorParam(kShaderVecVertexLightPosZ0, packedPosZ);
+ params.SetVectorParam(kShaderVecVertexLightAtten0, packedAtten);
+ params.SetVectorParam(kShaderVecLight0Diffuse, lightColors[0]);
+ params.SetVectorParam(kShaderVecLight1Diffuse, lightColors[1]);
+ params.SetVectorParam(kShaderVecLight2Diffuse, lightColors[2]);
+ params.SetVectorParam(kShaderVecLight3Diffuse, lightColors[3]);
+
+ g_ShaderKeywords.Enable (kKeywordVertexLight);
+ }
+ else
+ {
+ g_ShaderKeywords.Disable (kKeywordVertexLight);
+ }
+
+
+ // Setup per-pixel main light constants
+ if (lights.mainLight == NULL)
+ {
+ params.SetVectorParam(kShaderVecLightColor0, Vector4f(0,0,0,0));
+ return;
+ }
+
+ Light* light = lights.mainLight->light;
+ Assert (light->GetType() == kLightDirectional);
+
+ const Transform& transform = light->GetComponent(Transform);
+
+ Vector3f lightDir = transform.TransformDirection (Vector3f(0,0,-1));
+ params.SetVectorParam(kShaderVecWorldSpaceLightPos0, Vector4f(lightDir.x, lightDir.y, lightDir.z, 0.0f));
+
+ Matrix4x4f world2Light = light->GetWorldToLocalMatrix();
+ light->GetMatrix (&world2Light, &params.GetWritableMatrixParam(kShaderMatLightMatrix));
+
+ light->SetLightKeyword ();
+ light->SetPropsToShaderLab (1.0f);
+}
+
+void LightManager::SetupForwardAddLight (Light* light, float blend)
+{
+ Assert (light);
+
+ BuiltinShaderParamValues& params = GetGfxDevice().GetBuiltinParamValues();
+ Vector4f lightInfo;
+ if (light->GetType() != kLightDirectional)
+ {
+ Vector3f worldPos = light->GetWorldPosition();
+ lightInfo.Set (worldPos.x, worldPos.y, worldPos.z, 1.0f);
+ }
+ else
+ {
+ const Transform& transform = light->GetComponent(Transform);
+ Vector3f worldDir = transform.TransformDirection (Vector3f(0,0,-1));
+ lightInfo.Set (worldDir.x, worldDir.y, worldDir.z, 0.0f);
+ }
+ params.SetVectorParam(kShaderVecWorldSpaceLightPos0, lightInfo);
+
+ Matrix4x4f world2Light = light->GetWorldToLocalMatrix();
+ light->GetMatrix (&world2Light, &params.GetWritableMatrixParam(kShaderMatLightMatrix));
+
+ light->SetLightKeyword ();
+ light->SetPropsToShaderLab (blend);
+}
+
+
+
+void LightManager::AddLight (Light* source)
+{
+ DebugAssert (source);
+
+ m_Lights.push_back(*source);
+}
+
+void LightManager::RemoveLight (Light* source)
+{
+ DebugAssert (source && source->IsInList());
+
+ m_Lights.erase(source);
+
+ // If this was the last main light, clear it
+ if (m_LastMainLight == source)
+ m_LastMainLight = NULL;
+}
+
+static float kRenderModeSortBias[Light::kRenderModeCount] = {
+ 0.0f,
+ 1000.0f,
+ -1000.0f,
+};
+
+
+
+
+// Figures out lights for given object, sorts them by intensity.
+// Fills them into an array of CulledLight.
+// Returns number of lights filled.
+
+struct CulledLight
+{
+ UInt32 lightIndex;
+ float sortIntensity;
+ friend bool operator< (const CulledLight& lhs, const CulledLight& rhs)
+ {
+ return lhs.sortIntensity > rhs.sortIntensity;
+ }
+};
+
+static void SortLights (dynamic_array<CulledLight>& outLights, const UInt32* lightIndices, UInt32 lightCount, const ActiveLights& activeLights, const Vector3f& objectCenter)
+{
+ outLights.resize_uninitialized(lightCount);
+ for (size_t i = 0; i < lightCount; i++)
+ {
+ // Light passed culling, add it
+ CulledLight& outLight = outLights[i];
+ outLight.lightIndex = lightIndices[i];
+ const Light& light = *activeLights.lights[outLight.lightIndex].light;
+ int lightRenderMode = light.GetRenderMode ();
+ float blend = CalculateLightIntensityAtPoint (light, objectCenter);
+ outLight.sortIntensity = blend + kRenderModeSortBias[lightRenderMode];
+ }
+
+ // Sort culled lights by intensity (most intensive lights first)
+ std::sort( outLights.begin(), outLights.end() );
+}
+
+UNITY_VECTOR(kMemRenderer, Light*) LightManager::GetLights(LightType type, int layer)
+{
+ UNITY_VECTOR(kMemRenderer, Light*) lights;
+ layer = 1 << layer;
+ for (LightManager::Lights::iterator i= m_Lights.begin();i != m_Lights.end();i++)
+ {
+ Light* light = &*i;
+ if (!light)
+ continue;
+ if (light->GetType() == type && (light->GetCullingMask() & layer) != 0)
+ lights.push_back(light);
+ }
+
+ return lights;
+}
+
+void LightManager::FindVertexLightsForObject (dynamic_array<UInt8>& dest, const UInt32* lightIndices, UInt32 lightCount, const ActiveLights& activeLights, const VisibleNode& node)
+{
+ DebugAssert (node.renderer);
+
+ dynamic_array<CulledLight> culledLights(kMemTempAlloc);
+ SortLights (culledLights, lightIndices, lightCount, activeLights, node.worldAABB.GetCenter ());
+
+ int resultLightCount = std::min<int> (lightCount, std::min<int>(gGraphicsCaps.maxLights,kMaxSupportedVertexLights));
+
+ // allocate block to hold light count & light pointers
+ size_t resultOffset = dest.size();
+ size_t requiredSize = sizeof(VertexLightsBlock) + resultLightCount * sizeof(Light*);
+ dest.resize_uninitialized(resultOffset + requiredSize);
+ VertexLightsBlock* outBlock = reinterpret_cast<VertexLightsBlock*>(&dest[resultOffset]);
+ const ActiveLight** outLights = reinterpret_cast<const ActiveLight**>(outBlock + 1);
+ outBlock->lightCount = resultLightCount;
+ for (int i = 0; i < resultLightCount; ++i)
+ outLights[i] = &activeLights.lights[culledLights[i].lightIndex];
+}
+
+
+
+static void AddLightToSH (const VisibleNode& node, const Light& source, ForwardLightsBlock* lights, float blend)
+{
+ Vector3f lightDir;
+ if (source.GetType() != kLightDirectional)
+ {
+ lightDir = NormalizeSafe(source.GetWorldPosition() - node.worldAABB.GetCenter());
+ }
+ else
+ {
+ const Transform& lightTransform = source.GetComponent (Transform);
+ lightDir = lightTransform.TransformDirection (Vector3f(0,0,-1));
+ }
+
+ ColorRGBAf color = CalculateLightColorAtPointForSH (source, node.worldAABB, node.localAABB, 1.0f/node.invScale) * (blend * 2.0f);
+
+ float shR[9], shG[9], shB[9];
+ SHEvalDirectionalLight9 (
+ lightDir.x, lightDir.y, lightDir.z,
+ color.r, color.g, color.b,
+ shR, shG, shB);
+ for (int i = 0; i < 9; ++i) {
+ lights->sh[i][0] += shR[i];
+ lights->sh[i][1] += shG[i];
+ lights->sh[i][2] += shB[i];
+ }
+}
+
+
+// Light we want to blend is L1 (with L0 the brighter one before it, and L2 the dimmer one after it).
+// Blend is: (L1-L2)/(L0-L2)
+static inline bool CalculateLightBlend (const CulledLight* lights, int lightsCount, int index, float* outBlend)
+{
+ if (index <= 0 || index >= lightsCount-1)
+ return false;
+
+ float l0 = lights[index-1].sortIntensity;
+ float l1 = lights[index ].sortIntensity;
+ float l2 = lights[index+1].sortIntensity;
+ if (l0 - l2 >= kRenderModeSortBias[Light::kRenderImportant])
+ return false;
+
+ *outBlend = clamp01 ((l1 - l2) / (l0 - l2 + 0.001f));
+ return true;
+}
+
+
+static void CrossBlendForwardLights (
+ CulledLight* culledLights,
+ const ActiveLights& activeLights,
+ int culledLightsCount,
+ int lastAutoAddLightIndex,
+ bool lightmappedObject,
+ dynamic_array<UInt8>& dest,
+ size_t resultOffset,
+ const VisibleNode& node
+ )
+{
+ ForwardLightsBlock* lights = reinterpret_cast<ForwardLightsBlock*>(&dest[resultOffset]);
+ int lastVertexLightIndex = lights->addLightCount + lights->vertexLightCount-1;
+
+ // How much we need to blend the last additive light?
+ lights->lastAddLightBlend = 1.0f;
+ bool blendLastAddLight = CalculateLightBlend (culledLights, culledLightsCount, lastAutoAddLightIndex, &lights->lastAddLightBlend);
+ if (blendLastAddLight && !lightmappedObject)
+ {
+ // For non-lightmapped objects, we need to add oppositely blended
+ // vertex or SH light.
+ UInt32 blendIndex = culledLights[lastAutoAddLightIndex].lightIndex;
+ const Light& blendLight = *activeLights.lights[blendIndex].light;
+ if (blendLight.GetType() != kLightDirectional)
+ {
+ // Non-directional light: insert one vertex light
+ dest.resize_uninitialized(dest.size() + sizeof(Light*));
+ lights = reinterpret_cast<ForwardLightsBlock*>(&dest[resultOffset]); // could result in reallocation; recalculate block pointer
+ const ActiveLight** lightPtrs = reinterpret_cast<const ActiveLight**>(lights + 1); // ActiveLight array begins at end of struct
+ // move all vertex lights
+ for (int i = lights->addLightCount+lights->vertexLightCount-1; i >= lights->addLightCount-1; --i)
+ lightPtrs[i+1] = lightPtrs[i];
+ Assert (lights->vertexLightCount <= kMaxForwardVertexLights);
+ ++lights->vertexLightCount;
+ if (lights->vertexLightCount > kMaxForwardVertexLights)
+ {
+ --lastVertexLightIndex;
+ lights->vertexLightCount = kMaxForwardVertexLights;
+ }
+ }
+ else
+ {
+ // Directional light: add to SH
+ AddLightToSH (node, blendLight, lights, 1.0f-lights->lastAddLightBlend);
+ }
+ }
+
+ // If we have vertex lights, we might want to blend last one
+ if (lights->vertexLightCount > 0)
+ {
+ float vertexBlend;
+ lights->lastVertexLightBlend = 1.0f;
+ bool blendLastVertexLight = CalculateLightBlend (culledLights, culledLightsCount, lastVertexLightIndex, &vertexBlend);
+ if (blendLastVertexLight)
+ lights->lastVertexLightBlend = vertexBlend;
+ }
+}
+
+static size_t CountNonOffscreenVertexLights (const UInt32* lightIndices, UInt32 lightCount, const ActiveLights& activeLights)
+{
+ size_t visibleLightCount = 0;
+ for (visibleLightCount=0;visibleLightCount<lightCount;visibleLightCount++)
+ {
+ if (activeLights.lights[lightIndices[visibleLightCount]].isOffscreenVertexLight)
+ break;
+ }
+
+ for (int j=visibleLightCount;j<lightCount;j++)
+ {
+ DebugAssert(activeLights.lights[lightIndices[j]].isOffscreenVertexLight);
+ }
+
+
+ return visibleLightCount;
+}
+
+void LightManager::FindForwardLightsForObject (dynamic_array<UInt8>& dest, const UInt32* lightIndices, UInt32 lightCount, const ActiveLights& activeLights, const VisibleNode& node, bool lightmappedObject, bool dualLightmapsMode, bool useVertexLights, int maxAutoAddLights, bool disableAddLights, const ColorRGBAf& ambient)
+{
+ DebugAssert (node.renderer);
+
+ // cull and sort lights into temporary memory block
+ Renderer* renderer = static_cast<Renderer*>(node.renderer);
+ UInt32 layerMask = renderer->GetLayerMask();
+ const bool isUsingLightProbes = node.renderer->GetRendererType() != kRendererIntermediate && renderer->GetUseLightProbes() && LightProbes::AreBaked();
+ const bool directLightFromLightProbes = isUsingLightProbes && !dualLightmapsMode;
+
+ dynamic_array<CulledLight> culledLights(kMemTempAlloc);
+
+ // If we don't support vertex lights we can skip rendering any offscreen lights completely (offscreen lights always come after visible lights in the index list)
+ if (!useVertexLights)
+ lightCount = CountNonOffscreenVertexLights (lightIndices, lightCount, activeLights);
+
+
+ SortLights (culledLights, lightIndices, lightCount, activeLights, node.worldAABB.GetCenter ());
+
+ // put ForwardLightsBlock header structure into buffer
+ size_t resultOffset = dest.size();
+ size_t arrayOffset = resultOffset + sizeof(ForwardLightsBlock);
+ dest.resize_uninitialized(arrayOffset);
+ ForwardLightsBlock* lights = reinterpret_cast<ForwardLightsBlock*>(&dest[resultOffset]);
+ lights->addLightCount = 0;
+ lights->vertexLightCount = 0;
+ lights->mainLight = NULL;
+ lights->lastAddLightBlend = 1.0f;
+ lights->lastVertexLightBlend = 1.0f;
+
+ if (useVertexLights)
+ {
+ // if we want vertex lights as result, just take N brightest ones
+ int resultLightCount = std::min<int> (lightCount, std::min<int>(gGraphicsCaps.maxLights,kMaxSupportedVertexLights));
+
+ // set SH to zero (not really used for rendering, but having garbage there would break batches)
+ memset (lights->sh, 0, sizeof(lights->sh));
+
+ // allocate block to hold light pointers
+ dest.resize_uninitialized(arrayOffset + sizeof(ActiveLight*) * resultLightCount);
+ // could result in reallocation; recalculate block pointer
+ lights = reinterpret_cast<ForwardLightsBlock*>(&dest[resultOffset]);
+ const ActiveLight** lightPtrs = reinterpret_cast<const ActiveLight**>(lights + 1);
+ lights->vertexLightCount = resultLightCount;
+ for (int i = 0; i < resultLightCount; ++i)
+ lightPtrs[i] = &activeLights.lights[culledLights[i].lightIndex];
+ }
+ else
+ {
+ // we want main light + SH + additive lights as result
+
+ // Main light if any will be first one in activeLights
+ const ActiveLight* mainLight = NULL;
+ if (activeLights.hasMainLight )
+ mainLight = &activeLights.lights[0];
+
+ // Take globally main light if that fits our layer mask, it does not have a cookie, and it's not an auto light while we're using probes
+ if (mainLight && (mainLight->cullingMask & layerMask) != 0 && !mainLight->hasCookie &&
+ !(directLightFromLightProbes && mainLight->lightmappingForRender == Light::kLightmappingAuto))
+ lights->mainLight = mainLight;
+
+ // put ambient into SH
+ SHEvalAmbientLight(ambient, &lights->sh[0][0]);
+ for (int i = 1; i < 9; ++i) {
+ lights->sh[i][0] = 0.0f;
+ lights->sh[i][1] = 0.0f;
+ lights->sh[i][2] = 0.0f;
+ }
+
+ // go over result lights and place them
+ int lastAutoAddLightIndex = -1;
+ for (int i = 0; i < lightCount; ++i)
+ {
+ UInt32 lightIndex = culledLights[i].lightIndex;
+ const ActiveLight& activeLight = activeLights.lights[lightIndex];
+ const int lightRenderMode = activeLight.lightRenderMode;
+ const int lightmappingMode = activeLight.lightmappingForRender;
+
+ Assert(!activeLight.isOffscreenVertexLight);
+
+ // BasePass for lightmapped objects has no code for run-time lighting
+ // therefore we have to promote RuntimeOnly directional lights to Additive pass
+ // (This bug was fixed in 3.5. Thus we keep behaviour in the webplayer)
+ bool forceAdditive = lightmappedObject && lightmappingMode == Light::kLightmappingRealtimeOnly;
+ forceAdditive &= IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_5_a1);
+
+ // If this is already set as main light: skip it
+ if (lights->mainLight && lightIndex == 0 && !forceAdditive)
+ {
+ // nothing
+ }
+ // If we don't have main light yet and this could be it: do it
+ else if (lights->mainLight == NULL && activeLight.lightType == kLightDirectional && lightRenderMode != Light::kRenderNotImportant && !activeLight.hasCookie && !forceAdditive)
+ {
+ lights->mainLight = &activeLight;
+ }
+ // If it's an important light, add it to additive lights
+ else if (((lightRenderMode == Light::kRenderImportant) || (lightRenderMode!=Light::kRenderNotImportant && lights->addLightCount < maxAutoAddLights)) && !disableAddLights)
+ {
+ // now that we know it will not be rendered as a vertex light, we can check if it's actually visible;
+ // can't do that for vertex lights, as they influence object past the range
+ size_t curOffset = dest.size();
+ dest.resize_uninitialized(curOffset + sizeof(ActiveLight*));
+ *reinterpret_cast<const ActiveLight**>(&dest[curOffset]) = &activeLight;
+ // could result in reallocation; recalculate block pointer
+ lights = reinterpret_cast<ForwardLightsBlock*>(&dest[resultOffset]);
+ ++lights->addLightCount;
+ if (lights->addLightCount == maxAutoAddLights && lightRenderMode != Light::kRenderImportant)
+ lastAutoAddLightIndex = i;
+ }
+ // All Vertex/SH lights for non-lightmapped objects
+ else if (!lightmappedObject)
+ {
+ // Add some non-directional lights as vertex lights
+ if (activeLight.lightType != kLightDirectional && lights->vertexLightCount < kMaxForwardVertexLights)
+ {
+ size_t curOffset = dest.size();
+ dest.resize_uninitialized(curOffset + sizeof(Light*));
+ *reinterpret_cast<const ActiveLight**>(&dest[curOffset]) = &activeLight;
+ // could result in reallocation; recalculate block pointer
+ lights = reinterpret_cast<ForwardLightsBlock*>(&dest[resultOffset]);
+ ++lights->vertexLightCount;
+ }
+ // Otherwise, add light to SH
+ else
+ {
+ AddLightToSH (node, *activeLight.light, lights, 1.0f);
+ }
+ }
+ }
+
+ // Blend light transitions: full to vertex lit; and vertex lit to SH.
+ CrossBlendForwardLights (culledLights.data(), activeLights, lightCount, lastAutoAddLightIndex, lightmappedObject, dest, resultOffset, node);
+
+ if (isUsingLightProbes)
+ {
+ float coefficients[kLightProbeCoefficientCount];
+ GetLightProbes()->GetInterpolatedLightProbe(renderer->GetLightProbeInterpolationPosition(node.worldAABB), renderer, &coefficients[0]);
+ for (int i = 0; i < kLightProbeCoefficientCount; i++)
+ {
+ lights->sh[0][i] += coefficients[i];
+ }
+ }
+ }
+}