void VRendererNodeHelper::GetFrustumFarCorners(hkvVec3* pVectors) { VisRenderContext_cl *pContext = m_pRendererNode->GetReferenceContext(); hkvMat4 projMat = pContext->GetViewProperties()->getProjectionMatrix(hkvClipSpaceYRange::MinusOneToOne); projMat.invert (); // top left far corner pVectors[0].set(-1.0f,1.0f,1.0f); // bottom left far corner pVectors[1].set(-1.0f,-1.0f,1.0f); // bottom right far corner pVectors[2].set(1.0f,-1.0f,1.0f); // top right far corner pVectors[3].set(1.0f,1.0f,1.0f); for(int i=0;i<4;i++) { hkvVec4 vTransformed = projMat.transform (pVectors[i].getAsPosition()); pVectors[i] = vTransformed.getAsVec3 () / vTransformed.w; } }
int VMobileForwardRenderLoop::GetLightInfluenceArea(VisLightSource_cl *pLight) { VASSERT(pLight != NULL); VisRenderContext_cl *pContext = VisRenderContext_cl::GetCurrentContext(); int iScreenWidth,iScreenHeight; pContext->GetSize(iScreenWidth, iScreenHeight); if (pLight->GetType() == VIS_LIGHT_DIRECTED) { // directional lights influence the whole screen return (iScreenWidth*iScreenHeight); } hkvMat4 projMatrix = pContext->GetViewProperties()->getProjectionMatrix(hkvClipSpaceYRange::MinusOneToOne); hkvMat4 viewMatrix = pContext->GetCamera()->GetWorldToCameraTransformation(); // get position/ radius of bounding sphere hkvVec3 vPosition; float fRadius = 0.0f; if (pLight->GetType() == VIS_LIGHT_POINT) { vPosition = pLight->GetPosition(); fRadius = pLight->GetRadius(); } else if (pLight->GetType() == VIS_LIGHT_SPOTLIGHT) { hkvAlignedBBox bBox; pLight->GetBoundingBox(bBox); vPosition = bBox.getBoundingSphere().m_vCenter; fRadius = bBox.getBoundingSphere().m_fRadius; } else VASSERT_MSG(false, "Unsupported light type"); // get corners of bounding rectangle in view space hkvVec4 vPositionVS = viewMatrix*vPosition.getAsVec4(1.0f); hkvVec4 vCorners[4]; vCorners[0] = vPositionVS+hkvVec4(-fRadius, -fRadius, 0.0f, 0.0f); vCorners[1] = vPositionVS+hkvVec4(fRadius, -fRadius, 0.0f, 0.0f); vCorners[2] = vPositionVS+hkvVec4(fRadius, fRadius, 0.0f, 0.0f); vCorners[3] = vPositionVS+hkvVec4(-fRadius, fRadius, 0.0f, 0.0f); // get corners of bounding rectangle in normalized device coordinates for (int i=0;i<4;i++) { vCorners[i] = projMatrix*vCorners[i]; vCorners[i] /= vCorners[i].w; } // clip corners of bounding rectangle hkvVec2 vMin(vCorners[0].x, vCorners[0].y); vMin.clampTo(hkvVec2(-1.0f, -1.0f), hkvVec2(1.0f, 1.0f)); hkvVec2 vMax(vCorners[2].x, vCorners[2].y); vMax.clampTo(hkvVec2(-1.0f, -1.0f), hkvVec2(1.0f, 1.0f)); // calculate influence area int iWidth = (int)((vMax.x-vMin.x)*0.5f*iScreenWidth); int iHeight = (int)((vMax.y-vMin.y)*0.5f*iScreenHeight); return (iWidth*iHeight); }
void VSimpleCopyPostprocess::Execute() { if (!IsActive() || !m_bIsInitialized) return; INSERT_PERF_MARKER_SCOPE("VSimpleCopyPostprocess"); RenderingOptimizationHelpers_cl::SetShaderPreference(112); int iWidth, iHeight; VisRenderContext_cl *pContext = VisRenderContext_cl::GetCurrentContext(); pContext->GetSize(iWidth, iHeight); Vision::RenderLoopHelper.SetScissorRect(NULL); Vision::RenderLoopHelper.ClearScreen(); // On DX9 a half pixel shift is required for the copy full screen pass. #if defined(_VR_DX9) const hkvVec2 texelShift(1.0f / (float)(iWidth*2), 1.0f / (float)(iHeight*2)); #else const hkvVec2 texelShift(0.0f, 0.0f); #endif VSimpleRenderState_t iState(VIS_TRANSP_NONE,RENDERSTATEFLAG_FRONTFACE|RENDERSTATEFLAG_ALWAYSVISIBLE|RENDERSTATEFLAG_NOWIREFRAME|RENDERSTATEFLAG_NOMULTISAMPLING); IVRender2DInterface *pRI = Vision::RenderLoopHelper.BeginOverlayRendering(); pRI->DrawTexturedQuad(hkvVec2(0.f,0.f), hkvVec2((float)iWidth, (float)iHeight), m_spSourceTextures[0], hkvVec2(0.0f) + texelShift, hkvVec2(1.0f) + texelShift, V_RGBA_WHITE, iState); Vision::RenderLoopHelper.EndOverlayRendering(); }
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::DetermineRelevantLights() { m_DynamicLightCollection.Clear(); m_pBasePassLight = NULL; m_iBasePassLightPriority = 0; // Get all visible light sources IVisVisibilityCollector_cl *pVisColl = VisRenderContext_cl::GetCurrentContext()->GetVisibilityCollector(); if (pVisColl == NULL) return; const VisLightSrcCollection_cl *pLightSourceCollection = pVisColl->GetVisibleLights(); if (pLightSourceCollection == NULL) return; unsigned int iNumLights = pLightSourceCollection->GetNumEntries(); if (iNumLights == 0) return; VisRenderContext_cl *pContext = VisRenderContext_cl::GetCurrentContext(); const hkvVec3 &vCamPos = pContext->GetCamera()->GetPosition(); for (unsigned i=0;i<iNumLights;i++) { VisLightSource_cl *pLight = pLightSourceCollection->GetEntry(i); // We are only interested in dynamic lights or static lights with attached shadow map component if ((!pLight->IsDynamic() && !GetCompatibleShadowMapComponent(pLight)) || pLight->GetRadius()<=HKVMATH_LARGE_EPSILON) continue; const float fFade = pLight->GetMultiplier()*pLight->GetFadeWeight(vCamPos); if (fFade <= HKVMATH_LARGE_EPSILON) continue; // See which geometry types have to cast shadows int iReceiverFlags = GetLightReceiverFlags(pLight); // If nothing receives light from this light source, we can proceed to the next light. if (!iReceiverFlags) continue; // Find light with highest priority. This light will be rendered in the base pass, in contrast to all // additional lights that are rendered additively after the base pass. The search ignores lights with // attached light clipping volume, since light clipping volumes can't be rendered before the base pass. if (!pLight->HasClipVolumeComponent()) { // get the light with highest priority (largest influence area in screen space combined with weighting factor) int iLightPriority = GetLightPriority(pLight); if (iLightPriority > m_iBasePassLightPriority) { m_pBasePassLight = pLight; m_iBasePassLightPriority = iLightPriority; } } if (pLight->IsDynamic()) m_DynamicLightCollection.AppendEntry(pLight); } }
void VTerrainVisibilityInfo::Set(IVisVisibilityCollector_cl *pCollector,const VTerrainConfig &config) { hkvVec3 vCamPos(hkvNoInitialization), vCamDir(hkvNoInitialization); pCollector->GetSourceObject()->GetPosition(vCamPos); vCamDir = pCollector->GetSourceObject()->GetDirection(); VisRenderContext_cl *pLODContext = pCollector->GetLODReferenceRenderContext(); VASSERT(pLODContext!=NULL && pLODContext->GetCamera()!=NULL); pLODContext->GetCamera()->GetPosition(m_vCamLODPos); m_vCamPos.FromRenderSpace(config,(const hkvVec3& )vCamPos); m_vCamVisPos = vCamPos; m_CameraPlane.setFromPointAndNormal(vCamPos,vCamDir); VASSERT(m_vCamPos.IsValid(config)); // compute the rest float fNear,fFar; pCollector->GetClipPlanes(fNear,fFar); m_fMaxViewDistance = fFar; m_fLODDistanceInvScale = pLODContext->GetLODDistanceScaling() * VTerrainSectorManager::g_fDecorationDistanceInvScaling; if (m_fLODDistanceInvScale>0.f) m_fLODDistanceScale = 1.f/m_fLODDistanceInvScale; else m_fLODDistanceScale = 0.f; // overestimate config.GetViewDistanceBox(m_CamBBox, m_vCamVisPos, fFar); m_VisibleRangeBox.FromRenderSpace(config,m_CamBBox); m_iContextFilterMask = pCollector->GetFilterBitmask(); // reset min/max m_iVisibleSectorRange[0] = m_iVisibleSectorRange[1] = 32000; m_iVisibleSectorRange[2] = m_iVisibleSectorRange[3] = -32000; // reset some of the values: m_iVisibleDecorationCount = 0; m_iEstimatedDecorationCount = 0; static bool bEnableOpt = true; // shadowmap related: if (bEnableOpt && pCollector->GetTypeId()==V_RUNTIME_CLASS(VShadowmapVisibilityCollector)) { m_pSMGenerator = ((VShadowmapVisibilityCollector *)pCollector)->m_pSMGenerator; float fShadowExtrusionFactor = m_pSMGenerator->GetShadowMapComponent()->GetShadowBoxExtrudeMultiplier(); m_vShadowExtrusion = m_pSMGenerator->GetDirection(); if (m_vShadowExtrusion.z<-0.01f) // normalize height m_vShadowExtrusion.z *= (-1.f/m_vShadowExtrusion.z); m_vShadowExtrusion *= fShadowExtrusionFactor; m_bCastDynamicShadows = (m_pSMGenerator->GetShadowMapComponent()->GetGeometryTypes()&SHADOW_CASTER_TERRAIN)>0; } else m_pSMGenerator = NULL; }
void VPostProcessScreenMasks::SetupContext() { VASSERT_MSG(vdynamic_cast<VRendererNodeCommon*>(m_pOwner) != NULL, "Postprocessing effects require a valid renderer node!"); VRendererNodeCommon* pRenderNode = GetOwner(); VisRenderContext_cl* pTargetContext = GetTargetContext(); pTargetContext->SetVisibilityCollector(NULL); // no visibility collector is needed for this post processor pRenderNode->AddContext(pTargetContext); }
bool VRendererNodeCommon::RendersIntoBackBuffer() { int iNumContexts = GetContextCount(); for (int i=0; i<iNumContexts; i++) { VisRenderContext_cl *pContext = GetContext(i); if (pContext != NULL) if (pContext->RendersIntoBackBuffer()) return true; } return false; }
void VLensFlareComponent::UpdateVisibility (float& fLastVisibilityQuery, float& fCurrentVisibility) { // Make sure we are actually attached to an object if (GetOwner()) { // Get Camera VisRenderContext_cl* pContext = VisRenderContext_cl::GetCurrentContext(); const hkvVec3 vCameraPos = pContext->GetCamera()->GetPosition(); // Get Light VisLightSource_cl* pLight = (VisLightSource_cl*)GetOwner(); hkvVec3 vPos; pLight->GetVirtualPosition( vPos, pContext ); hkvVec3 vDist = vCameraPos - vPos; float fDist = vDist.getLength(); float fFactor = 1.0f; // Distance FadeOut if (FadeOutEnd != 0 && FadeOutStart < FadeOutEnd) { if (fDist > FadeOutEnd) { fFactor = 0.0f; } else if (fDist > FadeOutStart) { fFactor = 1.0f - (fDist - FadeOutStart) / (FadeOutEnd - FadeOutStart); } } // Apply distance fade out fLastVisibilityQuery *= fFactor; // PreGlow/AfterGlow if (fLastVisibilityQuery > fCurrentVisibility) { float fSpeed = Vision::GetTimer()->GetTimeDifference() / ((PreGlowMS + 1) * 0.001f); fCurrentVisibility = hkvMath::Min(fCurrentVisibility + fSpeed, fLastVisibilityQuery); } else if (fLastVisibilityQuery < fCurrentVisibility) { float fSpeed = Vision::GetTimer()->GetTimeDifference() / ((AfterGlowMS + 1) * 0.001f); fCurrentVisibility = hkvMath::Max(fCurrentVisibility - fSpeed, fLastVisibilityQuery); } fCurrentVisibility = hkvMath::clamp(fCurrentVisibility, 0.0f, 1.0f); } }
void VRendererNodeCommon::DrawMeshBufferObjects(unsigned int iRenderOrder) { s_MeshBufferObjectCollection.Clear(); VisRenderContext_cl *pContext = VisRenderContext_cl::GetCurrentContext(); int iRenderFlags = pContext->GetRenderFilterMask(); int iNumMeshBufferObjects = VisMeshBufferObject_cl::ElementManagerGetSize(); for (int i=0;i<iNumMeshBufferObjects;i++) { VisMeshBufferObject_cl *pMeshBufferObject = VisMeshBufferObject_cl::ElementManagerGet(i); if (!pMeshBufferObject) continue; if (!(pMeshBufferObject->GetVisibleBitmask() & iRenderFlags)) continue; if (pMeshBufferObject->GetOrder() != iRenderOrder) continue; s_MeshBufferObjectCollection.AppendEntry(pMeshBufferObject); } Vision::RenderLoopHelper.RenderMeshBufferObjects(s_MeshBufferObjectCollection, iRenderOrder); }
bool VOcclusionQueryObjectPixelCounterLensFlare::Render(VOcclusionQuery &query, const hkvAlignedBBox &safeBox) { if (m_pLensFlare != NULL && m_pLensFlare->GetOwner() != NULL) { SetState(VISQUERY_RENDERSTATE_BILLBOARD); VisRenderContext_cl* pContext = VisRenderContext_cl::GetCurrentContext(); VisLightSource_cl* pLight = (VisLightSource_cl*)m_pLensFlare->GetOwner(); hkvVec3 vPos(hkvNoInitialization); pLight->GetVirtualPosition(vPos, pContext); hkvVec3 vCameraDir = pContext->GetCamera()->GetPosition() - vPos; vCameraDir /= hkvMath::Max(vCameraDir.getLength(), HKVMATH_LARGE_EPSILON); vPos += vCameraDir * m_pLensFlare->GetDepthBias(); query.DoHardwareOcclusionTest_Billboard(&vPos.x, m_pLensFlare->GetCheckBlockSize()); return true; } return false; }
int VMobileForwardRenderLoop::GetLightPriority(VisLightSource_cl *pLight) { int iLightPriority = 0; if (pLight->IsDynamic()) { iLightPriority = GetLightInfluenceArea(pLight); // lights with attached shadow map component have higher priority if (GetCompatibleShadowMapComponent(pLight)) iLightPriority *= 2; } // static lights with attached shadow map component (subtractive shadows) have highest priority else { VisRenderContext_cl *pContext = VisRenderContext_cl::GetCurrentContext(); int iScreenWidth,iScreenHeight; pContext->GetSize(iScreenWidth, iScreenHeight); iLightPriority = iScreenWidth*iScreenHeight*3; } return iLightPriority; }
// TODO: This doesn't handle opaque fullbright surfaces correctly yet, and translucent fullbright surfaces are simply ignored. void MirrorRenderLoop_cl::OnDoRenderLoop(void *pUserData) { INSERT_PERF_MARKER_SCOPE("MirrorRenderLoop_cl::OnDoRenderLoop"); #if defined (WIN32) || defined (_VISION_XENON) || defined (_VISION_PS3) || defined(_VISION_PSP2) || defined(_VISION_WIIU) if (Vision::Editor.GetIgnoreAdvancedEffects()) { // force a black reflection because it won't work with orthographic views Vision::RenderLoopHelper.ClearScreen(VisRenderLoopHelper_cl::VCTF_All, V_RGBA_BLACK); return; } #endif VisRenderContext_cl *pContext = Vision::Contexts.GetCurrentContext(); const int iRenderFlags = pContext->GetRenderFlags(); const float fFarClipDist = m_pMirror->GetActualFarClipDistance(); 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); // set the oblique clipping plane... pContext->SetCustomProjectionMatrix (m_pMirror->GetObliqueClippingProjection().getPointer ()); const VisStaticGeometryInstanceCollection_cl *pVisibleGeoInstancesPrimaryOpaquePass; const VisStaticGeometryInstanceCollection_cl *pVisibleGeoInstancesSecondaryOpaquePass; const VisStaticGeometryInstanceCollection_cl *pVisibleGeoInstancesTransparentPass; const VisEntityCollection_cl *pVisEntities; // === Visibility Determination === IVisVisibilityCollector_cl *pVisColl = VisRenderContext_cl::GetCurrentContext()->GetVisibilityCollector(); if (pVisColl == NULL) return; const VisVisibilityObjectCollection_cl *pVisObjectCollection = pVisColl->GetVisibleVisObjects(); hkvAlignedBBox box; int iVoCount = m_pMirror->GetVisibilityObjectCount(); int iFrustumCount = 0; bool bUseCommonFrustum = false; // === Determine Scissor Rect === hkvVec2 vMinScreenSpace, vMaxScreenSpace; const hkvAlignedBBox &worldSpaceBox = m_pMirror->GetBoundingBox(); hkvVec3 vCorners[8]; worldSpaceBox.getCorners (vCorners); VRectanglef scissorRect; bool bUseScissorRect = true; for (int i=0; i<8; i++) { float x2d, y2d; BOOL bInFrontOfCamera = pContext->Project2D(vCorners[i], x2d, y2d); if (bInFrontOfCamera) { scissorRect.Add(hkvVec2(x2d, y2d)); } else { bUseScissorRect = false; break; } } if (bUseScissorRect) Vision::RenderLoopHelper.SetScissorRect(&scissorRect); for (int iVo = 0; iVo < iVoCount; iVo++) { VisVisibilityObject_cl *pVisObj = m_pMirror->GetVisibilityObject(iVo); if (pVisObj != NULL && pVisObj->WasVisibleInAnyLastFrame()) { if (iFrustumCount <= MAX_SEPARATE_FRUSTA) { const hkvAlignedBBox &voBox = pVisObj->GetWorldSpaceBoundingBox(); box.expandToInclude(voBox); if (m_Frustum[iFrustumCount].Set(pContext->GetCamera()->GetPosition(), voBox, true, fFarClipDist)) { iFrustumCount++; } else { bUseCommonFrustum = true; } } else { const hkvAlignedBBox &voBox = pVisObj->GetWorldSpaceBoundingBox(); box.expandToInclude(voBox); bUseCommonFrustum = true; } } } if (bUseCommonFrustum) { iFrustumCount = 1; if (!m_Frustum[0].Set(pContext->GetCamera()->GetPosition(), box, true, fFarClipDist)) iFrustumCount = 0; } if (iFrustumCount>0) { for (int i=0; i<iFrustumCount; i++) { m_visiblePrimaryOpaquePassGeoInstances.Clear(); m_visibleSecondaryOpaquePassGeoInstances.Clear(); m_visibleTransparentOpaquePassGeoInstances.Clear(); m_visEntities.Clear(); pVisColl->GetVisibleStaticGeometryInstancesForPass(VPT_PrimaryOpaquePass)->DetermineEntriesTouchingFrustum(m_Frustum[i], m_visiblePrimaryOpaquePassGeoInstances); pVisColl->GetVisibleStaticGeometryInstancesForPass(VPT_SecondaryOpaquePass)->DetermineEntriesTouchingFrustum(m_Frustum[i], m_visibleSecondaryOpaquePassGeoInstances); pVisColl->GetVisibleStaticGeometryInstancesForPass(VPT_TransparentPass)->DetermineEntriesTouchingFrustum(m_Frustum[i], m_visibleTransparentOpaquePassGeoInstances); pVisColl->GetVisibleEntities()->DetermineEntriesTouchingFrustum(m_Frustum[i], m_visEntities); if (iFrustumCount == 1) break; m_visiblePrimaryOpaquePassGeoInstances.TagEntries(); m_visibleSecondaryOpaquePassGeoInstances.TagEntries(); m_visibleTransparentOpaquePassGeoInstances.TagEntries(); m_visEntities.TagEntries(); } if (iFrustumCount > 1) { m_visiblePrimaryOpaquePassGeoInstances.Clear(); m_visibleSecondaryOpaquePassGeoInstances.Clear(); m_visibleTransparentOpaquePassGeoInstances.Clear(); m_visEntities.Clear(); pVisColl->GetVisibleStaticGeometryInstancesForPass(VPT_PrimaryOpaquePass)->GetTaggedEntries(m_visiblePrimaryOpaquePassGeoInstances); pVisColl->GetVisibleStaticGeometryInstancesForPass(VPT_SecondaryOpaquePass)->GetTaggedEntries(m_visibleSecondaryOpaquePassGeoInstances); pVisColl->GetVisibleStaticGeometryInstancesForPass(VPT_TransparentPass)->GetTaggedEntries(m_visibleTransparentOpaquePassGeoInstances); pVisColl->GetVisibleEntities()->GetTaggedEntries(m_visEntities); } pVisibleGeoInstancesPrimaryOpaquePass = &m_visiblePrimaryOpaquePassGeoInstances; pVisibleGeoInstancesSecondaryOpaquePass = &m_visibleSecondaryOpaquePassGeoInstances; pVisibleGeoInstancesTransparentPass = &m_visibleTransparentOpaquePassGeoInstances; pVisEntities = &m_visEntities; } else { pVisibleGeoInstancesPrimaryOpaquePass = pVisColl->GetVisibleStaticGeometryInstancesForPass(VPT_PrimaryOpaquePass); pVisibleGeoInstancesSecondaryOpaquePass = pVisColl->GetVisibleStaticGeometryInstancesForPass(VPT_SecondaryOpaquePass); pVisibleGeoInstancesTransparentPass = pVisColl->GetVisibleStaticGeometryInstancesForPass(VPT_TransparentPass); pVisEntities = pVisColl->GetVisibleEntities(); } // === End Visibility Determination === if (m_pMirror->GetExecuteRenderHooks()) { VisRenderHookDataObject_cl data(&Vision::Callbacks.OnRenderHook,VRH_PRE_PRIMARY_OPAQUE_PASS_GEOMETRY); Vision::Callbacks.OnRenderHook.TriggerCallbacks(&data); } // Render opaque static geometry VASSERT(m_spDefaultLightMapping->m_Shaders.Count()==1); TRIGGER_MIRROR_HOOK(VRH_PRE_PRIMARY_OPAQUE_PASS_GEOMETRY) VisMirror_cl::VReflectionShaderSets_e shaderMode = m_pMirror->m_eReflectionShaderMode; DrawStaticGeometry(*pVisibleGeoInstancesPrimaryOpaquePass, VPT_PrimaryOpaquePass); DrawStaticGeometry(*pVisibleGeoInstancesSecondaryOpaquePass, VPT_SecondaryOpaquePass); // Render entities const VisEntityCollection_cl *pEntities = pVisEntities; int iCount = pEntities->GetNumEntries(); VASSERT(m_spDefaultLightGrid->m_Shaders.Count()==1); //VCompiledShaderPass *pLightgridShader = m_spDefaultLightGrid->m_Shaders.GetAt(0); int i; //bool bUseSimpleShader = shaderMode==VisMirror_cl::AlwaysSimple; Vision::RenderLoopHelper.BeginEntityRendering(); for (i=0;i<iCount;i++) { VisBaseEntity_cl *pEnt = pEntities->GetEntry(i); // Vision::RenderLoopHelper.TrackLightGridInfo(pEnt); // important: need to be done in RenderEntityWithSurfaceShaderList //if (bUseSimpleShader) //{ // Vision::RenderLoopHelper.RenderEntityWithShaders(pEnt,1,&pLightgridShader); //} //else { VisDrawCallInfo_t surfaceShaderList[RLP_MAX_ENTITY_SURFACES]; VDynamicMesh *pMesh = pEnt->GetMesh(); VisSurface_cl **ppSurfaces = pEnt->GetSurfaceArray(); int iNumSubmeshes = pMesh->GetSubmeshCount(); for (int j=0; j<iNumSubmeshes; j++) { VisDrawCallInfo_t &info(surfaceShaderList[j]); VBaseSubmesh* pSubmesh = pMesh->GetSubmesh(j); VisSurface_cl* pSurface = ppSurfaces[pSubmesh->m_iMaterialIndex]; info.Set(pSubmesh, pSurface, GetMirrorShader (pSurface, shaderMode)); } Vision::RenderLoopHelper.RenderEntityWithSurfaceShaderList(pEnt, iNumSubmeshes, surfaceShaderList); } } Vision::RenderLoopHelper.EndEntityRendering(); // Render Sky if (VSky::IsVisible()) { // The sky has to be rendered without oblique clipping pContext->SetCustomProjectionMatrix(NULL); Vision::RenderLoopHelper.RenderSky(); // set the oblique clipping plane after sky... pContext->SetCustomProjectionMatrix (m_pMirror->GetObliqueClippingProjection().getPointer ()); } if (m_pMirror->GetExecuteRenderHooks()) { VisRenderHookDataObject_cl data(&Vision::Callbacks.OnRenderHook,VRH_PRE_OCCLUSION_TESTS); Vision::Callbacks.OnRenderHook.TriggerCallbacks(&data); } // Render Coronas / Lens Flares VisRenderHookDataObject_cl data(&Vision::Callbacks.OnRenderHook,VRH_CORONAS_AND_FLARES); Vision::Callbacks.OnRenderHook.TriggerCallbacks(&data); TRIGGER_MIRROR_HOOK(VRH_PRE_OCCLUSION_TESTS) if (iRenderFlags&VIS_RENDERCONTEXT_FLAG_USE_OCCLUSIONQUERY) Vision::RenderLoopHelper.PerformHardwareOcclusionQuery(); if (iRenderFlags&VIS_RENDERCONTEXT_FLAG_USE_PIXELCOUNTER) Vision::RenderLoopHelper.PerformHardwarePixelCounterQuery(); DrawDynamicLight(); TRIGGER_MIRROR_HOOK(VRH_DECALS) TRIGGER_MIRROR_HOOK(VRH_CORONAS_AND_FLARES) TRIGGER_MIRROR_HOOK(VRH_PRE_TRANSPARENT_PASS_GEOMETRY) DrawStaticGeometry(*pVisibleGeoInstancesTransparentPass, VPT_TransparentPass); TRIGGER_MIRROR_HOOK(VRH_POST_TRANSPARENT_PASS_GEOMETRY) if (bUseScissorRect) Vision::RenderLoopHelper.SetScissorRect(NULL); }
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 }
// renders visible wallmarks of specified pass type (pre or post, which is relevant in deferred context) void VWallmarkManager::RenderProjectedWallmarks(VPassType_e ePassType) { INSERT_PERF_MARKER_SCOPE("Wallmark Rendering (VWallmarkManager::RenderProjectedWallmarks)"); const int iWallmarkCount = m_AllProjectedWallmarks.Count(); IVisVisibilityCollector_cl *pVisCollector = Vision::Contexts.GetCurrentContext()->GetVisibilityCollector(); if (!pVisCollector || !iWallmarkCount) return; const VisStaticGeometryInstanceCollection_cl *pGeoInstances = pVisCollector->GetVisibleStaticGeometryInstances(); VisStaticGeometryInstance_cl::ResetTags(); pGeoInstances->TagEntries(); VisStaticGeometryInstanceCollection_cl &targetGiCollection = m_TempGeoInstanceCollection; VisRenderContext_cl *pContext = Vision::Contexts.GetCurrentContext(); VisRenderContext_cl *pLODContext = pContext->GetLODReferenceContext(); hkvVec3 vLODPos = pLODContext ? pLODContext->GetCamera()->GetPosition() : pContext->GetCamera()->GetPosition(); unsigned int iContextFilter = pContext->GetRenderFilterMask(); const VisFrustum_cl *pFrustum = pVisCollector->GetBaseFrustum(); int i; for (i=0;i<iWallmarkCount;i++) { VProjectedWallmark *pProjWallmark = m_AllProjectedWallmarks.GetAt(i); if ((pProjWallmark->GetVisibleBitmask() & iContextFilter)==0 || (ePassType & pProjWallmark->m_ePassType) == 0) continue; pProjWallmark->PrepareForRendering(); const VisStaticGeometryInstanceCollection_cl &wmGiList = pProjWallmark->GetStaticGeometryCollection(); #ifdef HK_DEBUG const int iNum = wmGiList.GetNumEntries(); for (int j=0;j<iNum;j++) { VisStaticGeometryInstance_cl *pInst = wmGiList.GetEntry(j); VASSERT_MSG(pInst && (pInst->GetGeometryType()==STATIC_GEOMETRY_TYPE_MESHINSTANCE || pInst->GetGeometryType()==STATIC_GEOMETRY_TYPE_TERRAIN), "The wallmark conains invalid primitive references") } #endif // clip against its bounding box (primitive visibility might overestimate visible parts) const hkvAlignedBBox &bbox = pProjWallmark->GetBoundingBox(); if (pProjWallmark->m_fFarClipDistance>0.f && pProjWallmark->m_fFarClipDistance<bbox.getDistanceTo(vLODPos)) continue; if (pFrustum && !pFrustum->Overlaps(bbox)) continue; const int iGeomFilter = pProjWallmark->GetGeometryTypeFilterMask(); if (iGeomFilter&PROJECTOR_AFFECTS_STATICMESHES) { // standard geometry targetGiCollection.Clear(); wmGiList.GetTaggedEntriesOfType(targetGiCollection,STATIC_GEOMETRY_TYPE_MESHINSTANCE); if (targetGiCollection.GetNumEntries()) { // render the static geometry instances using lightmapped or non-lightmapped shader VProjectorShaderPass *pShader = GetWallmarkShader(pProjWallmark,STATIC_GEOMETRY_TYPE_MESHINSTANCE); Vision::RenderLoopHelper.RenderStaticGeometryWithShader(targetGiCollection, *pShader); } } if (iGeomFilter&PROJECTOR_AFFECTS_TERRAIN) { // terrain geometry (different shader) targetGiCollection.Clear(); wmGiList.GetTaggedEntriesOfType(targetGiCollection,STATIC_GEOMETRY_TYPE_TERRAIN); if (targetGiCollection.GetNumEntries()>0) { // render the static geometry instances using lightmapped or non-lightmapped shader VProjectorShaderPass *pShader = GetWallmarkShader(pProjWallmark,STATIC_GEOMETRY_TYPE_TERRAIN); if (pShader) Vision::RenderLoopHelper.RenderStaticGeometryWithShader(targetGiCollection, *pShader); } } // entities if (iGeomFilter&PROJECTOR_AFFECTS_ENTITIES) { const VisEntityCollection_cl *pVisibleEntities = pVisCollector->GetVisibleEntities(); const unsigned int iInfluenceMask = pProjWallmark->GetInfluenceBitmask(); m_TempEntityCollection.Clear(); const int iEntCount = pVisibleEntities->GetNumEntries(); for (int j=0;j<iEntCount;j++) { VisBaseEntity_cl *pEntity = pVisibleEntities->GetEntry(j); if (pEntity==NULL || (pEntity->GetVisibleBitmask()&iInfluenceMask)==0) continue; const hkvAlignedBBox &entityBox(*pEntity->GetCurrentVisBoundingBoxPtr()); if (!entityBox.overlaps(bbox)) continue; m_TempEntityCollection.AppendEntry(pEntity); } if (m_TempEntityCollection.GetNumEntries()>0) { VProjectorShaderPass *pShader = GetWallmarkShader(pProjWallmark,STATIC_GEOMETRY_TYPE_MESHINSTANCE); // we can use this shader - VS skinning is used implicitly Vision::RenderLoopHelper.RenderEntitiesWithShader(m_TempEntityCollection, *pShader); } } } }
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 VCoronaManager::RenderCorona (VCoronaCandidate& coronaCandidate, VTextureObject*& pTexture) { #ifdef SUPPORTS_CORONAS VCoronaComponent *pCorona = coronaCandidate.m_pCorona; VisRenderContext_cl* pContext = VisRenderContext_cl::GetCurrentContext(); VisLightSource_cl* pLight = (VisLightSource_cl*)pCorona->GetOwner(); hkvVec3 vLightPos(hkvNoInitialization); pLight->GetVirtualPosition(vLightPos, pContext); hkvVec3 vEyePos(hkvNoInitialization); pContext->GetCamera()->GetPosition(vEyePos); hkvVec3 vDir = pContext->GetCamera()->GetDirection(); // Corona texture VTextureObject *pTex = pCorona->GetCoronaTexture(); if (pTex == NULL) return; if (pTexture != pTex) { pTexture = pTex; Vision::RenderLoopHelper.BindMeshTexture(pTexture,0); } // Get light color VColorRef color = pLight->GetColor(); hkvVec3 vDist = vLightPos - vEyePos; float fEyeDist = vDir.dot(vDist); //determine if camera is in light cone if the light is directional float fDirectionalDampening = 1.0f; if ( pLight->GetType() == VIS_LIGHT_SPOTLIGHT && pCorona->GetOnlyVisibleInSpotLight() ) { fDirectionalDampening = 0.0f; float fConeAngle = pLight->GetProjectionAngle(); float fConeLength = pLight->GetRadius(); hkvVec3 fConeDirection = pLight->GetDirection(); fConeDirection.normalize(); hkvVec3 vLightEyeDist = vEyePos - vLightPos; //#2 check if the camera is inside the angle of the cone float cosinusAngle = (vLightEyeDist/vLightEyeDist.getLength()).dot(fConeDirection); float fDegree = hkvMath::acosDeg(cosinusAngle); float normRadius = fDegree / (fConeAngle/2.0f); if (normRadius < 1.0f) { //hardcoded falloff. For better performance, we avoid sampling the projection texture here. const float fEpsilon = 64.0f/256.0f; const float fQuadFactor = 1.0f/fEpsilon - 1.0f; fDirectionalDampening = 1.0f / (1.0f + fQuadFactor*normRadius*normRadius); // scale the function so that the value is exactly 0.0 at the edge and 1.0 in the center fDirectionalDampening = (fDirectionalDampening - fEpsilon) / (1.0f - fEpsilon); } } // Fog params float fFogDampening = 1.0f; if (pLight->GetType() != VIS_LIGHT_DIRECTED && Vision::World.IsLinearDepthFogEnabled()) { const VFogParameters &fog = Vision::World.GetFogParameters(); float fFogStart = fog.fDepthStart; float fFogEnd = fog.fDepthEnd; float fFogFactor = (fFogEnd > fFogStart) ? ((fEyeDist - fFogStart) / (fFogEnd - fFogStart)) : 0.f; fFogDampening = 1.0f - hkvMath::clamp(fFogFactor, 0.0f, 1.0f); } // Get corona rotation float fRotation = 0.0f; hkvVec4 vRotation(1.0f, 0.0f, 0.0f, 1.0f); if (pCorona->CoronaFlags & VIS_CORONASCALE_ROTATING) { fRotation = hkvMath::mod (fEyeDist * 0.5f, 360.f); vRotation.x = hkvMath::cosDeg (fRotation); vRotation.y = -hkvMath::sinDeg (fRotation); vRotation.z = -vRotation.y; vRotation.w = vRotation.x; } // Texture dimensions int iSizeX, iSizeY, depth; pTex->GetTextureDimensions(iSizeX, iSizeY, depth); hkvVec4 vScale(0.0f, 0.0f, 0.0f, 0.0f); int iMainWidth, iMainHeight, iWidth, iHeight; pContext->GetSize(iWidth, iHeight); VisRenderContext_cl::GetMainRenderContext()->GetSize(iMainWidth, iMainHeight); // Preserve texture aspect ratio int iTexHeight = pTex->GetTextureHeight(); int iTexWidth = pTex->GetTextureWidth(); // Perspective scaling // This scaling ensures roughly the same size on 720p as the old implementation. vScale.z = iTexWidth * pCorona->CoronaScaling * 0.25f; vScale.w = iTexHeight * pCorona->CoronaScaling * 0.25f; // Screen-space scaling // This scaling ensures roughly the same size on 720p as the old implementation. const float fScaleFactor = pCorona->CoronaScaling * iMainHeight / 11.0f; vScale.x = ((float)iTexWidth / 128.0f) * fScaleFactor * (float(iWidth) / float(iMainWidth)); vScale.y = ((float)iTexHeight / 128.0f) * fScaleFactor * (float(iHeight) / float(iMainHeight)); vScale.x *= 2.0f / iWidth; vScale.y *= 2.0f / iHeight; // Scale by visibility if (pCorona->CoronaFlags & VIS_CORONASCALE_VISIBLEAREA) { vScale.x *= coronaCandidate.m_fCurrentVisibility; vScale.y *= coronaCandidate.m_fCurrentVisibility; vScale.z *= coronaCandidate.m_fCurrentVisibility; vScale.w *= coronaCandidate.m_fCurrentVisibility; } VCompiledShaderPass* pShader = m_spCoronaTechnique->GetShader(0); VShaderConstantBuffer *pVertexConstBuffer = pShader->GetConstantBuffer(VSS_VertexShader); // xyz = worldspace position, w = 1.0 if VIS_CORONASCALE_DISTANCE is true, otherwise zero. pVertexConstBuffer->SetSingleParameterF("coronaPosition", vLightPos.x, vLightPos.y, vLightPos.z, (pCorona->CoronaFlags & VIS_CORONASCALE_DISTANCE) ? 1.0f : 0.0f); // xyz = light color, w = corona visibility. pVertexConstBuffer->SetSingleParameterF("coronaColor", color.r/255.0f, color.g/255.0f, color.b/255.0f, coronaCandidate.m_fCurrentVisibility * fFogDampening * fDirectionalDampening); // xyzw = 2x2 rotation matrix. float2x2 is not supported in shader model 2, so a float4 is used and multiplication is done manually in the shader. pVertexConstBuffer->SetSingleParameterF("coronaRotation", vRotation.x, vRotation.y, vRotation.z, vRotation.w); // xy = screen-space scaling. zw = view-space scaling. pVertexConstBuffer->SetSingleParameterF("coronaScale", vScale.x, vScale.y, vScale.z, vScale.w); Vision::RenderLoopHelper.RenderMeshes(pShader, VisMeshBuffer_cl::MB_PRIMTYPE_TRILIST, 0, 2, 6); #endif }
void VRendererNodeCommon::InitializePostProcessors() { VASSERT_MSG(IsInitialized(), "The renderer node must be initialized before initializing the post processors."); ANALYSIS_IGNORE_WARNING_BLOCK_START(6385); ANALYSIS_IGNORE_WARNING_BLOCK_START(6211); // Increment the update counter to enable modifying the post processors without recursing m_iPostProcessorUpdateCounter++; VType* pCopyPostProcessorType = GetDefaultCopyPostprocessorType(); bool bInvalidPostProcessorActive = false; do { bInvalidPostProcessorActive = false; DeInitializePostProcessors(); VPostProcessingBaseComponent* pSimpleCopy = NULL; // Collect post processor components VMemoryTempBuffer<256> tempBuffer((Components().Count() + 1) * sizeof(VPostProcessingBaseComponent*)); VPostProcessingBaseComponent** postProcessors = reinterpret_cast<VPostProcessingBaseComponent**>(tempBuffer.GetBuffer()); int iPostProcessorIndex = 0; for(int iComponentIndex = 0; iComponentIndex < Components().Count(); iComponentIndex++) { if(VPostProcessingBaseComponent* pPostProcessor = vdynamic_cast<VPostProcessingBaseComponent*>(Components().GetAt(iComponentIndex))) { // Don't take the auto added copy PP into consideration, we'll handle that separately if(pCopyPostProcessorType != NULL && pPostProcessor->IsOfType(pCopyPostProcessorType)) { pSimpleCopy = pPostProcessor; continue; } // HS#10443: Skip post-processors which do nothing - needs testing whether this works cleanly when the identity state changes if(!pPostProcessor->IsActive() /*!pPostProcessor->IsIdentity()*/) { continue; } postProcessors[iPostProcessorIndex] = pPostProcessor; iPostProcessorIndex++; } } int iNumPostProcessors = iPostProcessorIndex; qsort(postProcessors, iNumPostProcessors, sizeof(VPostProcessingBaseComponent*), ComparePostProcessorsByPriority); int iCopyPPIndex = iNumPostProcessors; // Scan backwards through post processors to find one which can take over the responsibility // of copying the scene to the final target context // // This post processor must: // - come after the MSAA resolve step // - render an opaque full screen quad // - not have any postprocessor afterwards that reads the accumulation buffer bool bUsesOffscreenRenderTarget = !m_bUsesDirectRenderToFinalTargetContext; for(int i = iNumPostProcessors - 1; i >= 0; i--) { if(postProcessors[i]->GetPriority() < VIS_RENDERCONTEXTPRIORITY_POSTPROCESSOR_RESOLVED) { bUsesOffscreenRenderTarget = true; break; } const unsigned int flags = postProcessors[i]->GetBufferUsageFlags(); // Post processors that use their own render target can't be used for copying to the back buffer if((flags & VPostProcessingBaseComponent::USES_CUSTOM_RENDERTARGET) != 0) { bUsesOffscreenRenderTarget = true; break; } // Check first if the post processors draws an opaque full screen quad, because // a PP that draws a full screen quad AND samples the accumulation buffer // is still suitable for copying the accumulation buffer into the final target context (such as tonemapping). if((flags & VPostProcessingBaseComponent::DRAWS_FULLSCREEN_QUAD) != 0 && (flags & VPostProcessingBaseComponent::USES_BLENDING) == 0) { iCopyPPIndex = i; break; } if(flags & VPostProcessingBaseComponent::SAMPLES_ACCUMULATION_BUFFER) { bUsesOffscreenRenderTarget = true; break; } } VASSERT_MSG(bUsesOffscreenRenderTarget != m_bUsesDirectRenderToFinalTargetContext, "Renderer node indicated that it renders directly to the renderer node's final target context, but post-processors require an offscreen render target!"); // If no suitable post processor was found, we need to make sure the scene is copied bool bNeedsManualCopyToTarget = (iCopyPPIndex == iNumPostProcessors) && bUsesOffscreenRenderTarget; // If we don't use an offscreen RT, we don't have a copy PP if (!bUsesOffscreenRenderTarget) iCopyPPIndex = -1; if(bNeedsManualCopyToTarget) { if (pCopyPostProcessorType != NULL) { if(pSimpleCopy == NULL) { pSimpleCopy = (VPostProcessingBaseComponent*)pCopyPostProcessorType->CreateInstance(); VASSERT(pSimpleCopy != NULL); AddComponent(pSimpleCopy); } postProcessors[iNumPostProcessors] = pSimpleCopy; iNumPostProcessors++; } } else if(pSimpleCopy != NULL) { // Remove existing copy PP if not needed RemoveComponent(pSimpleCopy); } m_assignedContexts.EnsureCapacity(iNumPostProcessors); // Create a target context for each post processor for(iPostProcessorIndex = 0; iPostProcessorIndex < iNumPostProcessors; iPostProcessorIndex++) { VPostProcessingBaseComponent* pPostProcessor = postProcessors[iPostProcessorIndex]; pPostProcessor->m_iTargetIndex = iPostProcessorIndex; const VisRenderContext_cl* pFinalTargetContext = GetFinalTargetContext(); bool bRenderIntoFinalTargetContext = (iPostProcessorIndex >= iCopyPPIndex); int iPosX, iPosY, iWidth, iHeight; float zMin, zMax; if(bRenderIntoFinalTargetContext) { pFinalTargetContext->GetViewport(iPosX, iPosY, iWidth, iHeight, zMin, zMax); } else { GetReferenceContext()->GetViewport(iPosX, iPosY, iWidth, iHeight, zMin, zMax); } VisRenderContext_cl* pContext = new VisRenderContext_cl(pFinalTargetContext->GetCamera(), 90.0f, 90.0f, iWidth, iHeight, 0.0f, 0.0f, pFinalTargetContext->GetRenderFlags()); pContext->SetRenderFilterMask(pFinalTargetContext->GetRenderFilterMask()); pContext->SetViewport(iPosX, iPosY, iWidth, iHeight, zMin, zMax); pContext->SetViewProperties(pFinalTargetContext->GetViewProperties()); pContext->SetName(pPostProcessor->GetTypeId()->m_lpszClassName); pContext->SetVisibilityCollector(pFinalTargetContext->GetVisibilityCollector(), false); pContext->SetPriority(pPostProcessor->GetPriority()); pContext->SetUserData(pPostProcessor); pContext->SetRenderLoop(new PostProcessRenderLoop_cl(pPostProcessor)); if(bRenderIntoFinalTargetContext) { pContext->SetRenderAndDepthStencilTargets(pFinalTargetContext); if (bUsesOffscreenRenderTarget) { // If possible, try to give the post processors that render directly into the final target context a useful depth-stencil target. // This is only possible if the final target context has MSAA disabled. bool bCanReplaceDST = false; if(pFinalTargetContext->RendersIntoBackBuffer()) { #if !defined(_VISION_ANDROID) && !defined(_VISION_TIZEN) && !defined(_VISION_NACL) // On Android, the back buffer context uses a fixed FBO, so we can't replace the DST. bCanReplaceDST = Vision::Video.GetCurrentConfig()->m_eMultiSample == VVIDEO_MULTISAMPLE_OFF; #endif } else if(pFinalTargetContext->GetRenderTarget(0) != NULL) { bCanReplaceDST = static_cast<VisRenderableTexture_cl*>(pFinalTargetContext->GetRenderTarget(0))->GetConfig()->m_iMultiSampling <= 1; } int iRefWidth, iRefHeight, iFinalWidth, iFinalHeight; pFinalTargetContext->GetSize(iFinalWidth, iFinalHeight); GetReferenceContext()->GetSize(iRefWidth, iRefHeight); if(iRefWidth != iFinalWidth || iRefHeight != iFinalHeight) { bCanReplaceDST = false; } if(bCanReplaceDST) { pContext->SetDepthStencilTarget(static_cast<VisRenderableTexture_cl*>(GetPostProcessDepthStencilTarget(VRTV_RESOLVED))); } else { hkvLog::Warning("Could not attach a depth-stencil target to the context of the \"%s\" post processor - depth testing will not work correctly.", pPostProcessor->GetTypeId()->m_lpszClassName); } } } else { VRenderTargetVersion_e targetVersion = (pPostProcessor->GetPriority() <= VIS_RENDERCONTEXTPRIORITY_POSTPROCESSOR_RESOLVED) ? VRTV_MSAA : VRTV_RESOLVED; if((pPostProcessor->GetBufferUsageFlags() & VPostProcessingBaseComponent::USES_CUSTOM_RENDERTARGET) == 0) { pContext->SetRenderTarget(0, static_cast<VisRenderableTexture_cl*>(GetPostProcessColorTarget(targetVersion))); pContext->SetDepthStencilTarget(static_cast<VisRenderableTexture_cl*>(GetPostProcessDepthStencilTarget(targetVersion))); } } m_assignedContexts.Add(pContext); pPostProcessor->InitializePostProcessor(); // Validity can only be determined after initialization, so deactivate the invalid postprocessor and retry the entire context setup if(!pPostProcessor->IsValid()) { // the post-processor will have deactivated itself by now pPostProcessor->SetActive(false); bInvalidPostProcessorActive = true; } } } while ( bInvalidPostProcessorActive ); m_bPostProcessorAssignmentDirty = false; m_iPostProcessorUpdateCounter--; VisRenderContext_cl::ElementManagerDeleteAllUnRef(); ANALYSIS_IGNORE_WARNING_BLOCK_END; ANALYSIS_IGNORE_WARNING_BLOCK_END; }
void VFmodManager::RunTick(float fTimeDelta) { VISION_PROFILE_FUNCTION(PROFILING_FMOD_OVERALL); if (!IsInitialized()) { if (!IsOutputDevicePresent()) InitDevice(); return; } // profiling scope { VISION_PROFILE_FUNCTION(PROFILING_FMOD_PUREUPDATE); VASSERT(m_pEventSystem!=NULL); // update Fmod listener attributes VisObject3D_cl *pListener = m_pListenerObject; if (pListener == NULL) { // The listener is the main camera. Check for teleportation since the last Fmod update, in // which case we won't use the position difference to calculate the listener speed. VisContextCamera_cl* pCamera = Vision::Camera.GetMainCamera(); VisRenderContext_cl* pContext = VisRenderContext_cl::GetMainRenderContext(); if (pCamera != NULL && pContext != NULL) { if (m_bLastListenerPositionValid && pCamera->GetLastTeleported() > m_iFrameOfLastUpdate) m_bLastListenerPositionValid = false; m_iFrameOfLastUpdate = pContext->GetLastRenderedFrame(); pListener = pCamera; } } if (!pListener) return; hkvVec3 vCamPos = pListener->GetPosition(); hkvVec3 vDir(pListener->GetObjDir()), vRight(pListener->GetObjDir_Right()), vUp(pListener->GetObjDir_Up()); // Determine the camera velocity based on the previous known position hkvVec3 vCamVel(m_bLastListenerPositionValid && (fTimeDelta > 0.f) ? (vCamPos - m_vLastListenerPosition) * (1.f / fTimeDelta) : hkvVec3::ZeroVector()); m_vLastListenerPosition = vCamPos; m_bLastListenerPositionValid = true; vUp = -vUp; // compensate for coordinate system m_pEventSystem->set3DListenerAttributes(0, (FMOD_VECTOR *)&vCamPos, (FMOD_VECTOR *)&vCamVel, (FMOD_VECTOR *)&vDir, (FMOD_VECTOR *)&vUp); // update all sound objects SoundInstances().Update(fTimeDelta); // update all events Events().Update(fTimeDelta); // update Fmod event system m_fTimeLeftOver += fTimeDelta; if (m_fTimeLeftOver > m_config.fTimeStep) { m_pEventSystem->update(); #ifdef VFMOD_SUPPORTS_NETWORK if (m_config.bUseNetworkSystem) FMOD::NetEventSystem_Update(); #endif m_fTimeLeftOver = hkvMath::mod (m_fTimeLeftOver, m_config.fTimeStep); } } // do not purge sounds/ events in vForge, in order to allow toggling playback via hotspot button if (Vision::Editor.IsInEditor()) return; if (m_bAnyStopped) { VISION_PROFILE_FUNCTION(PROFILING_FMOD_PURGE); // all sounds/ events that have finished playing are removed from handling SoundInstances().PurgeNotPlaying(); Events().PurgeNotPlaying(); m_bAnyStopped = false; // reset any stopped flag } }
void VisMirrorManager_cl::OnHandleCallback(IVisCallbackDataObject_cl *pData) { if (pData->m_pSender==&Vision::Callbacks.OnRendererNodeChanged) { VisRendererNodeChangedDataObject_cl &data = *((VisRendererNodeChangedDataObject_cl *)pData); if (data.m_spAddedNode != NULL) { const int iMirrorCount = m_Instances.Count(); for (int i=0; i<iMirrorCount; ++i) { data.m_spAddedNode->AddContext(m_Instances.GetAt(i)->m_spReflectionContext); } } if (data.m_spRemovedNode != NULL) { const int iMirrorCount = m_Instances.Count(); for (int i=0; i<iMirrorCount; ++i) { data.m_spRemovedNode->RemoveContext(m_Instances.GetAt(i)->m_spReflectionContext); } } return; } if (pData->m_pSender==&Vision::Callbacks.OnRendererNodeSwitching) { // Handle all mirrors and see whether they have to be rendered VisRendererNodeDataObject_cl &data = *static_cast<VisRendererNodeDataObject_cl *>(pData); const int iMirrorCount = m_Instances.Count(); for (int i=0; i<iMirrorCount; ++i) { VisMirror_cl *pMirror = m_Instances.GetAt(i); if (!pMirror->IsActive()) { continue; } if (data.m_pRendererNode == NULL) { continue; } VisRenderContext_cl *pRefContext = data.m_pRendererNode->GetReferenceContext(); if (pRefContext==NULL) // This should not happen unless the node is in invalid (= de-initialized) state continue; if ((pRefContext->GetRenderFlags() & (VIS_RENDERCONTEXT_FLAG_NOMIRRORS|VIS_RENDERCONTEXT_FLAG_NO_WORLDGEOM))) continue; if ((pRefContext->GetRenderFlags() & VIS_RENDERCONTEXT_FLAG_VIEWCONTEXT)==0) continue; VASSERT(data.m_pRendererNode->IsContextRegistered(pMirror->m_spReflectionContext)); pMirror->HandleMirror(data); } return; } if (pData->m_pSender==&Vision::Callbacks.OnWorldDeInit) { int iMirrorCount = m_Instances.Count(); for (int i=iMirrorCount-1;i>=0;i--) // Backwards to keep collection intact { VisMirror_cl *pMirror = m_Instances.GetAt(i); pMirror->ClearViewVisibilityCollectors(); pMirror->DisposeObject(); } m_Instances.Clear(); return; } if (pData->m_pSender==&Vision::Callbacks.OnEnterBackground) { const int iMirrorCount = m_Instances.Count(); for (int i=0; i<iMirrorCount; ++i) { m_Instances.GetAt(i)->ClearViewVisibilityCollectors(); } return; } // We only need to respond to this callback outside the editor, because vForge has its own re-assignment callback. // also note that Vision::Callbacks.OnReassignShaders is not triggered during runtime if (pData->m_pSender==&Vision::Callbacks.OnReassignShaders && !Vision::Editor.IsInEditor()) { const int iMirrorCount = m_Instances.Count(); for (int i=0; i<iMirrorCount; ++i) { m_Instances.GetAt(i)->ReassignEffect(); } return; } }
//Update, render and display the scene bool VisionApp_cl::Run() { static bool bInsideGameLoop = false; // Make sure the game loop isn't executed recursively. In Windows builds, this could for example happen if a shown message box triggers // a repaint message. This case needs to be handled by the code calling the game loop. if(bInsideGameLoop) { // We can't report this error in a form that could trigger a message box, so reporting a warning and breaking the debugger is the best we can do. Vision::Error.Warning("VisionApp_cl::Run called recursively! This is usually caused by triggering a repaint from inside the game loop."); #if defined(HK_DEBUG) VDBGBREAK; #endif // Just skip the game loop - this may invoke weird behavior if the calling code expects the game loop to complete, but is better // than recursing return true; } bInsideGameLoop = true; //Update the scene m_iUpdateSceneTickCount = 1; // by default one simulation tick per loop if (m_spUpdateSceneController!=NULL) m_iUpdateSceneTickCount = m_spUpdateSceneController->GetUpdateTickCount(); for (int i=0;i<m_iUpdateSceneTickCount;i++) { OnUpdateScene(); if (i<m_iUpdateSceneTickCount-1) // the last one is performed after rendering { OnFinishScene(); UpdateTimer(); } } // update everything that has to be done once per loop rather than per simulation steps OnFrameUpdatePreRender(); Vision::Profiling.Update(); VASSERT_MSG(Vision::Renderer.GetRendererNode(0) != NULL, "No renderer node is set. This isn't supported anymore. Use a VSimpleRendererNode instead of registering the main context globally."); // If in debug build, perform a sanity check - no context registered with the main context should also be registered // with a renderer node! #ifdef HK_DEBUG_SLOW static bool bContextErrorShown = false; if (!bContextErrorShown) { int iContextCount = Vision::Contexts.GetContextCount(); for (int iContext = 0; iContext < iContextCount; iContext++) { for (int iRendererNode=0; iRendererNode<V_MAX_RENDERER_NODES; iRendererNode++) { IVRendererNode *pNode = Vision::Renderer.GetRendererNode(iRendererNode); VisRenderContext_cl* pContext = Vision::Contexts.GetContext(iContext); if (pNode != NULL && pNode->IsContextRegistered(Vision::Contexts.GetContext(iContext))) { Vision::Error.Warning("Context %s (%p) is registered globally AND in renderer node %s (%p). This may be intended, but it is most likely a porting issue introduced by porting from a pre-8.0 version of the Vision Engine.", pContext->GetName(), pContext, pNode->GetTypeId()->m_lpszClassName, pNode); bContextErrorShown = true; } } } } #endif { INSERT_PERF_MARKER_SCOPE("BeginRendering"); // Inform the renderer that we are now going to start rendering Vision::Renderer.BeginRendering(); Vision::Callbacks.BeginRendering.TriggerCallbacks(); } Vision::Renderer.SetCurrentRendererNode(NULL); VisRendererNodeDataObject_cl data(&Vision::Callbacks.OnRendererNodeSwitching, NULL); Vision::Callbacks.OnRendererNodeSwitching.TriggerCallbacks(&data); { INSERT_PERF_MARKER_SCOPE("PreRendererNodeContexts"); Vision::Contexts.PerformVisibilityTests(); Vision::Contexts.RenderContexts(-FLT_MAX, VIS_RENDERCONTEXTPRIORITY_SCENE); } { for (int iRendererNode=0; iRendererNode<V_MAX_RENDERER_NODES; iRendererNode++) { IVRendererNode *pNode = Vision::Renderer.GetRendererNode(iRendererNode); if (pNode != NULL && pNode->GetRenderingEnabled()) { char buffer[192]; sprintf(buffer, "RendererNode %d (%s)", iRendererNode, pNode->GetTypeId()->m_lpszClassName); INSERT_PERF_MARKER_SCOPE(buffer); VASSERT_MSG(pNode->IsInitialized(), "Renderer Node is registered and enabled, but not initialized"); pNode->Execute(); } } } { INSERT_PERF_MARKER_SCOPE("PostRendererNodeContexts"); Vision::Renderer.SetCurrentRendererNode(NULL); Vision::Contexts.RenderContexts(VIS_RENDERCONTEXTPRIORITY_SCENE, FLT_MAX); } { INSERT_PERF_MARKER_SCOPE("EndRendering"); // Tell the renderer that we have finished rendering Vision::Callbacks.EndRendering.TriggerCallbacks(); Vision::Renderer.EndRendering(); } //Finish the scene - the last tick is performed here if (m_iUpdateSceneTickCount>0) OnFinishScene(); // update everything that has to be done once per loop rather than per simulation steps OnFrameUpdatePostRender(); //Display the scene Vision::Callbacks.OnBeforeSwapBuffers.TriggerCallbacks(); #ifdef WIN32 if (m_bUpdateScreen) // only supported on win32 #endif Vision::Video.UpdateScreen(); if (m_iUpdateSceneTickCount>0) // same as for OnFinishScene UpdateTimer(); bInsideGameLoop = false; return !WantsToQuit(); }
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; }
void VMobileForwardRenderLoop::RenderLitGeometry(VisLightSource_cl *pLight, IVShadowMapComponent *pShadowMapComponent, bool bBasePass, bool bUsesLightClippingVolume, bool bEntities, bool bStaticGeometry) { if (!pLight) return; // Some local variables for storing surfaces, shaders, surface shaders, and the like. VCompiledTechnique *pTechnique = NULL; VisDrawCallInfo_t SurfaceShaderList[RLP_MAX_ENTITY_SURFACESHADERS]; VisRenderContext_cl *pContext = VisRenderContext_cl::GetCurrentContext(); const hkvVec3 &vCamPos = pContext->GetCamera()->GetPosition(); const float fFade = pLight->GetFadeWeight(vCamPos); VisStaticGeometryInstanceCollection_cl *pLitGeoInstanceCollection = NULL; VisEntityCollection_cl *pLitEntityCollection = NULL; if (bBasePass || pLight != m_pBasePassLight) { if (bStaticGeometry) { pLitGeoInstanceCollection = &s_LitGeoInstanceCollection; pLitGeoInstanceCollection->Clear(); } if (bEntities) { pLitEntityCollection = &s_LitEntityCollection; pLitEntityCollection->Clear(); } Vision::RenderLoopHelper.GetVisibleGeometryInLightsourceRange(pLitGeoInstanceCollection, pLitEntityCollection, NULL, *pLight); } else { if (bStaticGeometry) pLitGeoInstanceCollection = &m_AdditiveLitGeoInstanceCollection; if (bEntities) pLitEntityCollection = &m_AdditiveLitEntityCollection; } #ifdef SUPPORTS_SHADOW_MAPS VShadowMapGenSpotDir *pShadowMapGenDir = NULL; if (pShadowMapComponent) { VShadowMapGenerator *pShadowMapGen = pShadowMapComponent->GetShadowMapGenerator(); if (pShadowMapGen->GetProjectionType()==SHADOW_PROJECTION_ORTHOGRAPHIC) pShadowMapGenDir = static_cast<VShadowMapGenSpotDir*>(pShadowMapGen); } #endif // Set the stencil render state for reading light clipping volume information if(bUsesLightClippingVolume) { const VLightClippingVolumeComponent* pLightClippingComponent = pLight->Components().GetComponentOfBaseType<VLightClippingVolumeComponent>(); VASSERT(pLightClippingComponent != NULL && V_ARRAY_SIZE(m_lightClippingStencilStatesRead)==2); VisRenderStates_cl::SetDepthStencilState(m_lightClippingStencilStatesRead[pLightClippingComponent->GetClipHandedness()]); } else VisRenderStates_cl::SetDepthStencilState(m_dynLightDefaultState); // For all illuminated entities: Render a dynamic lighting pass now. if (pLight->GetLightInfluenceBitMaskEntity() != 0 && bEntities) { int iNumLitEntities = pLitEntityCollection->GetNumEntries(); Vision::RenderLoopHelper.BeginEntityRendering(); for (int j=0; j<iNumLitEntities; j++) { VisBaseEntity_cl *pEntity = pLitEntityCollection->GetEntry(j); if (!(pEntity->GetLightInfluenceBitMask() & pLight->GetLightInfluenceBitMaskEntity())) continue; VDynamicMesh *pMesh = pEntity->GetMesh(); VisSurface_cl **ppSurfaces = pEntity->GetSurfaceArray(); // Get list of all the surfaces in the model int iNumSubmeshes = pMesh->GetSubmeshCount(); int iNumSurfaceShaders = 0; // For all the surfaces... for (int k=0; k<iNumSubmeshes; k++) { VDynamicSubmesh *pSubmesh = pMesh->GetSubmesh(k); VisSurface_cl *pSurface = ppSurfaces[pSubmesh->m_iMaterialIndex]; // Full-bright surfaces can't be rendered in the compound base pass, since such surfaces are not illuminated. Since tagging // of already rendered geometry happens per entity instance, in case an entity contains full-bright surfaces, all surfaces // of this entity have to be rendered in the normal rendering pipeline. const VisLightingMethod_e eLightingMethod = pSurface->GetLightingMode(); if (bBasePass) { // Since entities can contain several surfaces with different lighting methods (full-bright, dynamic only, etc.), entities // with full-bright surfaces have to be also added to the additive lit entity collection, in order to ensure rendering of // dynamic only surfaces. if (eLightingMethod==VIS_LIGHTING_FULLBRIGHT) { iNumSurfaceShaders = 0; m_AdditiveLitEntityCollection.AppendEntry(pEntity); break; } } else { if (eLightingMethod == VIS_LIGHTING_FULLBRIGHT) continue; } // If not all surfaces have a primary opaque pass type in the base pass, then render corresponding entity // in the additive lighting pass. if (bBasePass && pSurface->GetResolvedPassType()!=VPT_PrimaryOpaquePass) { iNumSurfaceShaders = 0; m_AdditiveLitEntityCollection.AppendEntry(pEntity); break; } // Check whether entity is in current shadow volume for orthographic shadows. In that case the entity is rendered // without shadows since it is not in the relevant shadow volume. IVShadowMapComponent *pTmpShadowMapComponent = pShadowMapComponent; #ifdef SUPPORTS_SHADOW_MAPS if (pShadowMapGenDir) { if (!pShadowMapGenDir->IsEntityInsideOrthoShadowVolume(pEntity)) pTmpShadowMapComponent = NULL; } #endif pTechnique = GetLightShader(pLight, bBasePass, pSurface, fFade, pTmpShadowMapComponent); if (!pTechnique || !pTechnique->GetShaderCount()) // do not light this surface { // If base-pass lighting technique could not be retrieved, render lit entity in the additive pass. if (bBasePass && pLight->IsDynamic()) m_AdditiveLitEntityCollection.AppendEntry(pEntity); iNumSurfaceShaders = 0; break; } // Generate a list of surface shader from the combined surface/shader information VisDrawCallInfo_t &info(SurfaceShaderList[iNumSurfaceShaders++]); info.Set(pSubmesh, pSurface, pTechnique->m_Shaders.GetAt(0)); } // Finally, render the entity with a surface shader list. if (iNumSurfaceShaders>0) { Vision::RenderLoopHelper.RenderEntityWithSurfaceShaderList(pEntity, iNumSurfaceShaders, SurfaceShaderList); if (bBasePass) pEntity->Tag(); } } Vision::RenderLoopHelper.EndEntityRendering(); } // For all illuminated world primitives: Render a dynamic lighting pass now. if (pLight->GetLightInfluenceBitMaskWorld() != 0 && bStaticGeometry) { VisSurface_cl *pSurface; VisSurface_cl *pLastSurface = NULL; VCompiledTechnique *pLastTechnique = NULL; // We start collecting illuminated mesh instances. Whenever a relevant property changes, we set the // shader information, render all collected world instances, and start collecting from scratch. int iNumLitGeoInstances = pLitGeoInstanceCollection->GetNumEntries(); pLastSurface = NULL; s_RenderGeoInstanceCollection.Clear(); pLastTechnique = NULL; for (int j=0; j<iNumLitGeoInstances; j++) { VisStaticGeometryInstance_cl *pGI = pLitGeoInstanceCollection->GetEntry(j); pSurface = pGI->GetSurface(); VASSERT(pSurface); if (pSurface->IsFullbright()) continue; // If surface does not have a primary opaque pass type in the base pass, then render corresponding static // mesh instance in the additive lighting pass. if (bBasePass && pSurface->GetResolvedPassType()!=VPT_PrimaryOpaquePass) { m_AdditiveLitGeoInstanceCollection.AppendEntry(pGI); continue; } // Check whether mesh is in current shadow volume for orthographic shadows. In that case the mesh is rendered // without shadows since it is not in the relevant shadow volume. IVShadowMapComponent *pTmpShadowMapComponent = pShadowMapComponent; #ifdef SUPPORTS_SHADOW_MAPS if (pShadowMapGenDir) { if (!pShadowMapGenDir->IsMeshInsideOrthoShadowVolume(pGI)) pTmpShadowMapComponent = NULL; } #endif if (pLastSurface!=pSurface) { pTechnique = GetLightShader(pLight, bBasePass, pSurface, fFade, pTmpShadowMapComponent); pLastSurface = pSurface; } if (pTechnique == NULL || !pTechnique->GetShaderCount()) { // If base-pass lighting technique could not be retrieved, render lit mesh in the additive pass. if (bBasePass && pLight->IsDynamic()) m_AdditiveLitGeoInstanceCollection.AppendEntry(pGI); continue; } // If the state information is different from the previous one, we have to render the world primitives we // have collected so far if (pLastTechnique!=pTechnique) { if (s_RenderGeoInstanceCollection.GetNumEntries()!=0) { VASSERT(pLastTechnique != NULL); Vision::RenderLoopHelper.RenderStaticGeometryWithShader(s_RenderGeoInstanceCollection, *pLastTechnique->m_Shaders.GetAt(0)); if (bBasePass) s_RenderGeoInstanceCollection.TagEntries(); s_RenderGeoInstanceCollection.Clear(); } // Update the stored state information pLastTechnique = pTechnique; } // Naturally, we have to append the primitive to our collection (otherwise it won't be collected =) ). s_RenderGeoInstanceCollection.AppendEntry(pGI); } // If there's still something left in the collection, render it as well. if (s_RenderGeoInstanceCollection.GetNumEntries()!=0) { if (pLastTechnique && pLastTechnique->GetShaderCount()>0) { Vision::RenderLoopHelper.RenderStaticGeometryWithShader(s_RenderGeoInstanceCollection, *pLastTechnique->m_Shaders.GetAt(0)); if (bBasePass) s_RenderGeoInstanceCollection.TagEntries(); } s_RenderGeoInstanceCollection.Clear(); } } // Restore default render state VisRenderStates_cl::SetDepthStencilState(*VisRenderStates_cl::GetDepthStencilDefaultState()); }
/// update all internal data ////////////////////////////////////////////////////////////////////////// bool CScaler::UpdateInternalsAndGetVisibility() { VisRenderContext_cl* pCtx = Vision::Contexts.GetCurrentContext(); const VisMatrix4x4_cl& mtxProjection = pCtx->GetProjectionMatrix(); VisContextCamera_cl* pContextCam = pCtx->GetCamera(); pContextCam->GetWorldMatrix( m_mtxViewProjection ); // it`s View mtx and not all cells are filled?!?, fix below // ogl View to dx View mtx crap float* pGlmatrix = m_mtxViewProjection.GetFloatPtr(); pGlmatrix[ 2 ] = -pGlmatrix[ 2 ]; pGlmatrix[ 6 ] = -pGlmatrix[ 6 ]; pGlmatrix[ 10 ] = -pGlmatrix[ 10 ]; pGlmatrix[ 14 ] = -pGlmatrix[ 14 ]; pGlmatrix[ 15 ] = 1.f; //this must be here, GetWorldMatrix DOES NOT fill all cells !!! // multiply is now DX style (ogl is projection*view*world) m_mtxViewProjection *= mtxProjection; // get transpose mtx of ViewProjection m_mtxViewProjectionT = m_mtxViewProjection; m_mtxViewProjectionT.Transpose(); // gen VPInverse mtx (if sended to shader, must be transposed) m_mtxViewProjectionInv = m_mtxViewProjection; m_mtxViewProjectionInv.InvertEx(); // calculate -far & far plane vectors (homogeneous space) m_NearPlane.v1.SetXYZW( -1, 1, 0, 1 ); m_NearPlane.v2.SetXYZW( 1, 1, 0, 1 ); m_NearPlane.v3.SetXYZW( 1, -1, 0, 1 ); m_NearPlane.v4.SetXYZW( -1, -1, 0, 1 ); m_FarPlane.v1.SetXYZW( -1, 1, 1, 1 ); m_FarPlane.v2.SetXYZW( 1, 1, 1, 1 ); m_FarPlane.v3.SetXYZW( 1, -1, 1, 1 ); m_FarPlane.v4.SetXYZW( -1, -1, 1, 1 ); // w are all ones so Vision may be used m_NearPlane.v1 *= m_mtxViewProjectionInv; m_NearPlane.v2 *= m_mtxViewProjectionInv; m_NearPlane.v3 *= m_mtxViewProjectionInv; m_NearPlane.v4 *= m_mtxViewProjectionInv; m_FarPlane.v1 *= m_mtxViewProjectionInv; m_FarPlane.v2 *= m_mtxViewProjectionInv; m_FarPlane.v3 *= m_mtxViewProjectionInv; m_FarPlane.v4 *= m_mtxViewProjectionInv; _GetWorldPos( m_NearPlane, m_WorldNearPlane ); _GetWorldPos( m_FarPlane, m_WorldFarPlane ); GetIntersectionsAndPointsBeetween(); // will fill m_aIntesections & counter // skip if can`t generate proper matrix ( volume is outside of frustum ) if( m_iInterectionsCount >= 4 ) // valid { // get clip space coords to calculate XY span for( int i=0; i<m_iInterectionsCount; i++ ) { //m_aIntersections[ i ].w = 1.f; // Vision use w as 1 so don`t set m_aIntersections[ i ].z = 0.f; // TODO: should be water level m_aIntersections[ i ] *= m_mtxViewProjectionT; // use transposed mtx m_aIntersections[ i ].x /= m_aIntersections[ i ].w; m_aIntersections[ i ].y /= m_aIntersections[ i ].w; //m_aIntersections[ i ].z /= m_aIntersections[ i ].w; // we dont need Z } float fXMin = FLT_MAX; float fXMax = -FLT_MAX; float fYMin = FLT_MAX; float fYMax = -FLT_MAX; for( int i=0; i<m_iInterectionsCount; i++ ) { if( m_aIntersections[ i ].x > fXMax ) fXMax = m_aIntersections[ i ].x; if ( m_aIntersections[ i ].x < fXMin ) fXMin = m_aIntersections[ i ].x; if( m_aIntersections[ i ].y > fYMax ) fYMax = m_aIntersections[ i ].y; if ( m_aIntersections[ i ].y < fYMin ) fYMin = m_aIntersections[ i ].y; } // build scaling matrix. it prevents h-space verts in shader to run out of range /* normal way VisMatrix4x4_cl mtxScale; mtxScale.Identity(); mtxScale[ 0 ] = fXMax - fXMin; mtxScale[ 5 ] = fYMax - fYMin; mtxScale[ 12 ] = fXMin; mtxScale[ 13 ] = fYMin; mtxScale.Transpose(); */ // faster way m_mtxScale[ 0 ] = fXMax - fXMin; m_mtxScale[ 5 ] = fYMax - fYMin; m_mtxScale[ 3 ] = fXMin; m_mtxScale[ 7 ] = fYMin; m_mtxViewProjectionInv *= m_mtxScale; m_NearPlane.v1.SetXYZW( 0, 1, 0, 1 ); m_NearPlane.v2.SetXYZW( 1, 1, 0, 1 ); m_NearPlane.v3.SetXYZW( 1, 0, 0, 1 ); m_NearPlane.v4.SetXYZW( 0, 0, 0, 1 ); m_FarPlane.v1.SetXYZW( 0, 1, 1, 1 ); m_FarPlane.v2.SetXYZW( 1, 1, 1, 1 ); m_FarPlane.v3.SetXYZW( 1, 0, 1, 1 ); m_FarPlane.v4.SetXYZW( 0, 0, 1, 1 ); m_NearPlane.v1 *= m_mtxViewProjectionInv; m_NearPlane.v2 *= m_mtxViewProjectionInv; m_NearPlane.v3 *= m_mtxViewProjectionInv; m_NearPlane.v4 *= m_mtxViewProjectionInv; m_FarPlane.v1 *= m_mtxViewProjectionInv; m_FarPlane.v2 *= m_mtxViewProjectionInv; m_FarPlane.v3 *= m_mtxViewProjectionInv; m_FarPlane.v4 *= m_mtxViewProjectionInv; return true; }else { // do not render return false; } }
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); }