You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
786 lines
37 KiB
C++
786 lines
37 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
|
|
#include "Visibility_precompiled.h"
|
|
#include "EditorPortalComponent.h"
|
|
#include "EditorPortalComponentMode.h"
|
|
|
|
#include <AzCore/RTTI/BehaviorContext.h>
|
|
|
|
// Include files needed for writing DisplayEntity functions that access the DisplayContext directly.
|
|
#include <EditorCoreAPI.h>
|
|
|
|
#include <AzCore/Math/Crc.h>
|
|
#include <AzCore/Math/IntersectSegment.h>
|
|
#include <AzToolsFramework/Viewport/VertexContainerDisplay.h>
|
|
#include <Editor/Objects/BaseObject.h>
|
|
#include <MathConversion.h>
|
|
|
|
namespace Visibility
|
|
{
|
|
void EditorPortalComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
|
{
|
|
provided.push_back(AZ_CRC("EditorPortalService", 0x6ead38f6));
|
|
provided.push_back(AZ_CRC("PortalService", 0x06076210));
|
|
provided.push_back(AZ_CRC("FixedVertexContainerService", 0x83f1bbf2));
|
|
}
|
|
|
|
void EditorPortalComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
|
{
|
|
required.push_back(AZ_CRC("TransformService", 0x8ee22c50));
|
|
}
|
|
|
|
void EditorPortalComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
|
|
{
|
|
dependent.push_back(AZ_CRC("QuadShapeService", 0xe449b0fc));
|
|
}
|
|
|
|
void EditorPortalComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
|
{
|
|
incompatible.push_back(AZ_CRC("SphereShapeService", 0x90c8dc80));
|
|
incompatible.push_back(AZ_CRC("SplineShapeService", 0x4d4b94a2));
|
|
incompatible.push_back(AZ_CRC("PolygonPrismShapeService", 0x1cbc4ed4));
|
|
incompatible.push_back(AZ_CRC("FixedVertexContainerService", 0x83f1bbf2));
|
|
}
|
|
|
|
void EditorPortalConfiguration::Reflect(AZ::ReflectContext* context)
|
|
{
|
|
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
|
{
|
|
serializeContext->Class<EditorPortalConfiguration, PortalConfiguration>()
|
|
->Version(1);
|
|
|
|
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
|
{
|
|
editContext->Class<EditorPortalConfiguration>("Portal Configuration", "")
|
|
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
|
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
|
->Attribute(AZ::Edit::Attributes::AutoExpand, true);
|
|
|
|
editContext->Class<PortalConfiguration>("Portal Configuration", "")
|
|
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
|
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
|
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
|
|
|
->DataElement(
|
|
AZ::Edit::UIHandlers::Default, &PortalConfiguration::m_height, "Height", "How tall the Portal is.")
|
|
->Attribute(AZ::Edit::Attributes::ChangeNotify, &PortalConfiguration::OnChange)
|
|
->DataElement(
|
|
AZ::Edit::UIHandlers::Default, &PortalConfiguration::m_displayFilled, "DisplayFilled", "Display the Portal as a filled volume.")
|
|
->Attribute(AZ::Edit::Attributes::ChangeNotify, &PortalConfiguration::OnChange)
|
|
->DataElement(
|
|
AZ::Edit::UIHandlers::Default, &PortalConfiguration::m_affectedBySun, "AffectedBySun", "Allows sunlight to affect objects inside the Portal.")
|
|
->Attribute(AZ::Edit::Attributes::ChangeNotify, &PortalConfiguration::OnChange)
|
|
->DataElement(
|
|
AZ::Edit::UIHandlers::Default, &PortalConfiguration::m_viewDistRatio, "ViewDistRatio", "Specifies how far the Portal is rendered.")
|
|
->Attribute(AZ::Edit::Attributes::Max, 100.000000)
|
|
->Attribute(AZ::Edit::Attributes::Min, 0.000000)
|
|
->Attribute(AZ::Edit::Attributes::ChangeNotify, &PortalConfiguration::OnChange)
|
|
->DataElement(
|
|
AZ::Edit::UIHandlers::Default, &PortalConfiguration::m_skyOnly, "SkyOnly", "Only the Sky Box will render when looking outside the Portal.")
|
|
->Attribute(AZ::Edit::Attributes::ChangeNotify, &PortalConfiguration::OnChange)
|
|
->DataElement(
|
|
AZ::Edit::UIHandlers::Default, &PortalConfiguration::m_oceanIsVisible, "OceanIsVisible", "Ocean will be visible when looking outside the Portal.")
|
|
->Attribute(AZ::Edit::Attributes::ChangeNotify, &PortalConfiguration::OnChange)
|
|
->DataElement(
|
|
AZ::Edit::UIHandlers::Default, &PortalConfiguration::m_useDeepness, "UseDeepness", "Portal will be treated as an object with volume rather than a plane.")
|
|
->Attribute(AZ::Edit::Attributes::ChangeNotify, &PortalConfiguration::OnChange)
|
|
->DataElement(
|
|
AZ::Edit::UIHandlers::Default, &PortalConfiguration::m_doubleSide, "DoubleSide", "Cameras will be able to look through the portal from both sides.")
|
|
->Attribute(AZ::Edit::Attributes::ChangeNotify, &PortalConfiguration::OnChange)
|
|
->DataElement(
|
|
AZ::Edit::UIHandlers::Default, &PortalConfiguration::m_lightBlending, "LightBlending", "Light from neighboring VisAreas will blend into the Portal.")
|
|
->Attribute(AZ::Edit::Attributes::ChangeNotify, &PortalConfiguration::OnChange)
|
|
->DataElement(
|
|
AZ::Edit::UIHandlers::Default, &PortalConfiguration::m_lightBlendValue, "LightBlendValue", "How much to blend lights from neighboring VisAreas.")
|
|
->Attribute(AZ::Edit::Attributes::Max, 1.000000)
|
|
->Attribute(AZ::Edit::Attributes::Min, 0.000000)
|
|
->Attribute(AZ::Edit::Attributes::ChangeNotify, &PortalConfiguration::OnChange)
|
|
->DataElement(
|
|
AZ::Edit::UIHandlers::Default, &PortalConfiguration::m_vertices, "Vertices", "Points that make up the floor of the Portal.")
|
|
->Attribute(AZ::Edit::Attributes::ChangeNotify, &PortalConfiguration::OnVerticesChange)
|
|
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
|
;
|
|
}
|
|
}
|
|
}
|
|
|
|
void EditorPortalComponent::Reflect(AZ::ReflectContext* context)
|
|
{
|
|
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
|
{
|
|
serializeContext->Class<EditorPortalComponent, AzToolsFramework::Components::EditorComponentBase>()
|
|
->Version(2)
|
|
->Field("m_config", &EditorPortalComponent::m_config)
|
|
->Field("ComponentMode", &EditorPortalComponent::m_componentModeDelegate)
|
|
;
|
|
|
|
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
|
{
|
|
editContext->Class<EditorPortalComponent>("Portal", "An area that describes a visibility portal between VisAreas.")
|
|
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
|
->Attribute(AZ::Edit::Attributes::Category, "Rendering")
|
|
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/Portal.png")
|
|
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
|
->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/Portal.svg")
|
|
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
|
->Attribute(AZ::Edit::Attributes::HelpPageURL, "http://docs.aws.amazon.com/console/lumberyard/userguide/portal-component")
|
|
->DataElement(AZ::Edit::UIHandlers::Default, &EditorPortalComponent::m_config, "m_config", "No Description")
|
|
->DataElement(AZ::Edit::UIHandlers::Default, &EditorPortalComponent::m_componentModeDelegate, "Component Mode", "Portal Component Mode")
|
|
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
|
;
|
|
}
|
|
}
|
|
|
|
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
|
{
|
|
behaviorContext->EBus<EditorPortalRequestBus>("EditorPortalRequestBus")
|
|
->Event("SetHeight", &EditorPortalRequestBus::Events::SetHeight)
|
|
->Event("GetHeight", &EditorPortalRequestBus::Events::GetHeight)
|
|
->VirtualProperty("Height", "GetHeight", "SetHeight")
|
|
|
|
->Event("SetDisplayFilled", &EditorPortalRequestBus::Events::SetDisplayFilled)
|
|
->Event("GetDisplayFilled", &EditorPortalRequestBus::Events::GetDisplayFilled)
|
|
->VirtualProperty("DisplayFilled", "GetDisplayFilled", "SetDisplayFilled")
|
|
|
|
->Event("SetAffectedBySun", &EditorPortalRequestBus::Events::SetAffectedBySun)
|
|
->Event("GetAffectedBySun", &EditorPortalRequestBus::Events::GetAffectedBySun)
|
|
->VirtualProperty("AffectedBySun", "GetAffectedBySun", "SetAffectedBySun")
|
|
|
|
->Event("SetViewDistRatio", &EditorPortalRequestBus::Events::SetViewDistRatio)
|
|
->Event("GetViewDistRatio", &EditorPortalRequestBus::Events::GetViewDistRatio)
|
|
->VirtualProperty("ViewDistRatio", "GetViewDistRatio", "SetViewDistRatio")
|
|
|
|
->Event("SetSkyOnly", &EditorPortalRequestBus::Events::SetSkyOnly)
|
|
->Event("GetSkyOnly", &EditorPortalRequestBus::Events::GetSkyOnly)
|
|
->VirtualProperty("SkyOnly", "GetSkyOnly", "SetSkyOnly")
|
|
|
|
->Event("SetOceanIsVisible", &EditorPortalRequestBus::Events::SetOceanIsVisible)
|
|
->Event("GetOceanIsVisible", &EditorPortalRequestBus::Events::GetOceanIsVisible)
|
|
->VirtualProperty("OceanIsVisible", "GetOceanIsVisible", "SetOceanIsVisible")
|
|
|
|
->Event("SetUseDeepness", &EditorPortalRequestBus::Events::SetUseDeepness)
|
|
->Event("GetUseDeepness", &EditorPortalRequestBus::Events::GetUseDeepness)
|
|
->VirtualProperty("UseDeepness", "GetUseDeepness", "SetUseDeepness")
|
|
|
|
->Event("SetDoubleSide", &EditorPortalRequestBus::Events::SetDoubleSide)
|
|
->Event("GetDoubleSide", &EditorPortalRequestBus::Events::GetDoubleSide)
|
|
->VirtualProperty("DoubleSide", "GetDoubleSide", "SetDoubleSide")
|
|
|
|
->Event("SetLightBlending", &EditorPortalRequestBus::Events::SetLightBlending)
|
|
->Event("GetLightBlending", &EditorPortalRequestBus::Events::GetLightBlending)
|
|
->VirtualProperty("LightBlending", "GetLightBlending", "SetLightBlending")
|
|
|
|
->Event("SetLightBlendValue", &EditorPortalRequestBus::Events::SetLightBlendValue)
|
|
->Event("GetLightBlendValue", &EditorPortalRequestBus::Events::GetLightBlendValue)
|
|
->VirtualProperty("LightBlendValue", "GetLightBlendValue", "SetLightBlendValue")
|
|
;
|
|
|
|
behaviorContext->Class<EditorPortalComponent>()->RequestBus("EditorPortalRequestBus");
|
|
}
|
|
|
|
EditorPortalConfiguration::Reflect(context);
|
|
}
|
|
|
|
void EditorPortalConfiguration::OnChange()
|
|
{
|
|
EditorPortalRequestBus::Event(m_entityId, &EditorPortalRequests::UpdatePortalObject);
|
|
}
|
|
|
|
void EditorPortalConfiguration::OnVerticesChange()
|
|
{
|
|
EditorPortalRequestBus::Event(m_entityId, &EditorPortalRequests::UpdatePortalObject);
|
|
EditorPortalNotificationBus::Event(m_entityId, &EditorPortalNotifications::OnVerticesChangedInspector);
|
|
}
|
|
|
|
void EditorPortalConfiguration::SetEntityId(const AZ::EntityId entityId)
|
|
{
|
|
m_entityId = entityId;
|
|
}
|
|
|
|
EditorPortalComponent::~EditorPortalComponent()
|
|
{
|
|
if (m_area)
|
|
{
|
|
// reset the listener vis area in the unlucky case that we are deleting the
|
|
// vis area where the listener is currently in
|
|
// Audio: do we still need this?
|
|
GetIEditor()->Get3DEngine()->DeleteVisArea(m_area);
|
|
m_area = nullptr;
|
|
}
|
|
}
|
|
|
|
void EditorPortalComponent::Activate()
|
|
{
|
|
Base::Activate();
|
|
|
|
const AZ::EntityId entityId = GetEntityId();
|
|
m_config.SetEntityId(entityId);
|
|
|
|
// NOTE: We create the vis-area here at activated, but destroy it in the destructor.
|
|
// We have to do this, otherwise the vis-area is not saved into the level.
|
|
// Unfortunately, at this time we cannot create the vis-areas at game runtime.
|
|
// This means that dynamic slices cannot effectively contain vis-areas until we fix the core rendering system to allow that.
|
|
|
|
const auto visGUID = static_cast<AZ::u64>(entityId);
|
|
if(!m_area && GetIEditor())
|
|
{
|
|
m_area = GetIEditor()->Get3DEngine()->CreateVisArea(visGUID);
|
|
}
|
|
|
|
m_AZCachedWorldTransform = AZ::Transform::CreateIdentity();
|
|
m_cryCachedWorldTransform = Matrix34::CreateIdentity();
|
|
|
|
m_componentModeDelegate.ConnectWithSingleComponentMode<
|
|
EditorPortalComponent, EditorPortalComponentMode>(
|
|
AZ::EntityComponentIdPair(entityId, GetId()), this);
|
|
|
|
EditorPortalRequestBus::Handler::BusConnect(entityId);
|
|
AZ::FixedVerticesRequestBus<AZ::Vector3>::Handler::BusConnect(entityId);
|
|
AZ::TransformNotificationBus::Handler::BusConnect(entityId);
|
|
AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(entityId);
|
|
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(entityId);
|
|
AzFramework::BoundsRequestBus::Handler::BusConnect(entityId);
|
|
|
|
//Call OnTransformChanged manually to cache current transform since it won't be called
|
|
//automatically for us when the level starts up.
|
|
AZ::Transform worldTM;
|
|
AZ::TransformBus::EventResult(worldTM, entityId, &AZ::TransformBus::Events::GetWorldTM);
|
|
|
|
//Use an identity transform for localTM because the
|
|
//OnTransformChanged impl for this class doesn't need it
|
|
OnTransformChanged(AZ::Transform::CreateIdentity(), worldTM);
|
|
}
|
|
|
|
void EditorPortalComponent::Deactivate()
|
|
{
|
|
m_componentModeDelegate.Disconnect();
|
|
|
|
AzFramework::BoundsRequestBus::Handler::BusDisconnect();
|
|
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect(GetEntityId());
|
|
AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(GetEntityId());
|
|
AZ::TransformNotificationBus::Handler::BusDisconnect(GetEntityId());
|
|
AZ::FixedVerticesRequestBus<AZ::Vector3>::Handler::BusDisconnect();
|
|
EditorPortalRequestBus::Handler::BusDisconnect();
|
|
|
|
Base::Deactivate();
|
|
}
|
|
|
|
void EditorPortalComponent::SetHeight(const float height)
|
|
{
|
|
m_config.m_height = height;
|
|
UpdatePortalObject();
|
|
}
|
|
|
|
float EditorPortalComponent::GetHeight()
|
|
{
|
|
return m_config.m_height;
|
|
}
|
|
|
|
void EditorPortalComponent::SetDisplayFilled(const bool filled)
|
|
{
|
|
m_config.m_displayFilled = filled;
|
|
UpdatePortalObject();
|
|
}
|
|
|
|
bool EditorPortalComponent::GetDisplayFilled()
|
|
{
|
|
return m_config.m_displayFilled;
|
|
}
|
|
|
|
void EditorPortalComponent::SetAffectedBySun(const bool affectedBySun)
|
|
{
|
|
m_config.m_affectedBySun = affectedBySun;
|
|
UpdatePortalObject();
|
|
}
|
|
|
|
bool EditorPortalComponent::GetAffectedBySun()
|
|
{
|
|
return m_config.m_affectedBySun;
|
|
}
|
|
|
|
void EditorPortalComponent::SetViewDistRatio(const float viewDistRatio)
|
|
{
|
|
m_config.m_viewDistRatio = viewDistRatio;
|
|
UpdatePortalObject();
|
|
}
|
|
|
|
float EditorPortalComponent::GetViewDistRatio()
|
|
{
|
|
return m_config.m_viewDistRatio;
|
|
}
|
|
|
|
void EditorPortalComponent::SetSkyOnly(const bool skyOnly)
|
|
{
|
|
m_config.m_skyOnly = skyOnly;
|
|
UpdatePortalObject();
|
|
}
|
|
|
|
bool EditorPortalComponent::GetSkyOnly()
|
|
{
|
|
return m_config.m_skyOnly;
|
|
}
|
|
|
|
void EditorPortalComponent::SetOceanIsVisible(const bool oceanVisible)
|
|
{
|
|
m_config.m_oceanIsVisible = oceanVisible;
|
|
UpdatePortalObject();
|
|
}
|
|
|
|
bool EditorPortalComponent::GetOceanIsVisible()
|
|
{
|
|
return m_config.m_oceanIsVisible;
|
|
}
|
|
|
|
void EditorPortalComponent::SetUseDeepness(const bool useDeepness)
|
|
{
|
|
m_config.m_useDeepness = useDeepness;
|
|
UpdatePortalObject();
|
|
}
|
|
|
|
bool EditorPortalComponent::GetUseDeepness()
|
|
{
|
|
return m_config.m_useDeepness;
|
|
}
|
|
|
|
void EditorPortalComponent::SetDoubleSide(const bool doubleSided)
|
|
{
|
|
m_config.m_doubleSide = doubleSided;
|
|
UpdatePortalObject();
|
|
}
|
|
|
|
bool EditorPortalComponent::GetDoubleSide()
|
|
{
|
|
return m_config.m_doubleSide;
|
|
}
|
|
|
|
void EditorPortalComponent::SetLightBlending(const bool lightBending)
|
|
{
|
|
m_config.m_lightBlending = lightBending;
|
|
UpdatePortalObject();
|
|
}
|
|
|
|
bool EditorPortalComponent::GetLightBlending()
|
|
{
|
|
return m_config.m_lightBlending;
|
|
}
|
|
|
|
void EditorPortalComponent::SetLightBlendValue(const float lightBendAmount)
|
|
{
|
|
m_config.m_lightBlendValue = lightBendAmount;
|
|
UpdatePortalObject();
|
|
}
|
|
|
|
float EditorPortalComponent::GetLightBlendValue()
|
|
{
|
|
return m_config.m_lightBlendValue;
|
|
}
|
|
|
|
bool EditorPortalComponent::GetVertex(const size_t index, AZ::Vector3& vertex) const
|
|
{
|
|
if (index < m_config.m_vertices.size())
|
|
{
|
|
vertex = m_config.m_vertices[index];
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool EditorPortalComponent::UpdateVertex(const size_t index, const AZ::Vector3& vertex)
|
|
{
|
|
if (index < m_config.m_vertices.size())
|
|
{
|
|
m_config.m_vertices[index] = vertex;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// Update the object runtime after changes to the Configuration.
|
|
/// Called by the default RequestBus SetXXX implementations,
|
|
/// and used to initially set up the object the first time the
|
|
/// Configuration are set.
|
|
void EditorPortalComponent::UpdatePortalObject()
|
|
{
|
|
if (m_area)
|
|
{
|
|
SVisAreaInfo info;
|
|
info.vAmbientColor = Vec3(ZERO);
|
|
info.bAffectedByOutLights = m_config.m_affectedBySun;
|
|
info.bSkyOnly = m_config.m_skyOnly;
|
|
info.fViewDistRatio = m_config.m_viewDistRatio;
|
|
info.bDoubleSide = m_config.m_doubleSide;
|
|
info.bUseDeepness = m_config.m_useDeepness;
|
|
info.bUseInIndoors = true; //Does not apply to Portals (Portals are only in VisAreas)
|
|
info.bOceanIsVisible = m_config.m_oceanIsVisible;
|
|
info.fPortalBlending = -1.0f;
|
|
|
|
if (m_config.m_lightBlending)
|
|
{
|
|
info.fPortalBlending = m_config.m_lightBlendValue;
|
|
}
|
|
|
|
AZStd::string name = AZStd::string("Portal_") + GetEntity()->GetName();
|
|
|
|
// Calculate scaled height
|
|
// Height exists separate from plane points but we still want to scale it with the transform
|
|
info.fHeight = m_config.m_height;
|
|
|
|
/*
|
|
We have to derive at least 3 points and pass them to the vis area system
|
|
For now that means getting the 4 points of the bottom face of the box.
|
|
|
|
If we want to send *all* points of a shape the vis system we need to make sure
|
|
that Height is 0; otherwise it'll extend the AABB of the area upwards.
|
|
*/
|
|
|
|
//Convert to Cry vectors and apply the transform to the given points
|
|
AZStd::fixed_vector<Vec3, 4> verts(4);
|
|
for (AZ::u32 i = 0; i < verts.size(); ++i)
|
|
{
|
|
verts[i] = AZVec3ToLYVec3(m_config.m_vertices[i]);
|
|
verts[i] = m_cryCachedWorldTransform.TransformPoint(verts[i]);
|
|
}
|
|
|
|
GetIEditor()->Get3DEngine()->UpdateVisArea(m_area, &verts[0], verts.size(), name.c_str(), info, true);
|
|
|
|
AzFramework::EntityBoundsUnionRequestBus::Broadcast(
|
|
&AzFramework::EntityBoundsUnionRequestBus::Events::RefreshEntityLocalBoundsUnion, GetEntityId());
|
|
}
|
|
}
|
|
|
|
void EditorPortalComponent::OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& world)
|
|
{
|
|
//Cache the transform so that we don't have to retrieve it every time UpdatePortalObject is called
|
|
m_AZCachedWorldTransform = world;
|
|
m_cryCachedWorldTransform = AZTransformToLYTransform(m_AZCachedWorldTransform);
|
|
|
|
UpdatePortalObject();
|
|
}
|
|
|
|
struct PortalQuadVertices
|
|
{
|
|
AZ::Vector3 floorLeftFront;
|
|
AZ::Vector3 floorRightFront;
|
|
AZ::Vector3 floorLeftBack;
|
|
AZ::Vector3 floorRightBack;
|
|
|
|
AZ::Vector3 quadUpperLeftFront;
|
|
AZ::Vector3 quadUpperRightFront;
|
|
AZ::Vector3 quadUpperLeftBack;
|
|
AZ::Vector3 quadUpperRightBack;
|
|
|
|
AZ::Vector3 portalUpperLeftFront;
|
|
AZ::Vector3 portalUpperRightFront;
|
|
AZ::Vector3 portalUpperLeftBack;
|
|
AZ::Vector3 portalUpperRightBack;
|
|
};
|
|
|
|
PortalQuadVertices EditorPortalComponent::CalculatePortalQuadVertices(VertTranslation vertTranslation)
|
|
{
|
|
PortalQuadVertices pqv;
|
|
|
|
//Untransformed quad corners
|
|
const AZ::Vector3 lowerLeftFront = m_config.m_vertices[0];
|
|
const AZ::Vector3 lowerRightFront = m_config.m_vertices[1];
|
|
const AZ::Vector3 lowerLeftBack = m_config.m_vertices[3];
|
|
const AZ::Vector3 lowerRightBack = m_config.m_vertices[2];
|
|
|
|
//Need to calculate the height of the quad after transformation
|
|
const AZ::u32 quadPointCount = 4;
|
|
AZ::Vector3 transformedQuadPoints[quadPointCount];
|
|
|
|
transformedQuadPoints[0] = m_AZCachedWorldTransform.TransformPoint(lowerLeftFront);
|
|
transformedQuadPoints[1] = m_AZCachedWorldTransform.TransformPoint(lowerRightFront);
|
|
transformedQuadPoints[2] = m_AZCachedWorldTransform.TransformPoint(lowerLeftBack);
|
|
transformedQuadPoints[3] = m_AZCachedWorldTransform.TransformPoint(lowerRightBack);
|
|
|
|
const AZ::Vector3 translation = m_AZCachedWorldTransform.GetTranslation();
|
|
|
|
float minHeight = FLT_MAX;
|
|
float maxHeight = FLT_MIN;
|
|
|
|
for (auto& transformedQuadPoint : transformedQuadPoints)
|
|
{
|
|
// remove translation from quad points so we can use them with the DisplayContext's usage of the transform
|
|
if (vertTranslation == VertTranslation::Remove)
|
|
{
|
|
transformedQuadPoint -= translation;
|
|
}
|
|
|
|
const float height = transformedQuadPoint.GetZ();
|
|
if (height < minHeight)
|
|
{
|
|
minHeight = height;
|
|
}
|
|
if (height > maxHeight)
|
|
{
|
|
maxHeight = height;
|
|
}
|
|
}
|
|
|
|
pqv.floorLeftFront = AZ::Vector3(transformedQuadPoints[0].GetX(), transformedQuadPoints[0].GetY(), minHeight);
|
|
pqv.floorRightFront = AZ::Vector3(transformedQuadPoints[1].GetX(), transformedQuadPoints[1].GetY(), minHeight);
|
|
pqv.floorLeftBack = AZ::Vector3(transformedQuadPoints[2].GetX(), transformedQuadPoints[2].GetY(), minHeight);
|
|
pqv.floorRightBack = AZ::Vector3(transformedQuadPoints[3].GetX(), transformedQuadPoints[3].GetY(), minHeight);
|
|
|
|
pqv.quadUpperLeftFront = AZ::Vector3(transformedQuadPoints[0].GetX(), transformedQuadPoints[0].GetY(), maxHeight);
|
|
pqv.quadUpperRightFront = AZ::Vector3(transformedQuadPoints[1].GetX(), transformedQuadPoints[1].GetY(), maxHeight);
|
|
pqv.quadUpperLeftBack = AZ::Vector3(transformedQuadPoints[2].GetX(), transformedQuadPoints[2].GetY(), maxHeight);
|
|
pqv.quadUpperRightBack = AZ::Vector3(transformedQuadPoints[3].GetX(), transformedQuadPoints[3].GetY(), maxHeight);
|
|
|
|
pqv.portalUpperLeftFront = AZ::Vector3(transformedQuadPoints[0].GetX(), transformedQuadPoints[0].GetY(), maxHeight + m_config.m_height);
|
|
pqv.portalUpperRightFront = AZ::Vector3(transformedQuadPoints[1].GetX(), transformedQuadPoints[1].GetY(), maxHeight + m_config.m_height);
|
|
pqv.portalUpperLeftBack = AZ::Vector3(transformedQuadPoints[2].GetX(), transformedQuadPoints[2].GetY(), maxHeight + m_config.m_height);
|
|
pqv.portalUpperRightBack = AZ::Vector3(transformedQuadPoints[3].GetX(), transformedQuadPoints[3].GetY(), maxHeight + m_config.m_height);
|
|
|
|
return pqv;
|
|
}
|
|
|
|
void EditorPortalComponent::DisplayEntityViewport(
|
|
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo,
|
|
AzFramework::DebugDisplayRequests& debugDisplay)
|
|
{
|
|
/*
|
|
IMPORTANT NOTE: This method may seem very complicated but it is an accurate visualization of
|
|
how portals actually work. The legacy visualization used with the legacy portal entity is
|
|
very misleading!
|
|
|
|
Portals always exist as a quad but if the quad becomes non-planar, from rotation or in the legacy
|
|
system from a point being pulled up or down, the volume changes in a non-obvious way. Instead of portal
|
|
existing as the shape defined by 4 points and extruded upwards, the portal actually remains planar.
|
|
Any height difference that you add by making the shape non-planar is just applied to the height of the volume.
|
|
|
|
If this is confusing, please actually look at the visualization created by this method. Make sure
|
|
that you rotate the portal in many weird contorted ways and examine how the visualization reacts.
|
|
The portal volume is always going to be a box rotated on only X and Y axes that stretches up along the Z axis.
|
|
|
|
Important note on the complexity of this method:
|
|
We cannot directly visualize the OBB of the portal with an AABB that we then transform. The OBB that's mentioned
|
|
here is best imagined as the top plane being all points of the quad pulled up to the height of the highest quad's vert
|
|
and the bottom plane being all points of the quad pulled down to the height of the lowest quad's vert. Trying to
|
|
create an AABB from these points won't produce the correct visualization under complex rotations as the Min and Max
|
|
of the AABB will either only encompass part of the bounding volume or be too large.
|
|
*/
|
|
|
|
const PortalQuadVertices pqv = CalculatePortalQuadVertices(VertTranslation::Remove);
|
|
|
|
//Draw the outline of the OBB of the Portal's quad
|
|
AZ::Color color(0.000f, 1.0f, 0.000f, 1.0f);
|
|
debugDisplay.SetColor(AZ::Vector4(color.GetR(), color.GetG(), color.GetB(), 1.f));
|
|
|
|
//Remove all rotation from the transform
|
|
const AZ::Quaternion rotation = AZ::Quaternion::CreateIdentity();
|
|
|
|
AZ::Transform worldTMOnlyZRot = m_AZCachedWorldTransform;
|
|
worldTMOnlyZRot.SetRotation(rotation);
|
|
|
|
debugDisplay.PushMatrix(worldTMOnlyZRot);
|
|
|
|
//Draw the outline of the OBB of the portal quad
|
|
|
|
//Bottom
|
|
debugDisplay.DrawLine(pqv.floorLeftFront, pqv.floorRightFront);
|
|
debugDisplay.DrawLine(pqv.floorRightFront, pqv.floorRightBack);
|
|
debugDisplay.DrawLine(pqv.floorRightBack, pqv.floorLeftBack);
|
|
debugDisplay.DrawLine(pqv.floorLeftBack, pqv.floorLeftFront);
|
|
//Top
|
|
debugDisplay.DrawLine(pqv.quadUpperLeftFront, pqv.quadUpperRightFront);
|
|
debugDisplay.DrawLine(pqv.quadUpperRightFront, pqv.quadUpperRightBack);
|
|
debugDisplay.DrawLine(pqv.quadUpperRightBack, pqv.quadUpperLeftBack);
|
|
debugDisplay.DrawLine(pqv.quadUpperLeftBack, pqv.quadUpperLeftFront);
|
|
//Left
|
|
debugDisplay.DrawLine(pqv.floorLeftFront, pqv.quadUpperLeftFront);
|
|
debugDisplay.DrawLine(pqv.quadUpperLeftFront, pqv.quadUpperLeftBack);
|
|
debugDisplay.DrawLine(pqv.quadUpperLeftBack, pqv.floorLeftBack);
|
|
debugDisplay.DrawLine(pqv.floorLeftBack, pqv.floorLeftFront);
|
|
//Right
|
|
debugDisplay.DrawLine(pqv.floorRightFront, pqv.quadUpperRightFront);
|
|
debugDisplay.DrawLine(pqv.quadUpperRightFront, pqv.quadUpperRightBack);
|
|
debugDisplay.DrawLine(pqv.quadUpperRightBack, pqv.floorRightBack);
|
|
debugDisplay.DrawLine(pqv.floorRightBack, pqv.floorRightFront);
|
|
//Front
|
|
debugDisplay.DrawLine(pqv.floorLeftFront, pqv.floorRightFront);
|
|
debugDisplay.DrawLine(pqv.floorRightFront, pqv.quadUpperRightFront);
|
|
debugDisplay.DrawLine(pqv.quadUpperRightFront, pqv.quadUpperLeftFront);
|
|
debugDisplay.DrawLine(pqv.quadUpperLeftFront, pqv.floorLeftFront);
|
|
//Back
|
|
debugDisplay.DrawLine(pqv.floorLeftBack, pqv.floorRightBack);
|
|
debugDisplay.DrawLine(pqv.floorRightBack, pqv.quadUpperRightBack);
|
|
debugDisplay.DrawLine(pqv.quadUpperRightBack, pqv.quadUpperLeftBack);
|
|
debugDisplay.DrawLine(pqv.quadUpperLeftBack, pqv.floorLeftBack);
|
|
|
|
//Now draw the entire portal volume (Previous OBB + extra height)
|
|
if (m_config.m_displayFilled)
|
|
{
|
|
//Draw whole portal with less alpha
|
|
debugDisplay.SetColor(AZ::Vector4(color.GetR(), color.GetG(), color.GetB(), 0.1f));
|
|
|
|
//Draw both winding orders for quads so they appear solid from all angles
|
|
//Not drawing boxes because the corners of the quad may not be hit if the bounds are rotated oddly
|
|
|
|
//Bottom
|
|
debugDisplay.DrawQuad(pqv.floorLeftFront, pqv.floorRightFront, pqv.floorRightBack, pqv.floorLeftBack);
|
|
debugDisplay.DrawQuad(pqv.floorLeftFront, pqv.floorLeftBack, pqv.floorRightBack, pqv.floorRightFront);
|
|
//Top
|
|
debugDisplay.DrawQuad(pqv.portalUpperLeftFront, pqv.portalUpperRightFront, pqv.portalUpperRightBack, pqv.portalUpperLeftBack);
|
|
debugDisplay.DrawQuad(pqv.portalUpperLeftFront, pqv.portalUpperLeftBack, pqv.portalUpperRightBack, pqv.portalUpperRightFront);
|
|
//Left
|
|
debugDisplay.DrawQuad(pqv.floorLeftFront, pqv.portalUpperLeftFront, pqv.portalUpperLeftBack, pqv.floorLeftBack);
|
|
debugDisplay.DrawQuad(pqv.floorLeftFront, pqv.floorLeftBack, pqv.portalUpperLeftBack, pqv.portalUpperLeftFront);
|
|
//Right
|
|
debugDisplay.DrawQuad(pqv.floorRightFront, pqv.portalUpperRightFront, pqv.portalUpperRightBack, pqv.floorRightBack);
|
|
debugDisplay.DrawQuad(pqv.floorRightFront, pqv.floorRightBack, pqv.portalUpperRightBack, pqv.portalUpperRightFront);
|
|
//Front
|
|
debugDisplay.DrawQuad(pqv.floorLeftFront, pqv.floorRightFront, pqv.portalUpperRightFront, pqv.portalUpperLeftFront);
|
|
debugDisplay.DrawQuad(pqv.floorLeftFront, pqv.portalUpperLeftFront, pqv.portalUpperRightFront, pqv.floorRightFront);
|
|
//Back
|
|
debugDisplay.DrawQuad(pqv.floorLeftBack, pqv.floorRightBack, pqv.portalUpperRightBack, pqv.portalUpperLeftBack);
|
|
debugDisplay.DrawQuad(pqv.floorLeftBack, pqv.portalUpperLeftBack, pqv.portalUpperRightBack, pqv.floorRightBack);
|
|
}
|
|
else
|
|
{
|
|
//Bottom
|
|
debugDisplay.DrawLine(pqv.floorLeftFront, pqv.floorRightFront);
|
|
debugDisplay.DrawLine(pqv.floorRightFront, pqv.floorRightBack);
|
|
debugDisplay.DrawLine(pqv.floorRightBack, pqv.floorLeftBack);
|
|
debugDisplay.DrawLine(pqv.floorLeftBack, pqv.floorLeftFront);
|
|
//Top
|
|
debugDisplay.DrawLine(pqv.portalUpperLeftFront, pqv.portalUpperRightFront);
|
|
debugDisplay.DrawLine(pqv.portalUpperRightFront, pqv.portalUpperRightBack);
|
|
debugDisplay.DrawLine(pqv.portalUpperRightBack, pqv.portalUpperLeftBack);
|
|
debugDisplay.DrawLine(pqv.portalUpperLeftBack, pqv.portalUpperLeftFront);
|
|
//Left
|
|
debugDisplay.DrawLine(pqv.floorLeftFront, pqv.portalUpperLeftFront);
|
|
debugDisplay.DrawLine(pqv.portalUpperLeftFront, pqv.portalUpperLeftBack);
|
|
debugDisplay.DrawLine(pqv.portalUpperLeftBack, pqv.floorLeftBack);
|
|
debugDisplay.DrawLine(pqv.floorLeftBack, pqv.floorLeftFront);
|
|
//Right
|
|
debugDisplay.DrawLine(pqv.floorRightFront, pqv.portalUpperRightFront);
|
|
debugDisplay.DrawLine(pqv.portalUpperRightFront, pqv.portalUpperRightBack);
|
|
debugDisplay.DrawLine(pqv.portalUpperRightBack, pqv.floorRightBack);
|
|
debugDisplay.DrawLine(pqv.floorRightBack, pqv.floorRightFront);
|
|
//Front
|
|
debugDisplay.DrawLine(pqv.floorLeftFront, pqv.floorRightFront);
|
|
debugDisplay.DrawLine(pqv.floorRightFront, pqv.portalUpperRightFront);
|
|
debugDisplay.DrawLine(pqv.portalUpperRightFront, pqv.portalUpperLeftFront);
|
|
debugDisplay.DrawLine(pqv.portalUpperLeftFront, pqv.floorLeftFront);
|
|
//Back
|
|
debugDisplay.DrawLine(pqv.floorLeftBack, pqv.floorRightBack);
|
|
debugDisplay.DrawLine(pqv.floorRightBack, pqv.portalUpperRightBack);
|
|
debugDisplay.DrawLine(pqv.portalUpperRightBack, pqv.portalUpperLeftBack);
|
|
debugDisplay.DrawLine(pqv.portalUpperLeftBack, pqv.floorLeftBack);
|
|
}
|
|
|
|
if (m_componentModeDelegate.AddedToComponentMode())
|
|
{
|
|
AzToolsFramework::VertexContainerDisplay::DisplayVertexContainerIndices(
|
|
debugDisplay, AzToolsFramework::FixedVerticesArray<AZ::Vector3, 4>(m_config.m_vertices),
|
|
GetWorldTM(), AZ::Vector3::CreateOne(), IsSelected());
|
|
}
|
|
|
|
debugDisplay.PopMatrix();
|
|
}
|
|
|
|
void EditorPortalComponent::BuildGameEntity(AZ::Entity* gameEntity)
|
|
{
|
|
gameEntity->CreateComponent<PortalComponent>(m_config);
|
|
}
|
|
|
|
AZ::Aabb EditorPortalComponent::GetEditorSelectionBoundsViewport(const AzFramework::ViewportInfo& /*viewportInfo*/)
|
|
{
|
|
const PortalQuadVertices pqv = CalculatePortalQuadVertices(VertTranslation::Keep);
|
|
|
|
AZ::Aabb bbox = AZ::Aabb::CreateNull();
|
|
bbox.AddPoint(pqv.floorLeftFront);
|
|
bbox.AddPoint(pqv.floorRightFront);
|
|
bbox.AddPoint(pqv.floorLeftBack);
|
|
bbox.AddPoint(pqv.floorRightBack);
|
|
bbox.AddPoint(pqv.portalUpperLeftFront);
|
|
return bbox;
|
|
}
|
|
|
|
bool EditorPortalComponent::EditorSelectionIntersectRayViewport(
|
|
const AzFramework::ViewportInfo& /*viewportInfo*/, const AZ::Vector3& src,
|
|
const AZ::Vector3& dir, float& distance)
|
|
{
|
|
float t;
|
|
float intermediateT = FLT_MAX;
|
|
|
|
const PortalQuadVertices pqv = CalculatePortalQuadVertices(VertTranslation::Keep);
|
|
|
|
//Count each quad for intersection hits, two hits implies we are intersecting the prism from outside of it (or from too far)
|
|
AZ::u8 hits = 0;
|
|
|
|
//Bottom
|
|
if (AZ::Intersect::IntersectRayQuad(src, dir, pqv.floorLeftFront, pqv.floorRightFront, pqv.floorRightBack, pqv.floorLeftBack, t))
|
|
{
|
|
++hits;
|
|
intermediateT = AZStd::GetMin(t, intermediateT);
|
|
}
|
|
//Top
|
|
if (AZ::Intersect::IntersectRayQuad(src, dir, pqv.portalUpperLeftFront, pqv.portalUpperRightFront, pqv.portalUpperRightBack, pqv.portalUpperLeftBack, t))
|
|
{
|
|
++hits;
|
|
intermediateT = AZStd::GetMin(t, intermediateT);
|
|
}
|
|
|
|
//Left
|
|
if (AZ::Intersect::IntersectRayQuad(src, dir, pqv.floorLeftFront, pqv.portalUpperLeftFront, pqv.portalUpperLeftBack, pqv.floorLeftBack, t))
|
|
{
|
|
++hits;
|
|
intermediateT = AZStd::GetMin(t, intermediateT);
|
|
}
|
|
//Right
|
|
if (AZ::Intersect::IntersectRayQuad(src, dir, pqv.floorRightFront, pqv.portalUpperRightFront, pqv.portalUpperRightBack, pqv.floorRightBack, t))
|
|
{
|
|
++hits;
|
|
intermediateT = AZStd::GetMin(t, intermediateT);
|
|
}
|
|
//Front
|
|
if (AZ::Intersect::IntersectRayQuad(src, dir, pqv.floorLeftFront, pqv.floorRightFront, pqv.portalUpperRightFront, pqv.portalUpperLeftFront, t))
|
|
{
|
|
++hits;
|
|
intermediateT = AZStd::GetMin(t, intermediateT);
|
|
}
|
|
//Back
|
|
if (AZ::Intersect::IntersectRayQuad(src, dir, pqv.floorLeftBack, pqv.floorRightBack, pqv.portalUpperRightBack, pqv.portalUpperLeftBack, t))
|
|
{
|
|
++hits;
|
|
intermediateT = AZStd::GetMin(t, intermediateT);
|
|
}
|
|
|
|
if (hits > 0)
|
|
{
|
|
distance = intermediateT;
|
|
}
|
|
return hits >= 2;
|
|
}
|
|
|
|
AZ::Aabb EditorPortalComponent::GetWorldBounds()
|
|
{
|
|
return GetLocalBounds().GetTransformedAabb(m_AZCachedWorldTransform);
|
|
}
|
|
|
|
AZ::Aabb EditorPortalComponent::GetLocalBounds()
|
|
{
|
|
AZ::Aabb bbox = AZ::Aabb::CreateNull();
|
|
for (const auto& vertex : m_config.m_vertices)
|
|
{
|
|
bbox.AddPoint(vertex);
|
|
}
|
|
return bbox;
|
|
}
|
|
} //namespace Visibility
|