public void ApplyDrawParameters(RenderDrawContext context, ParameterCollection parameters, FastListStruct <LightDynamicEntry> currentLights, ref BoundingBoxExt boundingBox) { var boundingBoxCasted = (BoundingBox)boundingBox; int lightIndex = 0; for (int i = 0; i < currentLights.Count; ++i) { LightDynamicEntry lightEntry = currentLights[i]; LightComponent lightComponent = lightEntry.Light; if (lightComponent.BoundingBox.Intersects(ref boundingBoxCasted)) { var spotLight = (LightSpot)lightComponent.Type; /* * // TODO: Just save the shaderdata struct directly within "LightDynamicEntry"? * var singleLightData = (LightSpotTextureProjectionShaderData)lightEntry.ShadowMapTexture.ShaderData; // TODO: This must not depend on the shadow map texture! * * worldToTextureUV[lightIndex] = singleLightData.WorldToTextureUV; * projectionTextureMipMapLevels[lightIndex] = singleLightData.ProjectiveTextureMipMapLevel; * projectiveTexture = singleLightData.ProjectiveTexture; */ // TODO: Move this to "Collect()" and use "LightSpotTextureProjectionShaderData", but IDK how! worldToTextureUV[lightIndex] = ComputeWorldToTextureUVMatrix(lightComponent); projectorPlaneMatrices[lightIndex] = ComputeProjectorPlaneMatrix(lightComponent); // We use the maximum number of mips instead of the actual number, // so things like video textures behave more consistently when changing the number of mip maps to generate. int maxMipMapCount = Texture.CountMips(lightParameters.ProjectionTexture.Width, lightParameters.ProjectionTexture.Height); float projectiveTextureMipMapLevel = (float)(maxMipMapCount - 1) * spotLight.MipMapScale; // "- 1" because the lowest mip level is 0, not 1. projectionTextureMipMapLevels[lightIndex] = projectiveTextureMipMapLevel; transitionAreas[lightIndex] = Math.Max(spotLight.TransitionArea, 0.001f); // Keep the value just above zero. This is to prevent some issues with the "smoothstep()" function on OpenGL and OpenGL ES. ++lightIndex; } } // TODO: Why is this set if it's already in the collection? // TODO: Does this get set once per group or something? parameters.Set(projectiveTextureKey, lightParameters.ProjectionTexture); parameters.Set(uvScale, lightParameters.UVScale); parameters.Set(uvOffset, lightParameters.UVOffset); parameters.Set(worldToProjectiveTextureUVsKey, worldToTextureUV); parameters.Set(projectorPlaneMatricesKey, projectorPlaneMatrices); parameters.Set(projectionTextureMipMapLevelsKey, projectionTextureMipMapLevels); parameters.Set(transitionAreasKey, transitionAreas); }
public override void ProcessLights(ProcessLightsParameters parameters) { if (parameters.LightCollection.Count == 0) { return; } // Check if we have a fallback renderer next in the chain, in case we don't need shadows bool hasNextRenderer = parameters.RendererIndex < (parameters.Renderers.Length - 1); var currentGroupParameters = SpotLightGroupParameters.Null; // Start by filtering/sorting what can be processed shadowComparer.ShadowMapTexturesPerLight = parameters.ShadowMapTexturesPerLight; shadowComparer.Lights = parameters.LightCollection; parameters.LightIndices.Sort(0, parameters.LightIndices.Count, shadowComparer); // Loop over the number of lights + 1 where the last iteration will always flush the last batch of lights for (int j = 0; j < parameters.LightIndices.Count + 1;) { // TODO: Eventually move this loop to a separate function that returns a structure. // These variables will contain the relevant parameters of the next usable light: var nextGroupParameters = SpotLightGroupParameters.Null; LightShadowMapTexture nextShadowTexture = null; RenderLight nextLight = null; // Find the next light whose attributes aren't null: if (j < parameters.LightIndices.Count) { nextLight = parameters.LightCollection[parameters.LightIndices[j]]; if (nextLight.Type is LightSpot spotLight) { if (spotLight.ProjectiveTexture != null) // TODO: Remove this branch?! { nextGroupParameters.SpotParameters.ProjectionTexture = spotLight.ProjectiveTexture; nextGroupParameters.SpotParameters.FlipMode = spotLight.FlipMode; nextGroupParameters.SpotParameters.UVScale = spotLight.UVScale; nextGroupParameters.SpotParameters.UVOffset = spotLight.UVOffset; } } if (parameters.ShadowMapRenderer != null && parameters.ShadowMapTexturesPerLight.TryGetValue(nextLight, out nextShadowTexture) && nextShadowTexture.Atlas != null) // atlas could not be allocated? treat it as a non-shadowed texture { nextGroupParameters.ShadowType = nextShadowTexture.ShadowType; nextGroupParameters.ShadowRenderer = nextShadowTexture.Renderer; } } // Flush current group // If we detect that the previous light's attributes don't match the next one's, create a new group (or add to an existing one that has the same attributes): if (j == parameters.LightIndices.Count || !currentGroupParameters.Equals(ref nextGroupParameters)) { if (processedLights.Count > 0) { ITextureProjectionRenderer currentTextureProjectionRenderer = FindOrCreateTextureProjectionRenderer(currentGroupParameters); var lightGroupKey = new LightGroupKey(currentGroupParameters.ShadowRenderer, currentGroupParameters.ShadowType, currentTextureProjectionRenderer); LightShaderGroupDynamic lightShaderGroup = FindOrCreateLightShaderGroup(lightGroupKey, parameters); // Add view and lights to the current group: var allowedLightCount = lightShaderGroup.AddView(parameters.ViewIndex, parameters.View, processedLights.Count); for (int i = 0; i < allowedLightCount; ++i) { LightDynamicEntry light = processedLights[i]; lightShaderGroup.AddLight(light.Light, light.ShadowMapTexture); } // TODO: assign extra lights to non-shadow rendering if possible //for (int i = lightCount; i < processedLights.Count; ++i) // XXX.AddLight(processedLights[i], null); // Add the current light shader group to the collection if it hasn't already been added: var lightShaderGroupEntry = new LightShaderGroupEntry <LightGroupKey>(lightGroupKey, lightShaderGroup); if (!lightShaderGroups.Contains(lightShaderGroupEntry)) { lightShaderGroups.Add(lightShaderGroupEntry); } processedLights.Clear(); } // Start next group currentGroupParameters = nextGroupParameters; } if (j < parameters.LightIndices.Count) { // Do we need to process non shadowing lights or defer it to something else? if (nextShadowTexture == null && hasNextRenderer) { // Break out so the remaining lights can be handled by the next renderer break; } parameters.LightIndices.RemoveAt(j); processedLights.Add(new LightDynamicEntry(nextLight, nextShadowTexture)); } else { j++; } } processedLights.Clear(); }