void VPostProcessTranslucencies::Execute()
{
  INSERT_PERF_MARKER_SCOPE("VPostProcessTranslucencies");

  VisRenderContext_cl *pContext = VisRenderContext_cl::GetCurrentContext();
  IVisVisibilityCollector_cl *pVisCollector = pContext->GetVisibilityCollector();
  VASSERT(pVisCollector != NULL);

  const VisEntityCollection_cl *pVisibleForeGroundEntities = pVisCollector->GetVisibleForeGroundEntities();

  m_VisibilityObjectCollector.HandleVisibleVisibilityObjects();

#ifndef _VISION_MOBILE
  RenderingOptimizationHelpers_cl::SetShaderPreference(96);
#endif

  // Get a pointer to the collection of visible mesh buffer objects
  const VisMeshBufferObjectCollection_cl *pVisibleMeshBuffer = &m_VisibilityObjectCollector.GetMeshBufferObjectCollection();

  // Get a pointer to the collection of visible particle groups
  const VisParticleGroupCollection_cl *pVisibleParticleGroups = &m_VisibilityObjectCollector.GetParticleGroupCollection();

  // Mask out entities which are "always in foreground"
  MaskOutForegroundEntities(*pVisibleForeGroundEntities);

  if (pVisCollector->GetInterleavedTranslucencySorter() == NULL)
  {
    // --- Traditional transparency sorting (default)
    const VisStaticGeometryInstanceCollection_cl *pVisibleTransparentGeoInstances = pVisCollector->GetVisibleStaticGeometryInstancesForPass(VPT_TransparentPass);
    const VisEntityCollection_cl *pVisibleEntities = pVisCollector->GetVisibleEntitiesForPass(VPT_TransparentPass);

    VisionRenderLoop_cl::RenderHook(*pVisibleMeshBuffer, pVisibleParticleGroups, VRH_PRE_TRANSPARENT_PASS_GEOMETRY, true);

    // render transparent pass surface shaders on translucent static geometry instances
    Vision::RenderLoopHelper.RenderStaticGeometrySurfaceShaders(*pVisibleTransparentGeoInstances, VPT_TransparentPass);

    VisionRenderLoop_cl::RenderHook(*pVisibleMeshBuffer, pVisibleParticleGroups, VRH_PRE_TRANSPARENT_PASS_ENTITIES, true);

    // Render transparent pass shaders on entities
    DrawEntitiesShaders(*pVisibleEntities, VPT_TransparentPass);

    VisionRenderLoop_cl::RenderHook(*pVisibleMeshBuffer, pVisibleParticleGroups, VRH_POST_TRANSPARENT_PASS_GEOMETRY, true);

    VisionRenderLoop_cl::RenderHook(*pVisibleMeshBuffer, pVisibleParticleGroups, VRH_DECALS, true);

    RenderParticles(pVisibleMeshBuffer, pVisibleParticleGroups);
  }
  else
  {
    // --- Interleaved transparency sorting
    pVisCollector->GetInterleavedTranslucencySorter()->OnRender(pVisCollector, true);
  }

  // Render visible foreground entities (see DrawForegroundEntities)
  DrawTransparentForegroundEntities(*pVisibleForeGroundEntities);

  // Coronas and flares will be still rendered after the other interleaved sorted objects were rendered (lensflare and coronas don't must be always rendered "on top") 
  VisionRenderLoop_cl::RenderHook(*pVisibleMeshBuffer, pVisibleParticleGroups, VRH_CORONAS_AND_FLARES, true);
}
void VMobileForwardRenderLoop::OnDoRenderLoop(void *pUserData)
{
  INSERT_PERF_MARKER_SCOPE("VMobileForwardRenderLoop::OnDoRenderLoop");

  m_iFrameCounter++; // just for arbitrary custom purposes

#ifdef WIN32
  // vForge specific:
  if (Vision::RenderLoopHelper.GetReplacementRenderLoop())
  {
    // render with this render-loop instead
    Vision::RenderLoopHelper.GetReplacementRenderLoop()->OnDoRenderLoop(pUserData);
    return;
  }
#endif

  m_pShaderProvider = Vision::GetApplication()->GetShaderProvider();
  VASSERT(m_pShaderProvider);
  m_pShaderProvider->ResetCache();

  VisRenderContext_cl *pContext = VisRenderContext_cl::GetCurrentContext();
  IVisVisibilityCollector_cl *pVisCollector = pContext->GetVisibilityCollector();
  if (pVisCollector==NULL)
    return; 

  const int iRenderFlags = pContext->GetRenderFlags();
  m_pCameraFrustum = pVisCollector->GetBaseFrustum(); 

  const VisStaticGeometryInstanceCollection_cl *pVisibleGeoInstancesPrimaryOpaquePass = pVisCollector->GetVisibleStaticGeometryInstancesForPass(VPT_PrimaryOpaquePass);
  const VisStaticGeometryInstanceCollection_cl *pVisibleGeoInstancesSecondaryOpaquePass = pVisCollector->GetVisibleStaticGeometryInstancesForPass(VPT_SecondaryOpaquePass);
  const VisStaticGeometryInstanceCollection_cl *pVisibleGeoInstancesTransparentPass = pVisCollector->GetVisibleStaticGeometryInstancesForPass(VPT_TransparentPass);
  const VisEntityCollection_cl *pVisibleEntitiesPrimaryOpaquePass = pVisCollector->GetVisibleEntitiesForPass(VPT_PrimaryOpaquePass);
  const VisEntityCollection_cl *pVisibleEntitiesSecondaryOpaquePass = pVisCollector->GetVisibleEntitiesForPass(VPT_SecondaryOpaquePass);
  const VisEntityCollection_cl *pVisibleEntitiesTransparentPass = pVisCollector->GetVisibleEntitiesForPass(VPT_TransparentPass);
  const VisEntityCollection_cl *pVisibleForeGroundEntities = pVisCollector->GetVisibleForeGroundEntities();
  HandleVisibleVisibilityObjects();

  // Clear the screen
  if ((iRenderFlags&VIS_RENDERCONTEXT_FLAG_NO_CLEARSCREEN)==0)
  {
    const VFogParameters &fog = Vision::World.GetFogParameters();
    VColorRef clearColor = fog.depthMode != VFogParameters::Off ? fog.iDepthColor : Vision::Renderer.GetDefaultClearColor();
    Vision::RenderLoopHelper.ClearScreen(VisRenderLoopHelper_cl::VCTF_All, clearColor);
  }

  m_bHasRenderHookCallbacks = m_bTriggerCallbacks && Vision::Callbacks.OnRenderHook.HasCallbacks();

  // Get a pointer to the collection of visible mesh buffer objects
  const VisMeshBufferObjectCollection_cl *pVisibleMeshBuffer = &m_VisibilityObjectCollector.GetMeshBufferObjectCollection();

  // Get a pointer to the collection of visible particle groups
  const VisParticleGroupCollection_cl *pVisibleParticleGroups = &m_VisibilityObjectCollector.GetParticleGroupCollection();

  // Determine which lights have to rendered in the current frame
  DetermineRelevantLights();

  // Render all mesh buffer objects with the render order flag "VRH_PRE_RENDERING".
  RenderHook(*pVisibleMeshBuffer, pVisibleParticleGroups, VRH_PRE_RENDERING, m_bTriggerCallbacks);

  // Render all mesh buffer objects with the render order flag "VRH_PRE_PRIMARY_OPAQUE_PASS_GEOMETRY".
  RenderHook(*pVisibleMeshBuffer, pVisibleParticleGroups, VRH_PRE_PRIMARY_OPAQUE_PASS_GEOMETRY, m_bTriggerCallbacks);

  // Reset tags
  VisStaticGeometryInstance_cl::ResetTags();
  VisBaseEntity_cl::ResetTags();
  
  // Clear temporary collections for geometry that is lit by base pass light, but rendered in additive lighting pass
  m_AdditiveLitGeoInstanceCollection.Clear();
  m_AdditiveLitEntityCollection.Clear();

  // Prepare the initial lighting pass (one light collapsed with base lighting contribution)
  bool bUsesLightClippingVolume = false;
  IVShadowMapComponent *pShadowMap = PrepareLightingPass(m_pBasePassLight, true, bUsesLightClippingVolume);

  // Render lit geometry before actual base pass, whereby the geometry which has been rendered here will be tagged, in order
  // to avoid re-rendering later on. We first render static meshes lit base the base pass light, then static meshes not lit by the base pass light,
  // and then entities (with/without base pass light, respectively).
  {
    RenderLitGeometry(m_pBasePassLight, pShadowMap, true, bUsesLightClippingVolume, false, true);

    // Render all primary opaque pass surface shaders on opaque world geometry
    Vision::RenderLoopHelper.RenderStaticGeometrySurfaceShaders(*pVisibleGeoInstancesPrimaryOpaquePass, VPT_PrimaryOpaquePass, VTF_IGNORE_TAGGED_ENTRIES); 

    // Render all mesh buffer objects with the render order flag "VRH_PRE_PRIMARY_OPAQUE_PASS_ENTITIES".
    RenderHook(*pVisibleMeshBuffer, pVisibleParticleGroups, VRH_PRE_PRIMARY_OPAQUE_PASS_ENTITIES, m_bTriggerCallbacks);

    RenderLitGeometry(m_pBasePassLight, pShadowMap, true, bUsesLightClippingVolume, true, false);

    // Render all primary opaque pass shaders on entities (see "DrawEntitiesShaders")
    DrawEntitiesShaders(*pVisibleEntitiesPrimaryOpaquePass, VPT_PrimaryOpaquePass, VTF_IGNORE_TAGGED_ENTRIES);
  }

  // Finalize the initial pass
  FinalizeLightingPass(m_pBasePassLight, bUsesLightClippingVolume);

  RenderHook(*pVisibleMeshBuffer, pVisibleParticleGroups, VRH_PRE_SECONDARY_OPAQUE_PASS_GEOMETRY, m_bTriggerCallbacks);

  // Render static geometry instances for secondary opaque pass
  Vision::RenderLoopHelper.RenderStaticGeometrySurfaceShaders(*pVisibleGeoInstancesSecondaryOpaquePass, VPT_SecondaryOpaquePass, VTF_IGNORE_TAGGED_ENTRIES);
  
  RenderHook(*pVisibleMeshBuffer, pVisibleParticleGroups, VRH_PRE_SECONDARY_OPAQUE_PASS_ENTITIES, m_bTriggerCallbacks);

  // Render entities for secondary opaque pass
  DrawEntitiesShaders(*pVisibleEntitiesSecondaryOpaquePass, VPT_SecondaryOpaquePass, VTF_IGNORE_TAGGED_ENTRIES);

  // Start the hardware occlusion query. Note that this function always has to be called in render loops.
  // Also, the position of this call in the OnDoRenderLoop is important: The zBuffer contents at this stage of rendering will
  // act as occluders in the hardware occlusion queries.
  Vision::RenderLoopHelper.PerformHardwareOcclusionQuery();

  // Render sky
  Vision::RenderLoopHelper.RenderSky();

  // Render all mesh buffer objects with the render order flag "VRH_PRE_OCCLUSION_TESTS".
  RenderHook(*pVisibleMeshBuffer, pVisibleParticleGroups, VRH_PRE_OCCLUSION_TESTS, m_bTriggerCallbacks);

  Vision::RenderLoopHelper.PerformHardwarePixelCounterQuery();

  // Render all mesh buffer objects with the render order flag "VRH_POST_OCCLUSION_TESTS".
  RenderHook(*pVisibleMeshBuffer, pVisibleParticleGroups, VRH_POST_OCCLUSION_TESTS, m_bTriggerCallbacks);

  // Draw dynamic light 
  DrawDynamicLight();

  // Render all mesh buffer objects with the render order flag "VRH_PRE_TRANSPARENT_PASS_GEOMETRY".
  RenderHook(*pVisibleMeshBuffer, pVisibleParticleGroups, VRH_PRE_TRANSPARENT_PASS_GEOMETRY, m_bTriggerCallbacks); 
  
  // Render transparent pass surface shaders on translucent lit world primitives
  Vision::RenderLoopHelper.RenderStaticGeometrySurfaceShaders(*pVisibleGeoInstancesTransparentPass, VPT_TransparentPass, VTF_IGNORE_TAGGED_ENTRIES);

  RenderHook(*pVisibleMeshBuffer, pVisibleParticleGroups, VRH_PRE_TRANSPARENT_PASS_ENTITIES, m_bTriggerCallbacks);

  // Render transparent pass shaders on entities
  DrawEntitiesShaders(*pVisibleEntitiesTransparentPass, VPT_TransparentPass, VTF_IGNORE_TAGGED_ENTRIES);

  // Render all mesh buffer objects with the render order flag "VRH_POST_TRANSPARENT_PASS_GEOMETRY".
  RenderHook(*pVisibleMeshBuffer, pVisibleParticleGroups, VRH_POST_TRANSPARENT_PASS_GEOMETRY, m_bTriggerCallbacks);

  // Render all mesh buffer objects and particle systems with the render order flag "VRH_DECALS".
  RenderHook(*pVisibleMeshBuffer, pVisibleParticleGroups, VRH_DECALS, m_bTriggerCallbacks);

  // Render all mesh buffer objects and particle systems with the render order flag "VRH_PARTICLES".
  RenderHook(*pVisibleMeshBuffer, pVisibleParticleGroups, VRH_PARTICLES, m_bTriggerCallbacks);

  // Render all mesh buffer objects with the render order flag "VRH_ADDITIVE_PARTICLES"
  RenderHook(*pVisibleMeshBuffer, pVisibleParticleGroups, VRH_ADDITIVE_PARTICLES, m_bTriggerCallbacks);

  RenderHook(*pVisibleMeshBuffer, pVisibleParticleGroups, VRH_TRANSLUCENT_VOLUMES, m_bTriggerCallbacks);

  // Render visible foreground entities (see DrawForegroundEntities)
  DrawForegroundEntities(*pVisibleForeGroundEntities);

  // Render all mesh buffer objects with the render order flag "VRH_CORONAS_AND_FLARES"
  RenderHook(*pVisibleMeshBuffer, pVisibleParticleGroups, VRH_CORONAS_AND_FLARES, m_bTriggerCallbacks);

  m_pShaderProvider = NULL;
}
Пример #3
0
void VCoronaManager::UpdateCoronas(int iCoronaUpdateFlags)
{
#ifdef SUPPORTS_CORONAS

  VisRenderContext_cl* pContext = VisRenderContext_cl::GetCurrentContext();

  if ((iCoronaUpdateFlags & VCUF_USE_OC_CONTEXT) > 0)
  {
    // Determine relevant render context and visibility collector
    IVisVisibilityCollector_cl *pVisCollector = pContext->GetVisibilityCollector();
    if (!pVisCollector)
      return;
    VisRenderContext_cl *pOQContext = pVisCollector->GetOcclusionQueryRenderContext();
    if (pOQContext != NULL)
      pContext = pOQContext;
  }

  if (pContext == NULL)
    return;

  if ((pContext->GetRenderFlags() & VIS_RENDERCONTEXT_FLAG_USE_PIXELCOUNTER) == 0)
    return;

  if ((pContext->GetRenderFlags() & VIS_RENDERCONTEXT_FLAG_RENDER_CORONAS) == 0)
    return;

  // Get bitmask for this context.
  unsigned int iRenderFilterMask = pContext->GetRenderFilterMask();

  // get the collection of visible lights.
  IVisVisibilityCollector_cl* pVisCollector = VisRenderContext_cl::GetCurrentContext()->GetVisibilityCollector();
  if (pVisCollector == NULL)
    return;

  VISION_PROFILE_FUNCTION(PROFILING_CORONA_UPDATE);

  // Get multi-sampling mode
  unsigned int iTexelsPerPixel = 1;
  VTextureObject* pDepthTex = pContext->GetDepthStencilTarget();
  if(pDepthTex == NULL)
  {
    // If no depth stencil target is available, we might work without a renderer node and we're in the main context
    if(Vision::Renderer.GetCurrentRendererNode() == NULL && pContext == VisRenderContext_cl::GetMainRenderContext())
    {
      // In this case get the multi-sampling type from the video config as it's used to set the actual backbuffer settings
      // where the main context will render to
      iTexelsPerPixel = hkvMath::Max(1, 1 << ((int)Vision::Video.GetCurrentConfig()->m_eMultiSample));     
    }  
  }
  else if (pDepthTex->GetTextureType() == VTextureLoader::Texture2D)
  {
    iTexelsPerPixel = hkvMath::Max(1u, ((VisRenderableTexture_cl*)pDepthTex)->GetConfig()->m_iMultiSampling);
  }
  
  const VisLightSrcCollection_cl* pVisibleLights = pVisCollector->GetVisibleLights();
  int iCandidates = 0;
  if (pVisibleLights != NULL)
    iCandidates = pVisibleLights->GetNumEntries();

  // Ensure size of coronas state structure.
  int iContextIndex = pContext->GetNumber();
  if (iContextIndex + 1 > m_State.GetSize())
    m_State.SetSize(iContextIndex + 1, -1);
  VCoronaRenderContextState& state = m_State[iContextIndex];
  int iCapacity = m_Instances.GetCapacity();
  state.EnsureSize(iCapacity);

  // Add visible lights with a lens flare component to the candidate list for this frame
  if ((iCoronaUpdateFlags & VCUF_ADD) > 0)
  {
    for (int iCandidate = 0; iCandidate < iCandidates; ++iCandidate)
    {
      VisLightSource_cl* pLight = pVisibleLights->GetEntry(iCandidate);
      if (pLight)
      {
        VCoronaComponent *pComponent = pLight->Components().GetComponentOfBaseType<VCoronaComponent>();
        if (pComponent != NULL && pComponent->IsEnabled() && !state.IsBitSet(pComponent->m_iIndex))
        {
          // The component is not in m_Candidates yet, so we check whether it is a valid candidate
          bool bIsLightOnScreen = pComponent->IsValidCandidate(pContext);

          if (bIsLightOnScreen)
          {
            state.SetBit(pComponent->m_iIndex);
            pContext->SetPixelCounterResult(pComponent->m_CoronaPixelCounter.GetNumber(), 0);
            state.m_Candidates.Append(pComponent);
          }
        }
      }
    }
  }
  

  // Forces the retrieval all pending queries.
  pContext->FetchPixelCounterTestResults( (iCoronaUpdateFlags & VCUF_FORCE_FETCH) > 0 );
   
  
  // Retrieve Queries and update status of lens flares
  if ((iCoronaUpdateFlags & VCUF_UPDATE) > 0)
  {
    for (int i=0; i < state.m_Candidates.GetSize(); ++i)
    {
      VCoronaCandidate& coronaCandidate = state.m_Candidates.ElementAt(i);
      VCoronaComponent* pCorona = coronaCandidate.m_pCorona;
      if (!pCorona || !pCorona->IsEnabled())
        continue;

      if (pCorona->GetOwner())
      {
        // Retrieve occlusion results of the last query
        unsigned int iElementIndex = pCorona->m_CoronaPixelCounter.GetNumber();
        bool bRes = !pContext->IsPixelCounterQueryInProgress(iElementIndex);

        // Reschedule query if the old on could be retrieved or if a teleport forces us to re-query everything.
        if (bRes | ((iCoronaUpdateFlags & VCUF_FORCE_SCHEDULE) > 0) )
          pContext->SchedulePixelCounterTest(iElementIndex);

        unsigned int iDrawnPixels = pContext->GetPixelCounterResult(iElementIndex) / iTexelsPerPixel;

        float fVisibility = (float)iDrawnPixels / ((int)pCorona->QueryRadius * (int)pCorona->QueryRadius * 4);

        // ATI fix for random insanely high return values.
        if (iDrawnPixels > ((unsigned int)pCorona->QueryRadius * 2 + 1) * ((unsigned int)pCorona->QueryRadius * 2 + 1))
        {
          fVisibility = coronaCandidate.m_fLastVisibilityQuery;
        }

        if ((iCoronaUpdateFlags & VCUF_FORCE_FETCH) > 0)
        {
          // Force lens flare visibility to the current query value.
          coronaCandidate.m_fCurrentVisibility = fVisibility;
          coronaCandidate.m_fLastVisibilityQuery = fVisibility;
          pCorona->UpdateVisibility(coronaCandidate.m_fLastVisibilityQuery, coronaCandidate.m_fCurrentVisibility);
        }
        else if (!m_bTeleportedLastFrame)
        {
          coronaCandidate.m_fLastVisibilityQuery = fVisibility;
          pCorona->UpdateVisibility(coronaCandidate.m_fLastVisibilityQuery, coronaCandidate.m_fCurrentVisibility);
        }
        else
        {
          // if we were teleported, the last frame's query results must be invalidated
          coronaCandidate.m_fCurrentVisibility = 0.0f;
          coronaCandidate.m_fLastVisibilityQuery = 0.0f;
        }
      }
    }
  }
  
  // Removes coronas that are outside the frustum and no longer visible.
  if ((iCoronaUpdateFlags & VCUF_REMOVE) > 0)
  {
    for (int i=0; i < state.m_Candidates.GetSize();)
    {
      VCoronaCandidate& coronaCandidate = state.m_Candidates.ElementAt(i);
      VCoronaComponent* pCorona = coronaCandidate.m_pCorona;

      unsigned int iElementIndex = pCorona->m_CoronaPixelCounter.GetNumber();
      // If the visibility reached zero and the corona is no longer potentially visible it is removed from the list
      if (!pCorona->IsEnabled() || !pCorona->GetOwner()
        || (pCorona->GetVisibleBitmask() & iRenderFilterMask) == 0
        || (((VisLightSource_cl*)pCorona->GetOwner())->GetVisibleBitmask() & iRenderFilterMask) == 0
        || ( coronaCandidate.m_fCurrentVisibility == 0.0f && !pCorona->IsValidCandidate(pContext) ) )
      {
        state.RemoveBit(pCorona->m_iIndex);
        state.m_Candidates.SetAt(i, state.m_Candidates.GetAt(state.m_Candidates.GetSize()-1) );
        state.m_Candidates.RemoveAt(state.m_Candidates.GetSize() -1);
        // Reset cache to zero, so we don't see the lens flare once it enters the frustum again.
        pContext->SetPixelCounterResult(iElementIndex, 0);
      }
      else
      {
        ++i;
      }
    }
  }

#endif
}
Пример #4
0
void VCoronaManager::RenderAllVisibleCoronas()
{
#ifdef SUPPORTS_CORONAS
  VisRenderContext_cl* pContext = VisRenderContext_cl::GetCurrentContext();

  // Determine relevant render context and visibility collector
  IVisVisibilityCollector_cl *pVisCollector = pContext->GetVisibilityCollector();
  if (!pVisCollector)
    return;
  VisRenderContext_cl *pOQContext = pVisCollector->GetOcclusionQueryRenderContext();
  if (pOQContext != NULL)
    pContext = pOQContext;


  if ((pContext->GetRenderFlags() & VIS_RENDERCONTEXT_FLAG_USE_PIXELCOUNTER) == 0)
    return;

  if ((pContext->GetRenderFlags() & VIS_RENDERCONTEXT_FLAG_RENDER_CORONAS) == 0)
    return;

  INSERT_PERF_MARKER_SCOPE("VCoronaManager::RenderAllVisibleCoronas");
  VISION_PROFILE_FUNCTION(PROFILING_CORONA_RENDER);

  // Force for the queries to finish so they are available in this frame.
  if (m_bTeleportedLastFrame && m_bForceQueryOnTeleport)
  {
    UpdateCoronas(VCUF_UPDATE | VCUF_FORCE_FETCH | VCUF_USE_OC_CONTEXT);
  }

  // Ensure size of corona state structure.
  int iContextIndex = pContext->GetNumber();
  if (iContextIndex + 1 > m_State.GetSize())
    m_State.SetSize(iContextIndex + 1, -1);
  VCoronaRenderContextState& state = m_State[iContextIndex];
  int iCapacity = m_Instances.GetCapacity();
  state.EnsureSize(iCapacity);

  const int iCoronasToRender = state.m_Candidates.GetSize();

  // Sort candidates by texture?
  
  VTextureObject* pTexture = NULL;

  // Render all corona components
  Vision::RenderLoopHelper.BeginMeshRendering();
  Vision::RenderLoopHelper.AddMeshStreams(m_spBillboardMesh,VERTEX_STREAM_POSITION);

  for (int i=0; i < iCoronasToRender; ++i)
  {
    VCoronaCandidate& coronaCandidate = state.m_Candidates.ElementAt(i);
    if (coronaCandidate.m_fCurrentVisibility > 0.0f)
    {
      RenderCorona (coronaCandidate, pTexture);
    }
  }

  Vision::RenderLoopHelper.EndMeshRendering();

  m_bTeleportedLastFrame = (pContext->GetCamera()->GetLastTeleported() >= pContext->GetLastRenderedFrame());
#endif
}
void VFakeSpecularGenerator::OnDoRenderLoop(void *pUserData)
{
  VFrustumMeshHelper::UpdateMeshBuffer(m_spMeshBuffer, Vision::Contexts.GetCurrentContext(), VFrustumMeshHelper::IN_WORLD_SPACE);

  Vision::RenderLoopHelper.ClearScreen(VisRenderLoopHelper_cl::VCTF_All, VColorRef(0, 0, 0, 0));

  VisRenderContext_cl* pContext = Vision::Contexts.GetCurrentContext();

  const VisLightSrcCollection_cl* pLights = pContext->GetVisibilityCollector()->GetVisibleLights();

  Vision::RenderLoopHelper.BeginMeshRendering();
  Vision::RenderLoopHelper.ResetMeshStreams();
  Vision::RenderLoopHelper.AddMeshStreams(m_spMeshBuffer, m_spShader->GetStreamMask () | VERTEX_STREAM_INDEXBUFFER);
  for(unsigned int iLightIndex = 0; iLightIndex < pLights->GetNumEntries(); iLightIndex++)
  {
    VisLightSource_cl* pLight = pLights->GetEntry(iLightIndex);

    if((pLight->GetVisibleBitmask() & pContext->GetRenderFilterMask()) == 0)
      continue;

    if(!pLight->GetUseSpecular())
      continue;

    hkvVec4 vDirection;

    hkvVec3 vLightPositionRel = pLight->GetPosition() - pContext->GetCamera()->GetPosition();
    hkvVec3 vLightDirection = pLight->GetDirection();

    float fAttenuation = 1;
    switch(pLight->GetType())
    {
    case VIS_LIGHT_DIRECTED:
      vDirection = vLightDirection.getAsVec4(1.0f);
      break;

    case VIS_LIGHT_SPOTLIGHT:
      {
        vDirection = vLightDirection.getAsVec4(1.0f);

        float fAngle = vLightPositionRel.getAngleBetween(-vLightDirection);
        float fConeAngle = pLight->GetProjectionAngle();
        fAttenuation = hkvMath::clamp((fConeAngle - fAngle) / fConeAngle, 0.0f, 1.0f);

        float fDistance = vLightPositionRel.getLength();
        float fRadius = pLight->GetRadius();
        fAttenuation *= hkvMath::clamp((fRadius - fDistance) / fRadius, 0.0f, 1.0f);
      }
      break;

    case VIS_LIGHT_POINT:
      vDirection = (-vLightPositionRel).getNormalized().getAsVec4(1.0f);
      float fDistance = vLightPositionRel.getLength();
      float fRadius = pLight->GetRadius();
      fAttenuation = hkvMath::clamp((fRadius - fDistance) / fRadius, 0.0f, 1.0f);
      break;
    }

    hkvVec4 vColor = pLight->GetColor().getAsVec4() * pLight->GetMultiplier() * fAttenuation;

    hkvVec4 vParams(m_fSpecularPower, 0, 0, 0);

    m_spShader->GetConstantBuffer(VSS_PixelShader)->SetSingleParameterF("fLightDirection", vDirection.data);
    m_spShader->GetConstantBuffer(VSS_PixelShader)->SetSingleParameterF("fLightColor", vColor.data);
    m_spShader->GetConstantBuffer(VSS_PixelShader)->SetSingleParameterF("fParams", vParams.data);

    Vision::RenderLoopHelper.RenderMeshes(m_spShader, VisMeshBuffer_cl::MB_PRIMTYPE_INDEXED_TRILIST, 0, 2, 6);
  }
  Vision::RenderLoopHelper.EndMeshRendering();

  // Trigger pre-screenmask render hook to make the attached cubemap handle flip and blur the cubemap target
  VisRenderHookDataObject_cl data(&Vision::Callbacks.OnRenderHook, VRH_PRE_SCREENMASKS);
  Vision::Callbacks.OnRenderHook.TriggerCallbacks(&data);
}