// 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 namespace anki { /// Feedback component. class LightNode::OnMovedFeedbackComponent : public SceneComponent { ANKI_SCENE_COMPONENT(LightNode::OnMovedFeedbackComponent) public: OnMovedFeedbackComponent(SceneNode* node) : SceneComponent(node, getStaticClassId(), true) { } Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override { updated = false; const MoveComponent& move = node.getComponentAt(0); if(move.getTimestamp() == node.getGlobalTimestamp()) { // Move updated static_cast(node).onMoved(move); } return Error::NONE; } }; ANKI_SCENE_COMPONENT_STATICS(LightNode::OnMovedFeedbackComponent) /// Feedback component. class LightNode::OnLightShapeUpdatedFeedbackComponent : public SceneComponent { ANKI_SCENE_COMPONENT(LightNode::OnLightShapeUpdatedFeedbackComponent) public: OnLightShapeUpdatedFeedbackComponent(SceneNode* node) : SceneComponent(node, getStaticClassId(), true) { } Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override { updated = false; LightComponent& light = node.getFirstComponentOfType(); if(light.getTimestamp() == node.getGlobalTimestamp()) { // Shape updated static_cast(node).onLightShapeUpdated(light); } return Error::NONE; } }; ANKI_SCENE_COMPONENT_STATICS(LightNode::OnLightShapeUpdatedFeedbackComponent) LightNode::LightNode(SceneGraph* scene, CString name) : SceneNode(scene, name) { } LightNode::~LightNode() { } void LightNode::frameUpdateCommon() { // Update frustum comps shadow info const LightComponent& lc = getFirstComponentOfType(); const Bool castsShadow = lc.getShadowEnabled(); iterateComponentsOfType([&](FrustumComponent& frc) { if(castsShadow) { frc.setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::SHADOW_CASTERS); } else { frc.setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::NONE); } }); } void LightNode::onMoveUpdateCommon(const MoveComponent& move) { // Update the spatial SpatialComponent& sp = getFirstComponentOfType(); sp.setSpatialOrigin(move.getWorldTransform().getOrigin().xyz()); // Update the lens flare LensFlareComponent& lf = getFirstComponentOfType(); lf.setWorldPosition(move.getWorldTransform().getOrigin().xyz()); // Update light component getFirstComponentOfType().setWorldTransform(move.getWorldTransform()); } PointLightNode::PointLightNode(SceneGraph* scene, CString name) : LightNode(scene, name) { newComponent(); newComponent(); LightComponent* lc = newComponent(); lc->setLightComponentType(LightComponentType::POINT); newComponent(); newComponent(); newComponent(); } PointLightNode::~PointLightNode() { m_shadowData.destroy(getAllocator()); } void PointLightNode::onMoved(const MoveComponent& move) { onMoveUpdateCommon(move); // Update the frustums U32 count = 0; iterateComponentsOfType([&](FrustumComponent& fr) { Transform trf = m_shadowData[count].m_localTrf; trf.setOrigin(move.getWorldTransform().getOrigin()); fr.setWorldTransform(trf); ++count; }); } void PointLightNode::onLightShapeUpdated(LightComponent& light) { iterateComponentsOfType([&](FrustumComponent& fr) { fr.setFar(light.getRadius()); }); SpatialComponent& spatialc = getFirstComponentOfType(); spatialc.setSphereWorldSpace(Sphere(light.getWorldTransform().getOrigin(), light.getRadius())); } Error PointLightNode::frameUpdate(Second prevUpdateTime, Second crntTime) { // Lazily init const LightComponent& lightc = getFirstComponentOfType(); if(lightc.getShadowEnabled() && m_shadowData.isEmpty()) { m_shadowData.create(getAllocator(), 6); const F32 ang = toRad(90.0f); const F32 dist = lightc.getRadius(); const F32 zNear = CLUSTER_OBJECT_FRUSTUM_NEAR_PLANE; Mat3 rot; rot = Mat3(Euler(0.0, -PI / 2.0, 0.0)) * Mat3(Euler(0.0, 0.0, PI)); m_shadowData[0].m_localTrf.setRotation(Mat3x4(Vec3(0.0f), rot)); rot = Mat3(Euler(0.0, PI / 2.0, 0.0)) * Mat3(Euler(0.0, 0.0, PI)); m_shadowData[1].m_localTrf.setRotation(Mat3x4(Vec3(0.0f), rot)); rot = Mat3(Euler(PI / 2.0, 0.0, 0.0)); m_shadowData[2].m_localTrf.setRotation(Mat3x4(Vec3(0.0f), rot)); rot = Mat3(Euler(-PI / 2.0, 0.0, 0.0)); m_shadowData[3].m_localTrf.setRotation(Mat3x4(Vec3(0.0f), rot)); rot = Mat3(Euler(0.0, PI, 0.0)) * Mat3(Euler(0.0, 0.0, PI)); m_shadowData[4].m_localTrf.setRotation(Mat3x4(Vec3(0.0f), rot)); rot = Mat3(Euler(0.0, 0.0, PI)); m_shadowData[5].m_localTrf.setRotation(Mat3x4(Vec3(0.0f), rot)); const Vec4& origin = getFirstComponentOfType().getWorldTransform().getOrigin(); for(U32 i = 0; i < 6; i++) { Transform trf = m_shadowData[i].m_localTrf; trf.setOrigin(origin); FrustumComponent* frc = newComponent(); frc->setFrustumType(FrustumType::PERSPECTIVE); frc->setPerspective(zNear, dist, ang, ang); frc->setWorldTransform(trf); } } frameUpdateCommon(); return Error::NONE; } class SpotLightNode::OnFrustumUpdatedFeedbackComponent : public SceneComponent { ANKI_SCENE_COMPONENT(SpotLightNode::OnFrustumUpdatedFeedbackComponent) public: OnFrustumUpdatedFeedbackComponent(SceneNode* node) : SceneComponent(node, getStaticClassId(), true) { } Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override { updated = false; FrustumComponent& frc = node.getFirstComponentOfType(); if(frc.getTimestamp() == node.getGlobalTimestamp()) { // Shape updated static_cast(node).onFrustumUpdated(frc); } return Error::NONE; } }; ANKI_SCENE_COMPONENT_STATICS(SpotLightNode::OnFrustumUpdatedFeedbackComponent) SpotLightNode::SpotLightNode(SceneGraph* scene, CString name) : LightNode(scene, name) { newComponent(); newComponent(); LightComponent* lc = newComponent(); lc->setLightComponentType(LightComponentType::SPOT); newComponent(); newComponent(); FrustumComponent* fr = newComponent(); fr->setFrustumType(FrustumType::PERSPECTIVE); fr->setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::NONE); newComponent(); newComponent(); } void SpotLightNode::onMoved(const MoveComponent& move) { // Update the frustums iterateComponentsOfType([&](FrustumComponent& fr) { fr.setWorldTransform(move.getWorldTransform()); }); onMoveUpdateCommon(move); } void SpotLightNode::onLightShapeUpdated(LightComponent& light) { FrustumComponent& frc = getFirstComponentOfType(); frc.setPerspective(CLUSTER_OBJECT_FRUSTUM_NEAR_PLANE, light.getDistance(), light.getOuterAngle(), light.getOuterAngle()); } void SpotLightNode::onFrustumUpdated(FrustumComponent& frc) { SpatialComponent& sp = getFirstComponentOfType(); sp.setConvexHullWorldSpace(frc.getPerspectiveBoundingShapeWorldSpace()); } Error SpotLightNode::frameUpdate(Second prevUpdateTime, Second crntTime) { frameUpdateCommon(); return Error::NONE; } class DirectionalLightNode::FeedbackComponent : public SceneComponent { ANKI_SCENE_COMPONENT(DirectionalLightNode::FeedbackComponent) public: FeedbackComponent(SceneNode* node) : SceneComponent(node, getStaticClassId(), true) { } Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override { const MoveComponent& move = node.getComponentAt(0); if(move.getTimestamp() == node.getGlobalTimestamp()) { // Move updated LightComponent& lightc = node.getFirstComponentOfType(); lightc.setWorldTransform(move.getWorldTransform()); SpatialComponent& spatialc = node.getFirstComponentOfType(); spatialc.setSpatialOrigin(move.getWorldTransform().getOrigin().xyz()); } return Error::NONE; } }; ANKI_SCENE_COMPONENT_STATICS(DirectionalLightNode::FeedbackComponent) DirectionalLightNode::DirectionalLightNode(SceneGraph* scene, CString name) : SceneNode(scene, name) { newComponent(); newComponent(); LightComponent* lc = newComponent(); lc->setLightComponentType(LightComponentType::DIRECTIONAL); SpatialComponent* spatialc = newComponent(); // Make the spatial always visible spatialc->setAlwaysVisible(true); } } // end namespace anki