// Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors. // All rights reserved. // Code licensed under the BSD License. // http://www.anki3d.org/LICENSE #include #include #include #include #include #include namespace anki { constexpr FrustumComponentVisibilityTestFlag FRUSTUM_TEST_FLAGS = FrustumComponentVisibilityTestFlag::RENDER_COMPONENTS | FrustumComponentVisibilityTestFlag::LIGHT_COMPONENTS | FrustumComponentVisibilityTestFlag::DIRECTIONAL_LIGHT_SHADOWS_1_CASCADE; /// Feedback component class GlobalIlluminationProbeNode::MoveFeedbackComponent : public SceneComponent { ANKI_SCENE_COMPONENT(GlobalIlluminationProbeNode::MoveFeedbackComponent) public: MoveFeedbackComponent(SceneNode* node) : SceneComponent(node, getStaticClassId(), true) { } Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override { updated = false; MoveComponent& move = node.getFirstComponentOfType(); if(move.getTimestamp() == node.getGlobalTimestamp()) { // Move updated GlobalIlluminationProbeNode& dnode = static_cast(node); dnode.onMoveUpdate(move); } return Error::NONE; } }; ANKI_SCENE_COMPONENT_STATICS(GlobalIlluminationProbeNode::MoveFeedbackComponent) /// Feedback component class GlobalIlluminationProbeNode::ShapeFeedbackComponent : public SceneComponent { ANKI_SCENE_COMPONENT(GlobalIlluminationProbeNode::ShapeFeedbackComponent) public: ShapeFeedbackComponent(SceneNode* node) : SceneComponent(node, getStaticClassId(), false // Not feedback component. Can't be skipped because of getMarkedForRendering() ) { } Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override { updated = false; GlobalIlluminationProbeComponent& probec = node.getFirstComponentOfType(); if(probec.getTimestamp() == node.getGlobalTimestamp() || probec.getMarkedForRendering()) { // Move updated GlobalIlluminationProbeNode& dnode = static_cast(node); dnode.onShapeUpdateOrProbeNeedsRendering(); } return Error::NONE; } }; ANKI_SCENE_COMPONENT_STATICS(GlobalIlluminationProbeNode::ShapeFeedbackComponent) GlobalIlluminationProbeNode::GlobalIlluminationProbeNode(SceneGraph* scene, CString name) : SceneNode(scene, name) { // Move component first newComponent(); // Feedback component newComponent(); // GI probe comp newComponent(); // Second feedback component newComponent(); // The frustum components constexpr F32 ang = toRad(90.0f); const F32 zNear = CLUSTER_OBJECT_FRUSTUM_NEAR_PLANE; Mat3 rot; rot = Mat3(Euler(0.0f, -PI / 2.0f, 0.0f)) * Mat3(Euler(0.0f, 0.0f, PI)); m_cubeFaceTransforms[0].setRotation(Mat3x4(Vec3(0.0f), rot)); rot = Mat3(Euler(0.0f, PI / 2.0f, 0.0f)) * Mat3(Euler(0.0f, 0.0f, PI)); m_cubeFaceTransforms[1].setRotation(Mat3x4(Vec3(0.0f), rot)); rot = Mat3(Euler(PI / 2.0f, 0.0f, 0.0f)); m_cubeFaceTransforms[2].setRotation(Mat3x4(Vec3(0.0f), rot)); rot = Mat3(Euler(-PI / 2.0f, 0.0f, 0.0f)); m_cubeFaceTransforms[3].setRotation(Mat3x4(Vec3(0.0f), rot)); rot = Mat3(Euler(0.0f, PI, 0.0f)) * Mat3(Euler(0.0f, 0.0f, PI)); m_cubeFaceTransforms[4].setRotation(Mat3x4(Vec3(0.0f), rot)); rot = Mat3(Euler(0.0f, 0.0f, PI)); m_cubeFaceTransforms[5].setRotation(Mat3x4(Vec3(0.0f), rot)); for(U i = 0; i < 6; ++i) { m_cubeFaceTransforms[i].setOrigin(Vec4(0.0f)); m_cubeFaceTransforms[i].setScale(1.0f); FrustumComponent* frc = newComponent(); frc->setFrustumType(FrustumType::PERSPECTIVE); const F32 tempEffectiveDistance = 1.0f; frc->setPerspective(zNear, tempEffectiveDistance, ang, ang); frc->setWorldTransform(m_cubeFaceTransforms[i]); frc->setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::NONE); frc->setEffectiveShadowDistance(getSceneGraph().getConfig().m_reflectionProbeShadowEffectiveDistance); } // Spatial component SpatialComponent* spatialc = newComponent(); spatialc->setUpdateOctreeBounds(false); } GlobalIlluminationProbeNode::~GlobalIlluminationProbeNode() { } void GlobalIlluminationProbeNode::onMoveUpdate(MoveComponent& move) { GlobalIlluminationProbeComponent& gic = getFirstComponentOfType(); gic.setWorldPosition(move.getWorldTransform().getOrigin().xyz()); SpatialComponent& sp = getFirstComponentOfType(); sp.setSpatialOrigin(move.getWorldTransform().getOrigin().xyz()); } void GlobalIlluminationProbeNode::onShapeUpdateOrProbeNeedsRendering() { GlobalIlluminationProbeComponent& gic = getFirstComponentOfType(); // Update the frustum component if the shape needs rendering if(gic.getMarkedForRendering()) { // Compute effective distance F32 effectiveDistance = max(gic.getBoxVolumeSize().x(), gic.getBoxVolumeSize().y()); effectiveDistance = max(effectiveDistance, gic.getBoxVolumeSize().z()); effectiveDistance = max(effectiveDistance, getSceneGraph().getConfig().m_reflectionProbeEffectiveDistance); // Update frustum components U count = 0; iterateComponentsOfType([&](FrustumComponent& frc) { Transform trf = m_cubeFaceTransforms[count]; trf.setOrigin(gic.getRenderPosition().xyz0()); frc.setWorldTransform(trf); frc.setFar(effectiveDistance); ++count; }); ANKI_ASSERT(count == 6); } // Update the spatial comp const Bool shapeWasUpdated = gic.getTimestamp() == getGlobalTimestamp(); if(shapeWasUpdated) { // Update only when the shape was actually update SpatialComponent& sp = getFirstComponentOfType(); sp.setAabbWorldSpace(gic.getAabbWorldSpace()); } } Error GlobalIlluminationProbeNode::frameUpdate(Second prevUpdateTime, Second crntTime) { // Check the reflection probe component and if it's marked for rendering enable the frustum components const GlobalIlluminationProbeComponent& gic = getFirstComponentOfType(); const FrustumComponentVisibilityTestFlag testFlags = (gic.getMarkedForRendering()) ? FRUSTUM_TEST_FLAGS : FrustumComponentVisibilityTestFlag::NONE; iterateComponentsOfType([testFlags](FrustumComponent& frc) { frc.setEnabledVisibilityTests(testFlags); }); return Error::NONE; } } // end namespace anki