It comes into MovableObject::queryLights, and it updates there every frame (because I force it through calling mBillboardSet->_notifyMoved() every frame).
Then, in SceneManager::_populateLightList it seems to check against how many shadow textures there are... I am using 2 shadow textures in my current setup.
But that has nothing to do with non-casting lights, so I find that kind of strange.
It does not check if the light in question is actually using shadows, but it just assumes that it does.
So in here it gets the 2 first lights in the list, which means my directional light (which is always first) and then a random light in the scene.
That makes it take an incorrect light because of that, and if I had 4 shadow textures active it would take 3 incorrect lights without sorting them (since it then assumes I am using 4 lights that is using shadows).
What it should do it probably to check if the first lights are actually casting shadows, otherwise it is pretty bad to assume that they are (as it is currently in the code).
But still... That does not explain why it gets fixed when I move a random light.
But SceneManager::findLightsAffectingFrustum explains it.
Because when I add all lights to the scene, it might be in a random order. But then when I move a light (any light), SceneManager::findLightsAffectingFrustum sees a change and sorts the lights based on the camera position. And incidentally in this case, it means the object I look at gets the right lights on it.
So in short, the whole bug could probably be solved by user code that sets the number of shadow textures to 0 if there are no lights that casts shadows in the scene, and then to update it each time it gets a new one and then needs to remake shaders/GPU params if needed. Or it could instead just be solved to do that automatically in SceneManager::-populateLightList to something like this (not tested):
Code: Select all
void SceneManager::_populateLightList(const Vector3& position, Real radius,
LightList& destList, uint32 lightMask)
{
// Really basic trawl of the lights, then sort
// Subclasses could do something smarter
// Pick up the lights that affecting frustum only, which should has been
// cached, so better than take all lights in the scene into account.
const LightList& candidateLights = _getLightsAffectingFrustum();
// Pre-allocate memory
destList.clear();
destList.reserve(candidateLights.size());
size_t lightIndex = 0;
size_t numShadowTextures = isShadowTechniqueTextureBased() ? getShadowTextureConfigList().size() : 0;
for (Light* lt : candidateLights)
{
// check whether or not this light is suppose to be taken into consideration for the current light mask set for this operation
if(!(lt->getLightMask() & lightMask))
continue; //skip this light
// Calc squared distance
lt->_calcTempSquareDist(position);
// only add in-range lights, but ensure texture shadow casters are there
// note: in this case the first numShadowTextures canditate lights are casters
if ((lt->getCastShadows() && lightIndex < numShadowTextures) || lt->isInLightRange(Sphere(position, radius)))
{
destList.push_back(lt);
}
lightIndex++;
}
auto start = destList.begin();
// if we're using texture shadows, we actually want to use
// the first few lights unchanged from the frustum list, matching the
// texture shadows that were generated
// Thus we only allow object-relative sorting on the remainder of the list
std::advance(start, std::min(numShadowTextures, destList.size()));
// Sort (stable to guarantee ordering on directional lights)
std::stable_sort(start, destList.end(), lightLess());
// Now assign indexes in the list so they can be examined if needed
lightIndex = 0;
for (auto lt : destList)
{
lt->_notifyIndexInFrame(lightIndex++);
}
}
What do you think of that solution?
I basically only added "lt->getCastShadows()" and moved the "lightIndex++" to later.