// 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 #include #include namespace anki { const FrustumComponentVisibilityTestFlag FRUSTUM_TEST_FLAGS = FrustumComponentVisibilityTestFlag::RENDER_COMPONENTS | FrustumComponentVisibilityTestFlag::LIGHT_COMPONENTS | FrustumComponentVisibilityTestFlag::DIRECTIONAL_LIGHT_SHADOWS_1_CASCADE; /// Feedback component class ReflectionProbeNode::MoveFeedbackComponent : public SceneComponent { ANKI_SCENE_COMPONENT(ReflectionProbeNode::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 ReflectionProbeNode& dnode = static_cast(node); dnode.onMoveUpdate(move); } return Error::NONE; } }; ANKI_SCENE_COMPONENT_STATICS(ReflectionProbeNode::MoveFeedbackComponent) /// Feedback component class ReflectionProbeNode::ShapeFeedbackComponent : public SceneComponent { ANKI_SCENE_COMPONENT(ReflectionProbeNode::ShapeFeedbackComponent) public: ShapeFeedbackComponent(SceneNode* node) : SceneComponent(node, getStaticClassId(), true) { } Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override { updated = false; ReflectionProbeComponent& reflc = node.getFirstComponentOfType(); if(reflc.getTimestamp() == node.getGlobalTimestamp()) { ReflectionProbeNode& dnode = static_cast(node); dnode.onShapeUpdate(reflc); } return Error::NONE; } }; ANKI_SCENE_COMPONENT_STATICS(ReflectionProbeNode::ShapeFeedbackComponent) ReflectionProbeNode::ReflectionProbeNode(SceneGraph* scene, CString name) : SceneNode(scene, name) { // Move component first newComponent(); // Feedback component newComponent(); // The frustum components const F32 ang = toRad(90.0f); Mat3 rot; rot = Mat3(Euler(0.0f, -PI / 2.0f, 0.0f)) * Mat3(Euler(0.0f, 0.0f, PI)); m_frustumTransforms[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_frustumTransforms[1].setRotation(Mat3x4(Vec3(0.0f), rot)); rot = Mat3(Euler(PI / 2.0f, 0.0f, 0.0f)); m_frustumTransforms[2].setRotation(Mat3x4(Vec3(0.0f), rot)); rot = Mat3(Euler(-PI / 2.0f, 0.0f, 0.0f)); m_frustumTransforms[3].setRotation(Mat3x4(Vec3(0.0f), rot)); rot = Mat3(Euler(0.0f, PI, 0.0f)) * Mat3(Euler(0.0f, 0.0f, PI)); m_frustumTransforms[4].setRotation(Mat3x4(Vec3(0.0f), rot)); rot = Mat3(Euler(0.0f, 0.0f, PI)); m_frustumTransforms[5].setRotation(Mat3x4(Vec3(0.0f), rot)); for(U i = 0; i < 6; ++i) { m_frustumTransforms[i].setOrigin(Vec4(0.0f)); m_frustumTransforms[i].setScale(1.0f); FrustumComponent* frc = newComponent(); frc->setFrustumType(FrustumType::PERSPECTIVE); frc->setPerspective(CLUSTER_OBJECT_FRUSTUM_NEAR_PLANE, 10.0f, ang, ang); frc->setWorldTransform(m_frustumTransforms[i]); frc->setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::NONE); frc->setEffectiveShadowDistance(getSceneGraph().getConfig().m_reflectionProbeShadowEffectiveDistance); } // Reflection probe comp newComponent(); // Feedback newComponent(); // Spatial component SpatialComponent* spatialc = newComponent(); spatialc->setUpdateOctreeBounds(false); } ReflectionProbeNode::~ReflectionProbeNode() { } void ReflectionProbeNode::onMoveUpdate(MoveComponent& move) { // Update frustum components U count = 0; iterateComponentsOfType([&](FrustumComponent& frc) { Transform trf = m_frustumTransforms[count]; trf.setOrigin(move.getWorldTransform().getOrigin()); frc.setWorldTransform(trf); ++count; }); ANKI_ASSERT(count == 6); // Update the spatial comp SpatialComponent& sp = getFirstComponentOfType(); sp.setSpatialOrigin(move.getWorldTransform().getOrigin().xyz()); // Update the refl comp ReflectionProbeComponent& reflc = getFirstComponentOfType(); reflc.setWorldPosition(move.getWorldTransform().getOrigin().xyz()); } void ReflectionProbeNode::onShapeUpdate(ReflectionProbeComponent& reflc) { const Vec3 halfProbeSize = reflc.getBoxVolumeSize() / 2.0f; F32 effectiveDistance = max(halfProbeSize.x(), halfProbeSize.y()); effectiveDistance = max(effectiveDistance, halfProbeSize.z()); effectiveDistance = max(effectiveDistance, getSceneGraph().getConfig().m_reflectionProbeEffectiveDistance); // Update frustum components iterateComponentsOfType([&](FrustumComponent& frc) { frc.setFar(effectiveDistance); }); // Update the spatial comp SpatialComponent& sp = getFirstComponentOfType(); sp.setAabbWorldSpace(reflc.getAabbWorldSpace()); } Error ReflectionProbeNode::frameUpdate(Second prevUpdateTime, Second crntTime) { // Check the reflection probe component and if it's marked for rendering enable the frustum components const ReflectionProbeComponent& reflc = getFirstComponentOfType(); const FrustumComponentVisibilityTestFlag testFlags = reflc.getMarkedForRendering() ? FRUSTUM_TEST_FLAGS : FrustumComponentVisibilityTestFlag::NONE; iterateComponentsOfType([testFlags](FrustumComponent& frc) { frc.setEnabledVisibilityTests(testFlags); }); return Error::NONE; } } // end namespace anki