Merge branch 'development' into LYN-5265_state_tracker_impl

This commit is contained in:
John
2021-09-30 16:53:37 +01:00
114 changed files with 3339 additions and 3756 deletions
+1 -1
View File
@@ -44,7 +44,7 @@ enum
{
// in milliseconds
GameModeIdleFrequency = 0,
EditorModeIdleFrequency = 0,
EditorModeIdleFrequency = 1,
InactiveModeFrequency = 10,
UninitializedFrequency = 9999,
};
@@ -36,9 +36,6 @@
#include "CryEdit.h"
#include "Viewport.h"
// Atom Renderer
#include <Atom/RPI.Public/RPISystemInterface.h>
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
#include <TrackView/ui_SequenceBatchRenderDialog.h>
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
@@ -1237,13 +1234,6 @@ void CSequenceBatchRenderDialog::OnKickIdleTimout()
{
componentApplication->TickSystem();
}
// Directly tick the renderer, as it's no longer part of the system tick
if (auto rpiSystem = AZ::RPI::RPISystemInterface::Get())
{
rpiSystem->SimulationTick();
rpiSystem->RenderTick();
}
}
}
@@ -74,8 +74,6 @@
#include <AzCore/Module/Environment.h>
#include <AzCore/std/string/conversions.h>
AZ_CVAR(float, g_simulation_tick_rate, 0, nullptr, AZ::ConsoleFunctorFlags::Null, "The rate at which the game simulation tick loop runs, or 0 for as fast as possible");
static void PrintEntityName(const AZ::ConsoleCommandContainer& arguments)
{
if (arguments.empty())
@@ -1396,23 +1394,6 @@ namespace AZ
AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:OnTick");
EBUS_EVENT(TickBus, OnTick, m_deltaTime, ScriptTimePoint(now));
}
// If tick rate limiting is on, ensure (1 / g_simulation_tick_rate) ms has elapsed since the last frame,
// sleeping if there's still time remaining.
if (g_simulation_tick_rate > 0.f)
{
now = AZStd::chrono::system_clock::now();
// Work in microsecond durations here as that's the native measurement time for time_point
constexpr float microsecondsPerSecond = 1000.f * 1000.f;
const AZStd::chrono::microseconds timeBudgetPerTick(static_cast<int>(microsecondsPerSecond / g_simulation_tick_rate));
AZStd::chrono::microseconds timeUntilNextTick = m_currentTime + timeBudgetPerTick - now;
if (timeUntilNextTick.count() > 0)
{
AZStd::this_thread::sleep_for(timeUntilNextTick);
}
}
}
}
@@ -46,8 +46,6 @@ namespace AZ
TICK_PRE_RENDER = 750, ///< Suggested tick handler position to update render-related data.
TICK_RENDER = 800, ///< Suggested tick handler position for rendering.
TICK_DEFAULT = 1000, ///< Default tick handler position when the handler is constructed.
TICK_UI = 2000, ///< Suggested tick handler position for UI components.
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M16.9,16.7c0,0.7-0.6,1.3-1.3,1.3h-7c-0.7,0-1.3-0.6-1.3-1.3v-0.5h-2v0.5c0,1.8,1.5,3.3,3.3,3.3h2
c-0.2,0.5-0.1,1.2,0.3,1.6c0.6,0.6,1.5,0.6,2.1,0c0.4-0.4,0.5-1,0.3-1.6h2.3c1.8,0,3.3-1.5,3.3-3.3v-0.5l-2,0V16.7z M12.3,20.9
c-0.2,0.2-0.6,0.2-0.8,0c-0.2-0.2-0.2-0.5,0-0.8c0.2-0.2,0.5-0.2,0.8,0c0,0,0,0,0,0C12.5,20.3,12.5,20.7,12.3,20.9z"/>
<rect x="6.5" y="8" transform="matrix(0.9939 -0.1101 0.1101 0.9939 -0.8771 1.3776)" class="st0" width="11" height="1.4"/>
<rect x="6.5" y="10.6" transform="matrix(0.9939 -0.1101 0.1101 0.9939 -1.1681 1.3937)" class="st0" width="11" height="1.4"/>
<rect x="6.5" y="13.2" transform="matrix(0.9939 -0.1101 0.1101 0.9939 -1.4588 1.4055)" class="st0" width="11" height="1.4"/>
<path class="st0" d="M13.3,4c0.1-0.2,0.1-0.3,0.1-0.5c0-0.8-0.6-1.4-1.4-1.4s-1.4,0.6-1.4,1.4c0,0.2,0,0.4,0.1,0.5H5.2v2.3h13.6V4
H13.3z M12,4c-0.3,0-0.6-0.3-0.6-0.6c0-0.3,0.3-0.6,0.6-0.6s0.6,0.3,0.6,0.6C12.6,3.7,12.3,4,12,4z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M3,17.9v1.3c5.9,1.2,12.1,1.2,18,0v-1.3C15.1,19.1,8.9,19.1,3,17.9z"/>
<path class="st0" d="M3,15.9v1.3c5.9,1.2,12.1,1.2,18,0v-1.3C15.1,17.2,8.9,17.2,3,15.9z"/>
<path class="st0" d="M3,14v1.3c5.9,1.2,12.1,1.2,18-0.1v-1.3C15.1,15.2,8.9,15.3,3,14z"/>
<path class="st0" d="M6.5,11.5c0.1,0.1,0.2,0.2,0.4,0.2c0.1,0,0.3-0.1,0.4-0.2l2-2.1c0.2-0.2,0.2-0.5,0-0.8c-0.2-0.2-0.5-0.2-0.7,0
c0,0,0,0,0,0L7.4,9.9V3.3C7.4,3.2,7.3,3,7.2,3c0,0,0,0,0,0H6.7C6.5,3,6.4,3.1,6.4,3.3v0.2v6.4L5.3,8.7C5,8.5,4.7,8.5,4.5,8.7
C4.4,8.8,4.4,8.9,4.3,9.1c0,0.1,0.1,0.3,0.2,0.4L6.5,11.5z"/>
<path class="st0" d="M16.7,11.5c0.1,0.1,0.2,0.2,0.4,0.2c0.1,0,0.3-0.1,0.4-0.2l2-2.1c0.2-0.2,0.2-0.5,0-0.8
c-0.2-0.2-0.5-0.2-0.7,0c0,0,0,0,0,0l-1.1,1.1V3.3c0-0.2-0.1-0.3-0.2-0.3c0,0,0,0,0,0h-0.5c-0.2,0-0.3,0.1-0.3,0.3v0.2v6.4
l-1.1-1.2c-0.2-0.2-0.6-0.2-0.8,0c-0.1,0.1-0.1,0.2-0.1,0.4c0,0.1,0.1,0.3,0.1,0.4L16.7,11.5z"/>
<path class="st0" d="M9.6,11.3l2,2.1c0.1,0.1,0.2,0.2,0.4,0.2c0.1,0,0.3-0.1,0.4-0.2l2-2.1c0.2-0.2,0.2-0.5,0-0.8c0,0,0,0,0,0
c-0.2-0.2-0.5-0.2-0.7,0c0,0,0,0,0,0l-1.1,1.1V5.1c0-0.2-0.1-0.3-0.3-0.3c0,0,0,0,0,0h-0.5c-0.2,0-0.3,0.1-0.3,0.3v0.1v6.4
l-1.1-1.1c-0.2-0.2-0.5-0.2-0.7,0c0,0,0,0,0,0c-0.1,0.1-0.2,0.2-0.2,0.4C9.4,11,9.5,11.2,9.6,11.3z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M12,12.2c-5.9,0-7-1.6-7-1.6v7.2c0.1,1,3,1.9,6.7,1.9V21c0,0.2,0.1,0.3,0.3,0.3s0.3-0.1,0.3-0.3v-1.3
c3.6,0,6.5-0.9,6.7-1.9v-7.2C19,10.6,17.9,12.2,12,12.2z"/>
<path class="st0" d="M12.3,7.6V6.9l0.8,0.4V6.6c2.7-0.1,5.2-0.7,5.2-1.4c0-0.8-2.7-1.2-6-1.2V3c0-0.2-0.1-0.3-0.3-0.3
c-0.2,0-0.3,0.1-0.3,0.3v1c-3.3,0-6,0.4-6,1.2c0,0.2,0.5,0.4,0.8,0.5C7.2,5.9,8,6.1,8.8,6.1c0.3,0,0.5,0,0.8,0C9.4,6,9.2,6,9,6
C7.6,5.9,7.2,5.5,7.2,5.2c0-0.5,1.8-0.7,4.4-0.7v1.1l-1,0.5l1,0.5v1.1C8,7.7,5,8.5,5,9.6c0,1.1,3.1,2,7,2s7-0.9,7-2
C19,8.5,16,7.7,12.3,7.6z M12.3,4.6c2.6,0,4.5,0.2,4.5,0.7c0,0.5-1.7,0.6-3.6,0.7v-1l-0.9,0.4V4.6z M8.4,9.6c0-0.5,1.4-0.9,3.2-1v2
C9.8,10.6,8.4,10.1,8.4,9.6z M12.3,10.6v-2c1.8,0,3.2,0.5,3.2,1C15.6,10.1,14.2,10.6,12.3,10.6z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M19.6,11.7l-2.2-2.2l0,0l-1.7-1.7l0,0h0l0,0l0,0h0l0,0L13.4,10l-0.1,0.1l0,0l0,0l1.7,1.7l0,0l1.6,1.6
c1,0.8,1.1,2.2,0.4,3.2c-0.3,0.3-0.8,0.5-1.2,0.5c-0.7,0-1.5-0.3-2-0.8l-1.6-1.6l0,0L10.5,13l-0.7,0.7l-1.7,1.7l3.9,3.9
c2.2,2,5.6,1.7,7.6-0.5C21.3,16.7,21.3,13.7,19.6,11.7z M18.4,18.5c-1.6,1.4-4.1,1.4-5.7,0l-2.2-2.2l0.9-0.9L13,17
c0.7,0.7,1.7,1.1,2.7,1.1c0.7,0,1.5-0.3,2-0.8c1.2-1.4,1-3.5-0.3-4.7l-1.6-1.6l0.9-0.9l2.2,2.2C20.4,14.2,20.2,16.9,18.4,18.5z"/>
<polygon class="st0" points="6.1,7.5 5.1,7.5 5.1,5.9 3,8 5.1,10.1 5.1,8.5 6.1,8.5 "/>
<polygon class="st0" points="7.5,6.1 8.5,6.1 8.5,5.1 10.1,5.1 8,3 5.9,5.1 7.5,5.1 "/>
<polygon class="st0" points="8.5,11 8.5,10 7.5,10 7.5,10.9 5.9,10.9 8,13 10.1,11 "/>
<polygon class="st0" points="10,7.5 10,8.5 10.9,8.5 10.9,10.1 13,8 10.9,5.9 11,7.5 "/>
<rect x="6.5" y="6.5" class="st0" width="3" height="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M19.6,11.7l-2.2-2.2l0,0l-1.7-1.7l0,0h0l0,0l0,0h0l0,0L13.4,10l-0.1,0.1l0,0l0,0l1.7,1.7l0,0l1.6,1.6
c1,0.8,1.1,2.2,0.4,3.2c-0.3,0.3-0.8,0.5-1.2,0.5c-0.7,0-1.5-0.3-2-0.8l-1.6-1.6l0,0L10.5,13l-0.7,0.7l-1.7,1.7l3.9,3.9
c2.2,2,5.6,1.7,7.6-0.5C21.3,16.7,21.3,13.7,19.6,11.7z M18.4,18.5c-1.6,1.4-4.1,1.4-5.7,0l-2.2-2.2l0.9-0.9L13,17
c0.7,0.7,1.7,1.1,2.7,1.1c0.7,0,1.5-0.3,2-0.8c1.2-1.4,1-3.5-0.3-4.7l-1.6-1.6l0.9-0.9l2.2,2.2C20.4,14.2,20.2,16.9,18.4,18.5z"/>
<path class="st0" d="M7.1,9.5l2.1,2.1v-1.2c2.3-0.3,4-1.4,4-3.4c0-2.3-2.2-3.5-5-3.5s-5,1.2-5,3.5c0,0.5,0.2,1.1,0.6,1.5
C4.3,9,5,9.4,5.8,9.5c0.4,0.1,0.6,0.2,0.6,0.1S6.2,9.4,5.9,9.2C5,8.9,4.4,8,4.5,7c0-1.4,1.5-2,3.7-2S12,5.6,12,7
c0,1.2-1.1,2-2.8,2.2V7.4L7.1,9.5z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M14.5,11.3L14.5,11.3c-0.1,0-0.2,0-0.3,0.1c-0.2,0.1-0.4,0.2-0.6,0.4c-0.5,0.4-1,0.6-1.6,0.7
c-0.6-0.1-1.2-0.4-1.6-0.7c-0.2-0.1-0.4-0.3-0.6-0.4c-0.1,0-0.2-0.1-0.3-0.1H9.5c-2.4,0-3,1-3,1c-0.1,0.2-0.2,0.3-0.3,0.5v1.5
c0,0.4,0.5,0.8,0.5,0.8l1.6,1.6c0.5,0.5,0.8,1.3,0.9,2V21c0,0.4,0.3,0.7,0.7,0.7c0,0,0,0,0,0h4.1c0.4,0,0.7-0.3,0.7-0.7
c0,0,0,0,0,0v-2.3c0-0.7,0.3-1.5,0.9-2l1.6-1.6c0,0,0.5-0.4,0.5-0.8v-1.5c-0.1-0.2-0.2-0.4-0.3-0.5C17.5,12.3,16.9,11.3,14.5,11.3z
"/>
<path class="st0" d="M9.5,10.3c0.2,0,0.3,0.1,0.5,0.1c0.6,0.3,1.3,1.1,2.1,1.1s1.5-0.8,2.1-1.1c0.1-0.1,0.3-0.1,0.5-0.1
c2.7,0,3.3,1.1,3.3,1.1V11c0-0.6-0.3-1.2-0.7-1.7l-1.5-1.6c-0.5-0.7-0.7-1.5-0.7-2.4V3c0-0.6-0.4-1-1-1h-3.8c-0.6,0-1,0.4-1,1v2.3
C9.1,6.2,8.9,7,8.4,7.7L6.9,9.3C6.5,9.7,6.2,10.4,6.2,11v0.5C6.2,11.5,6.8,10.3,9.5,10.3z"/>
<path class="st0" d="M19.6,7.8c-0.1-0.2-0.3-0.2-0.5-0.1C19,7.7,18.9,7.9,19,8.1c1.4,2.5,1.4,5.4,0,7.9c0,0,0,0,0,0
c-0.1,0.2,0,0.4,0.1,0.4c0.1,0,0.1,0,0.2,0c0.1,0,0.2,0,0.3-0.1C21.1,13.7,21.1,10.4,19.6,7.8z"/>
<path class="st0" d="M4.1,11.7c0-1.2,0.3-2.4,0.8-3.4c0.1-0.2,0-0.4-0.2-0.4C4.5,7.8,4.3,7.8,4.3,8c-1.2,2.5-1.1,5.4,0.2,7.8
C4.5,15.9,4.6,16,4.7,16c0,0,0.1,0,0.1,0c0,0,0,0,0,0C5,15.9,5.1,15.7,5,15.5C4.4,14.3,4.1,13,4.1,11.7z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M19.3,10.7l0.1-2.5c0-0.2-0.2-0.4-0.4-0.4c-0.3,0-0.5,0.2-0.5,0.5l-0.1,1.6L18,9.5l0,0
c-0.1-0.1-0.2-0.2-0.2-0.2c-0.4-0.4-0.9-0.7-1.3-0.9L17,6.7c0.1-0.3,0-0.6-0.3-0.7c0,0-0.1,0-0.1,0l-3.1-0.6
c-0.3-0.1-0.6,0.1-0.7,0.4c0,0,0,0.1,0,0.1l-0.1,1.4C10.3,7,7.9,7.8,6,9.5l0,0L5.6,9.8V8.2c0-0.3-0.2-0.5-0.5-0.5c0,0,0,0,0,0
c-0.2,0-0.5,0.2-0.5,0.4c0,0,0,0,0,0v2.5c0,0.3,0.2,0.5,0.5,0.5l2.5,0.1c0.3,0,0.5-0.2,0.5-0.5c0-0.3-0.2-0.5-0.5-0.5l-1.5-0.1
C6.3,10,6.4,9.8,6.6,9.7c1.8-1.5,4-2.1,6.1-1.9l-0.1,1.9c0,0.4,0.3,0.7,0.6,0.8l0.3,0.1l-1.8,9.2c-0.6,0.1-1.1,0.6-1.1,1.2
c0,0.7,0.6,1.3,1.3,1.3s1.3-0.6,1.3-1.3c0-0.4-0.2-0.8-0.6-1l1.8-9.2l0.3,0.1c0.4,0.1,0.7-0.1,0.9-0.5l0.5-1.4
c0.6,0.3,1.1,0.7,1.6,1.2l0.1,0.1c0,0,0.1,0.1,0.1,0.1h-1.6c-0.1,0-0.3,0-0.3,0.1c-0.1,0.1-0.1,0.2-0.2,0.3c0,0.2,0.2,0.4,0.4,0.5
c0,0,0,0,0,0h2.5C19.1,11.2,19.3,11,19.3,10.7z"/>
<path class="st0" d="M2.8,10.1c-0.1-0.1-0.3-0.1-0.4,0c-0.1,0.1-0.1,0.3,0,0.4l2.9,2.7c0.1,0,0.1,0.1,0.2,0.1c0.1,0,0.2,0,0.2-0.1
c0.1-0.1,0.1-0.3,0-0.4L2.8,10.1z"/>
<path class="st0" d="M21.5,10c-0.1-0.1-0.3-0.1-0.4,0l-2.7,2.9c0,0.1-0.1,0.1-0.1,0.2c0,0.1,0,0.1,0.1,0.2c0.1,0.1,0.3,0.1,0.4,0
l2.6-2.9C21.6,10.3,21.6,10.1,21.5,10z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M17.5,18.1c-0.1,0-0.1,0-0.2,0H6.9l7.4-7.3c0.3-0.2,0.3-0.6,0-0.9c0,0,0,0,0,0c-0.1-0.1-0.3-0.2-0.5-0.2
c-0.2,0-0.3,0.1-0.5,0.2L6,17.2V6.9c0-0.4-0.3-0.7-0.7-0.7C5,6.2,4.7,6.5,4.7,6.9c0,0,0,0,0,0L4.6,17.1c0,0,0,0,0,0
c-1.3,0-2.3,1-2.3,2.3s1,2.3,2.3,2.3s2.3-1,2.3-2.3h10.4c0.4,0,0.7-0.2,0.7-0.6C18.1,18.5,17.9,18.1,17.5,18.1z"/>
<path class="st0" d="M21.8,13.2c-0.2-0.2-0.6-0.2-0.9,0l-1.3,1.3V14c-0.1-5.2-4.3-9.4-9.5-9.4H9.6l1.3-1.2c0.2-0.2,0.2-0.6,0-0.9
c-0.1-0.1-0.3-0.2-0.4-0.2c-0.2,0-0.3,0.1-0.4,0.2L7.7,4.8C7.6,4.9,7.5,5.1,7.5,5.2c0,0.2,0.1,0.3,0.2,0.4l2.3,2.3
c0.2,0.2,0.6,0.2,0.9,0c0.2-0.2,0.2-0.6,0-0.8L9.6,5.8h0.5c4.6,0,8.3,3.6,8.3,8.2c0,0,0,0,0,0v0.5l-1.3-1.3
c-0.1-0.1-0.3-0.2-0.4-0.2c-0.2,0-0.3,0.1-0.4,0.2c-0.2,0.2-0.2,0.6,0,0.8c0,0,0,0,0,0l2.4,2.3c0.2,0.2,0.6,0.2,0.9,0l2.3-2.3
C22.1,13.8,22.1,13.4,21.8,13.2z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -390,6 +390,14 @@
<file>img/UI20/toolbar/Y_axis.svg</file>
<file>img/UI20/toolbar/Z_axis.svg</file>
<file>img/UI20/toolbar/XY2_copy.svg</file>
<file>img/UI20/toolbar/joints/Damping.svg</file>
<file>img/UI20/toolbar/joints/MaxForce.svg</file>
<file>img/UI20/toolbar/joints/MaxTorque.svg</file>
<file>img/UI20/toolbar/joints/SnapPosition.svg</file>
<file>img/UI20/toolbar/joints/SnapRotation.svg</file>
<file>img/UI20/toolbar/joints/Stiffness.svg</file>
<file>img/UI20/toolbar/joints/SwingLimits.svg</file>
<file>img/UI20/toolbar/joints/TwistLimits.svg</file>
<file>img/triangle0.png</file>
<file>img/triangle0_highlighted.png</file>
<file>img/line.png</file>
@@ -20,7 +20,7 @@ AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
AZ_POP_DISABLE_WARNING
AZ_CVAR(
bool, ed_useNewAssetBrowserTableView, false, nullptr, AZ::ConsoleFunctorFlags::Null,
bool, ed_useNewAssetBrowserTableView, true, nullptr, AZ::ConsoleFunctorFlags::Null,
"Use the new AssetBrowser TableView for searching assets.");
namespace AzToolsFramework
{
@@ -29,7 +29,7 @@ AZ_PUSH_DISABLE_WARNING(4251 4244, "-Wunknown-warning-option") // disable warnin
AZ_POP_DISABLE_WARNING
AZ_CVAR(
bool, ed_hideAssetPickerPathColumn, false, nullptr, AZ::ConsoleFunctorFlags::Null,
bool, ed_hideAssetPickerPathColumn, true, nullptr, AZ::ConsoleFunctorFlags::Null,
"Hide AssetPicker path column for a clearer view.");
AZ_CVAR_EXTERNED(bool, ed_useNewAssetBrowserTableView);
@@ -818,7 +818,14 @@ namespace AzToolsFramework
auto linkIterator = m_linkIdMap.find(linkId);
if (linkIterator != m_linkIdMap.end())
{
return AreDirtyTemplatesPresent(linkIterator->second.GetSourceTemplateId());
if (AreDirtyTemplatesPresent(linkIterator->second.GetSourceTemplateId()))
{
return true;
}
else
{
continue;
}
}
}
return false;
@@ -29,13 +29,29 @@ namespace AzToolsFramework::ViewportUi::Internal
void ButtonGroup::SetHighlightedButton(ButtonId buttonId)
{
if (buttonId == m_highlightedButtonId) // the requested button is highlighted, so do nothing.
{
return;
}
if (auto buttonEntry = m_buttons.find(buttonId); buttonEntry != m_buttons.end())
{
for (auto& button : m_buttons)
{
button.second->m_state = Button::State::Deselected;
}
ClearHighlightedButton();
buttonEntry->second->m_state = Button::State::Selected;
m_highlightedButtonId = buttonId;
}
}
void ButtonGroup::ClearHighlightedButton()
{
if (m_highlightedButtonId == InvalidButtonId)
{
return;
}
if (auto buttonEntry = m_buttons.find(m_highlightedButtonId); buttonEntry != m_buttons.end())
{
buttonEntry->second->m_state = Button::State::Deselected;
m_highlightedButtonId = InvalidButtonId;
}
}
@@ -25,6 +25,7 @@ namespace AzToolsFramework::ViewportUi::Internal
~ButtonGroup() = default;
void SetHighlightedButton(ButtonId buttonId);
void ClearHighlightedButton();
void SetViewportUiElementId(ViewportUiElementId id);
ViewportUiElementId GetViewportUiElementId() const;
@@ -40,5 +41,6 @@ namespace AzToolsFramework::ViewportUi::Internal
AZ::Event<ButtonId> m_buttonTriggeredEvent;
ViewportUiElementId m_viewportUiId;
AZStd::unordered_map<ButtonId, AZStd::unique_ptr<Button>> m_buttons;
ButtonId m_highlightedButtonId = InvalidButtonId;
};
} // namespace AzToolsFramework::ViewportUi::Internal
@@ -50,6 +50,16 @@ namespace AzToolsFramework::ViewportUi
}
}
void ViewportUiManager::ClearClusterActiveButton(ClusterId clusterId)
{
if (auto clusterIt = m_clusterButtonGroups.find(clusterId); clusterIt != m_clusterButtonGroups.end())
{
auto cluster = clusterIt->second;
cluster->ClearHighlightedButton();
UpdateButtonGroupUi(cluster.get());
}
}
void ViewportUiManager::SetSwitcherActiveButton(const SwitcherId switcherId, const ButtonId buttonId)
{
if (auto switcherIt = m_switcherButtonGroups.find(switcherId); switcherIt != m_switcherButtonGroups.end())
@@ -30,6 +30,7 @@ namespace AzToolsFramework::ViewportUi
const ClusterId CreateCluster(Alignment align) override;
const SwitcherId CreateSwitcher(Alignment align) override;
void SetClusterActiveButton(ClusterId clusterId, ButtonId buttonId) override;
void ClearClusterActiveButton(ClusterId clusterId) override;
void SetSwitcherActiveButton(SwitcherId switcherId, ButtonId buttonId) override;
void SetClusterButtonLocked(ClusterId clusterId, ButtonId buttonId, bool isLocked) override;
void SetClusterButtonTooltip(ClusterId clusterId, ButtonId buttonId, const AZStd::string& tooltip) override;
@@ -59,6 +59,8 @@ namespace AzToolsFramework::ViewportUi
virtual const SwitcherId CreateSwitcher(Alignment align) = 0;
//! Sets the active button of the cluster. This is the button which will display as highlighted.
virtual void SetClusterActiveButton(ClusterId clusterId, ButtonId buttonId) = 0;
//! Clears the active button of the cluster if one is active. The button will no longer display as highlighted.
virtual void ClearClusterActiveButton(ClusterId clusterId) = 0;
//! Sets the active button of the switcher. This is the button which has a text label.
virtual void SetSwitcherActiveButton(SwitcherId clusterId, ButtonId buttonId) = 0;
//! Adds a locked overlay to the cluster button's icon.
@@ -127,6 +127,25 @@ namespace UnitTest
EXPECT_TRUE(button->m_state == AzToolsFramework::ViewportUi::Internal::Button::State::Selected);
}
TEST_F(ViewportUiManagerTestFixture, ClearClusterActiveButtonSetsButtonStateToDeselected)
{
// setup
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
auto buttonId = m_viewportManagerWrapper.GetViewportManager()->CreateClusterButton(clusterId, "");
auto clusterEntry = m_viewportManagerWrapper.GetViewportManager()->GetClusterMap().find(clusterId);
auto button = clusterEntry->second->GetButton(buttonId);
// first set a button to active
m_viewportManagerWrapper.GetViewportManager()->SetClusterActiveButton(clusterId, buttonId);
EXPECT_TRUE(button->m_state == AzToolsFramework::ViewportUi::Internal::Button::State::Selected);
// clear the active button on the cluster
m_viewportManagerWrapper.GetViewportManager()->ClearClusterActiveButton(clusterId);
// the button should now be deselected
EXPECT_TRUE(button->m_state == AzToolsFramework::ViewportUi::Internal::Button::State::Deselected);
}
TEST_F(ViewportUiManagerTestFixture, RegisterClusterEventHandlerConnectsHandlerToClusterEvent)
{
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
@@ -56,8 +56,7 @@ namespace AZ
//////////////////////////////////////////////////////////////////////////
virtual void OnBootstrapSceneReady([[maybe_unused]]AZ::RPI::Scene* bootstrapScene){}
virtual void OnFrameRateLimitChanged([[maybe_unused]]float fpsLimit){}
virtual void OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) = 0;
};
using NotificationBus = AZ::EBus<Notification>;
} // namespace Bootstrap
@@ -23,8 +23,6 @@ namespace AZ::Render::Bootstrap
virtual AZ::RPI::ScenePtr GetOrCreateAtomSceneFromAzScene(AzFramework::Scene* scene) = 0;
virtual bool EnsureDefaultRenderPipelineInstalledForScene(AZ::RPI::ScenePtr scene, AZ::RPI::ViewportContextPtr viewportContext) = 0;
virtual float GetFrameRateLimit() const = 0;
virtual void SetFrameRateLimit(float fpsLimit) = 0;
protected:
~Request() = default;
@@ -42,14 +42,7 @@
#include <AzCore/Console/IConsole.h>
#include <BootstrapSystemComponent_Traits_Platform.h>
static void OnFrameRateLimitChanged(const float& fpsLimit)
{
AZ::Render::Bootstrap::RequestBus::Broadcast(
&AZ::Render::Bootstrap::RequestBus::Events::SetFrameRateLimit, fpsLimit);
}
AZ_CVAR(AZ::CVarFixedString, r_default_pipeline_name, AZ_TRAIT_BOOTSTRAPSYSTEMCOMPONENT_PIPELINE_NAME, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Default Render pipeline name");
AZ_CVAR(float, r_fps_limit, 0, OnFrameRateLimitChanged, AZ::ConsoleFunctorFlags::Null, "The maximum framerate to render at, or 0 for unlimited");
namespace AZ
{
@@ -358,22 +351,6 @@ namespace AZ
return true;
}
float BootstrapSystemComponent::GetFrameRateLimit() const
{
return r_fps_limit;
}
void BootstrapSystemComponent::SetFrameRateLimit(float fpsLimit)
{
r_fps_limit = fpsLimit;
if (m_viewportContext)
{
m_viewportContext->SetFpsLimit(r_fps_limit);
}
Render::Bootstrap::NotificationBus::Broadcast(
&Render::Bootstrap::NotificationBus::Events::OnFrameRateLimitChanged, fpsLimit);
}
void BootstrapSystemComponent::CreateDefaultRenderPipeline()
{
EnsureDefaultRenderPipelineInstalledForScene(m_defaultScene, m_viewportContext);
@@ -413,11 +390,23 @@ namespace AZ
}
void BootstrapSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time)
{ }
{
// Temp: When running in the launcher without the legacy renderer
// we need to call RenderTick on the viewport context each frame.
if (m_viewportContext)
{
AZ::ApplicationTypeQuery appType;
ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::QueryApplicationType, appType);
if (appType.IsGame())
{
m_viewportContext->RenderTick();
}
}
}
int BootstrapSystemComponent::GetTickOrder()
{
return TICK_PRE_RENDER;
return TICK_LAST;
}
void BootstrapSystemComponent::OnWindowClosed()
@@ -69,8 +69,6 @@ namespace AZ
// Render::Bootstrap::RequestBus::Handler overrides ...
AZ::RPI::ScenePtr GetOrCreateAtomSceneFromAzScene(AzFramework::Scene* scene) override;
bool EnsureDefaultRenderPipelineInstalledForScene(AZ::RPI::ScenePtr scene, AZ::RPI::ViewportContextPtr viewportContext) override;
float GetFrameRateLimit() const override;
void SetFrameRateLimit(float fpsLimit) override;
protected:
// Component overrides ...
@@ -39,13 +39,24 @@ namespace AZ
//! Otherwise an attempt will be made to find or create a shared instance.
void RebuildInstance();
//! Release asset and instance references
void Release();
//! Return true if contained assets have not been loaded
bool RequiresLoading() const;
//! Applies property overrides to material instance
bool ApplyProperties();
//! Returns a string composed of the asset path.
AZStd::string ToString() const;
Data::Asset<RPI::MaterialAsset> m_materialAsset;
Data::Asset<RPI::MaterialAsset> m_defaultMaterialAsset;
Data::Instance<RPI::Material> m_materialInstance;
MaterialPropertyOverrideMap m_propertyOverrides;
RPI::MaterialModelUvOverrideMap m_matModUvOverrides;
bool m_materialInstancePreCreated = false;
};
using MaterialAssignmentMap = AZStd::unordered_map<MaterialAssignmentId, MaterialAssignment>;
@@ -71,9 +71,9 @@ namespace AZ
}
MaterialAssignment::MaterialAssignment(const AZ::Data::AssetId& materialAssetId)
: m_materialInstance()
: m_materialAsset(materialAssetId, AZ::AzTypeInfo<AZ::RPI::MaterialAsset>::Uuid())
, m_materialInstance()
{
m_materialAsset.Create(materialAssetId);
}
MaterialAssignment::MaterialAssignment(const Data::Asset<RPI::MaterialAsset>& asset)
@@ -90,12 +90,70 @@ namespace AZ
void MaterialAssignment::RebuildInstance()
{
if (m_materialInstancePreCreated)
{
return;
}
if (m_materialAsset.IsReady())
{
m_materialInstance =
m_propertyOverrides.empty() ? RPI::Material::FindOrCreate(m_materialAsset) : RPI::Material::Create(m_materialAsset);
m_materialInstance = m_propertyOverrides.empty() ? RPI::Material::FindOrCreate(m_materialAsset) : RPI::Material::Create(m_materialAsset);
AZ_Error("MaterialAssignment", m_materialInstance, "Material instance not initialized");
}
else if (m_defaultMaterialAsset.IsReady())
{
m_materialInstance = m_propertyOverrides.empty() ? RPI::Material::FindOrCreate(m_defaultMaterialAsset) : RPI::Material::Create(m_defaultMaterialAsset);
AZ_Error("MaterialAssignment", m_materialInstance, "Material instance not initialized");
}
}
void MaterialAssignment::Release()
{
if (!m_materialInstancePreCreated)
{
m_materialInstance = nullptr;
}
m_materialAsset.Release();
m_defaultMaterialAsset.Release();
}
bool MaterialAssignment::RequiresLoading() const
{
return
!m_materialInstancePreCreated &&
!m_materialAsset.IsReady() &&
!m_materialAsset.IsLoading() &&
!m_defaultMaterialAsset.IsReady() &&
!m_defaultMaterialAsset.IsLoading();
}
bool MaterialAssignment::ApplyProperties()
{
// if there is no instance or no properties there's nothing to apply
if (!m_materialInstance || m_propertyOverrides.empty())
{
return true;
}
if (m_materialInstance->CanCompile())
{
for (const auto& propertyPair : m_propertyOverrides)
{
if (!propertyPair.second.empty())
{
const auto& materialPropertyIndex = m_materialInstance->FindPropertyIndex(propertyPair.first);
if (!materialPropertyIndex.IsNull())
{
m_materialInstance->SetPropertyValue(
materialPropertyIndex, AZ::RPI::MaterialPropertyValue::FromAny(propertyPair.second));
}
}
}
return m_materialInstance->Compile();
}
return false;
}
AZStd::string MaterialAssignment::ToString() const
@@ -161,25 +161,19 @@ namespace AZ
//! Add this RenderPipeline to RPI system's RenderTick and it will be rendered whenever
//! the RPI system's RenderTick is called.
//! The RenderPipeline is rendered per RenderTick by default.
//! The RenderPipeline is rendered per RenderTick by default unless AddToRenderTickOnce() was called.
void AddToRenderTick();
//! Add this RenderPipeline to RPI system's RenderTick and it will be rendered every RenderTick
//! after the specified interval has elapsed since the last rendered frame.
//! @param renderInterval The desired time between rendered frames, in seconds.
void AddToRenderTickAtInterval(AZStd::chrono::duration<float> renderInterval);
//! Disable render for this RenderPipeline
void RemoveFromRenderTick();
~RenderPipeline();
enum class RenderMode : uint8_t
{
RenderEveryTick, //!< Render at each RPI system render tick.
RenderAtTargetRate, //!< Render on RPI system render tick after a target refresh rate interval has passed.
RenderOnce, //!< Render once in next RPI system render tick.
NoRender //!< Render disabled.
RenderEveryTick, // Render at each RPI system render tick
RenderOnce, // Render once in next RPI system render tick
NoRender // Render disabled.
};
//! Get current render mode
@@ -191,12 +185,6 @@ namespace AZ
//! Get draw filter mask
RHI::DrawFilterMask GetDrawFilterMask() const;
using FrameNotificationEvent = AZ::Event<>;
//! Notifies a listener when a frame is about to be prepared for render, before SRGs are bound.
void ConnectPrepareFrameHandler(FrameNotificationEvent::Handler& handler);
//! Notifies a listener when the rendering of a frame has finished
void ConnectEndFrameHandler(FrameNotificationEvent::Handler& handler);
private:
RenderPipeline() = default;
@@ -214,11 +202,8 @@ namespace AZ
void OnAddedToScene(Scene* scene);
void OnRemovedFromScene(Scene* scene);
// Called before this pipeline is about to be rendered and before SRGs are bound.
void OnPrepareFrame();
// Called when this pipeline is about to be rendered
void OnStartFrame();
void OnStartFrame(const TickTimeInfo& tick);
// Called when the rendering of current frame is finished.
void OnFrameEnd();
@@ -243,14 +228,8 @@ namespace AZ
PipelineViewMap m_pipelineViewsByTag;
// The system time when the last time this pipeline render was started
AZStd::chrono::system_clock::time_point m_lastRenderStartTime;
// The current system time, as of OnPrepareFrame's execution.
AZStd::chrono::system_clock::time_point m_lastRenderRequestTime;
// The target time between renders when m_renderMode is RenderMode::RenderAtTargetRate
AZStd::chrono::duration<float> m_targetRefreshRate;
/// The system time when the last time this pipeline render was started
float m_lastRenderStartTime = 0;
// RenderPipeline's name id, it will be used to identify the render pipeline when it's added to a Scene
RenderPipelineId m_nameId;
@@ -280,11 +259,7 @@ namespace AZ
RHI::DrawFilterTag m_drawFilterTag;
// A mask to filter draw items submitted by passes of this render pipeline.
// This mask is created from the value of m_drawFilterTag.
RHI::DrawFilterMask m_drawFilterMask = 0;
// Events for notification on render state
FrameNotificationEvent m_prepareFrameEvent;
FrameNotificationEvent m_endFrameEvent;
RHI::DrawFilterMask m_drawFilterMask = 0;
};
} // namespace RPI
@@ -67,9 +67,6 @@ namespace AZ
//! Notifies when the PrepareRender phase is ending
virtual void OnEndPrepareRender() {}
//! Notifies when the render tick for a given frame has finished.
virtual void OnFrameEnd() {}
};
using SceneNotificationBus = AZ::EBus<SceneNotification>;
@@ -51,21 +51,9 @@ namespace AZ
//! Sets the root scene associated with this viewport.
//! This does not provide a default render pipeline, one must be provided to enable rendering.
void SetRenderScene(ScenePtr scene);
//! Gets the maximum frame rate this viewport context's pipeline can render at, 0 for unlimited.
//! The target framerate for the pipeline will be determined by this frame limit and the
//! vsync settings for the current window.
float GetFpsLimit() const;
//! Sets the maximum frame rate this viewport context's pipeline can render at, 0 for unlimited.
//! The target framerate for the pipeline will be determined by this frame limit and the
//! vsync settings for the current window.
void SetFpsLimit(float fpsLimit);
//! Gets the target frame rate for this viewport context.
//! This returns the lowest of either the current VSync refresh rate
//! or 0 for an unlimited frame rate (if there's no FPS limit and vsync is off).
float GetTargetFrameRate() const;
//! Runs one simulation and render tick and renders a frame to this viewport's window.
//! @note This is likely to be replaced by a tick management system in the RPI.
void RenderTick();
//! Gets the current name of this ViewportContext.
//! This name is used to tie this ViewportContext to its View stack, and ViewportContexts may be
@@ -86,28 +74,19 @@ namespace AZ
//! \see AzFramework::WindowRequests::GetDpiScaleFactor
float GetDpiScalingFactor() const;
//! Gets the current vsync interval, as a divisor of the current refresh rate.
//! A value of 0 indicates that vsync is disabled.
uint32_t GetVsyncInterval() const;
//! Gets the current display refresh rate, in frames per second.
uint32_t GetRefreshRate() const;
// SceneNotificationBus interface overrides...
//! Ensures our default view remains set when our scene's render pipelines are modified.
void OnRenderPipelineAdded(RenderPipelinePtr pipeline) override;
//! Ensures our default view remains set when our scene's render pipelines are modified.
void OnRenderPipelineRemoved(RenderPipeline* pipeline) override;
//! OnBeginPrepareRender is forwarded to our RenderTick notification to allow subscribers to do rendering.
void OnBeginPrepareRender() override;
// WindowNotificationBus interface overrides...
//! Used to fire a notification when our window resizes.
void OnWindowResized(uint32_t width, uint32_t height) override;
//! Used to fire a notification when our window DPI changes.
void OnDpiScaleFactorChanged(float dpiScaleFactor) override;
//! Used to fire a notification when our vsync interval changes.
void OnVsyncIntervalChanged(uint32_t interval) override;
//! Used to fire a notification when our refresh rate changes.
void OnRefreshRateChanged(uint32_t refreshRate) override;
using SizeChangedEvent = AZ::Event<AzFramework::WindowSize>;
//! Notifies consumers when the viewport size has changed.
@@ -119,12 +98,6 @@ namespace AZ
//! Alternatively, connect to ViewportContextNotificationsBus and listen to ViewportContextNotifications::OnViewportDpiScalingChanged.
void ConnectDpiScalingFactorChangedHandler(ScalarChangedEvent::Handler& handler);
using UintChangedEvent = AZ::Event<uint32_t>;
//! Notifies consumers when the vsync interval has changed.
void ConnectVsyncIntervalChangedHandler(UintChangedEvent::Handler& handler);
//! Notifies consumers when the refresh rate has changed.
void ConnectRefreshRateChangedHandler(UintChangedEvent::Handler& handler);
using MatrixChangedEvent = AZ::Event<const AZ::Matrix4x4&>;
//! Notifies consumers when the view matrix has changed.
void ConnectViewMatrixChangedHandler(MatrixChangedEvent::Handler& handler);
@@ -166,24 +139,15 @@ namespace AZ
void SetDefaultView(ViewPtr view);
// Ensures our render pipeline's default camera matches ours.
void UpdatePipelineView();
// Ensures our render pipeline refresh rate matches our refresh rate.
void UpdatePipelineRefreshRate();
// Resets the current pipeline reference and ensures pipeline events are disconnected.
void ResetCurrentPipeline();
ScenePtr m_rootScene;
WindowContextSharedPtr m_windowContext;
ViewPtr m_defaultView;
AzFramework::WindowSize m_viewportSize;
float m_viewportDpiScaleFactor = 1.0f;
uint32_t m_vsyncInterval = 1;
uint32_t m_refreshRate = 60;
float m_fpsLimit = 0.f;
SizeChangedEvent m_sizeChangedEvent;
ScalarChangedEvent m_dpiScalingFactorChangedEvent;
UintChangedEvent m_vsyncIntervalChangedEvent;
UintChangedEvent m_refreshRateChangedEvent;
MatrixChangedEvent m_viewMatrixChangedEvent;
MatrixChangedEvent::Handler m_onViewMatrixChangedHandler;
MatrixChangedEvent m_projectionMatrixChangedEvent;
@@ -193,9 +157,6 @@ namespace AZ
ViewChangedEvent m_defaultViewChangedEvent;
ViewportIdEvent m_aboutToBeDestroyedEvent;
AZ::Event<>::Handler m_prepareFrameHandler;
AZ::Event<>::Handler m_endFrameHandler;
ViewportContextManager* m_manager;
RenderPipelinePtr m_currentPipeline;
Name m_name;
@@ -110,13 +110,11 @@ namespace AZ
virtual void OnViewportSizeChanged(AzFramework::WindowSize size){AZ_UNUSED(size);}
//! Called when the window DPI scaling changes for a given viewport context.
virtual void OnViewportDpiScalingChanged(float dpiScale){AZ_UNUSED(dpiScale);}
//! Called when the active view changes for a given viewport context.
//! Called when the active view for a given viewport context name changes.
virtual void OnViewportDefaultViewChanged(AZ::RPI::ViewPtr view){AZ_UNUSED(view);}
//! Called when the viewport is to be rendered.
//! Add draws to this function if they only need to be rendered to this viewport.
virtual void OnRenderTick(){}
//! Called when the viewport finishes rendering a frame.
virtual void OnFrameEnd(){}
//! Add draws to this functions if they only need to be rendered to this viewport.
virtual void OnRenderTick(){};
protected:
~ViewportContextNotifications() = default;
@@ -14,9 +14,6 @@
#include <RPI.Private/RPISystemComponent.h>
#include <Atom/RHI/Factory.h>
#include <Atom/RPI.Public/ViewportContextBus.h>
#include <Atom/RPI.Public/ViewportContext.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/IO/IOUtils.h>
@@ -96,29 +93,20 @@ namespace AZ
}
m_rpiSystem.Initialize(m_rpiDescriptor);
AZ::TickBus::Handler::BusConnect();
AZ::SystemTickBus::Handler::BusConnect();
}
void RPISystemComponent::Deactivate()
{
AZ::TickBus::Handler::BusDisconnect();
AZ::SystemTickBus::Handler::BusDisconnect();
m_rpiSystem.Shutdown();
}
void RPISystemComponent::OnTick([[maybe_unused]]float deltaTime, [[maybe_unused]]ScriptTimePoint time)
void RPISystemComponent::OnSystemTick()
{
if (deltaTime == 0.f)
{
return;
}
m_rpiSystem.SimulationTick();
m_rpiSystem.RenderTick();
}
int RPISystemComponent::GetTickOrder()
{
return AZ::ComponentTickBus::TICK_RENDER;
}
} // namespace RPI
} // namespace AZ
@@ -32,7 +32,7 @@ namespace AZ
*/
class RPISystemComponent final
: public AZ::Component
, private AZ::TickBus::Handler
, public AZ::SystemTickBus::Handler
{
public:
AZ_COMPONENT(RPISystemComponent, "{83E301F3-7A0C-4099-B530-9342B91B1BC0}");
@@ -50,9 +50,8 @@ namespace AZ
private:
RPISystemComponent(const RPISystemComponent&) = delete;
// TickBus overrides...
void OnTick(float deltaTime, ScriptTimePoint time) override;
int GetTickOrder() override;
// SystemTickBus overrides...
void OnSystemTick() override;
RPISystem m_rpiSystem;
@@ -93,11 +93,8 @@ namespace AZ
void Pass::SetEnabled(bool enabled)
{
if (m_flags.m_enabled != enabled)
{
m_flags.m_enabled = enabled;
OnHierarchyChange();
}
m_flags.m_enabled = enabled;
OnHierarchyChange();
}
bool Pass::IsEnabled() const
@@ -301,26 +301,6 @@ namespace AZ
m_drawFilterMask = 0;
}
void RenderPipeline::OnPrepareFrame()
{
m_lastRenderRequestTime = AZStd::chrono::system_clock::now();
// If we're attempting to render at a target interval, check to see if we're within
// 1ms of that interval, enabling rendering only if we are.
if (m_renderMode == RenderMode::RenderAtTargetRate)
{
constexpr AZStd::chrono::duration<float> updateThresholdMs(0.001f);
const bool shouldRender =
m_lastRenderRequestTime - m_lastRenderStartTime + updateThresholdMs >= m_targetRefreshRate;
m_rootPass->SetEnabled(shouldRender);
}
if (NeedsRender())
{
m_prepareFrameEvent.Signal();
}
}
void RenderPipeline::OnPassModified()
{
if (m_needsPassRecreate)
@@ -395,11 +375,11 @@ namespace AZ
m_scene->RemoveRenderPipeline(m_nameId);
}
void RenderPipeline::OnStartFrame()
void RenderPipeline::OnStartFrame(const TickTimeInfo& tick)
{
AZ_PROFILE_SCOPE(RPI, "RenderPipeline: OnStartFrame");
m_lastRenderStartTime = m_lastRenderRequestTime;
m_lastRenderStartTime = tick.m_currentGameTime;
OnPassModified();
@@ -427,7 +407,6 @@ namespace AZ
{
RemoveFromRenderTick();
}
m_endFrameEvent.Signal();
}
void RenderPipeline::CollectPersistentViews(AZStd::map<ViewPtr, RHI::DrawListMask>& outViewMasks) const
@@ -510,13 +489,6 @@ namespace AZ
m_renderMode = RenderMode::RenderEveryTick;
}
void RenderPipeline::AddToRenderTickAtInterval(AZStd::chrono::duration<float> renderInterval)
{
m_rootPass->SetEnabled(false);
m_renderMode = RenderMode::RenderAtTargetRate;
m_targetRefreshRate = renderInterval;
}
void RenderPipeline::RemoveFromRenderTick()
{
m_renderMode = RenderMode::NoRender;
@@ -530,7 +502,7 @@ namespace AZ
bool RenderPipeline::NeedsRender() const
{
return m_rootPass->IsEnabled();
return m_renderMode != RenderMode::NoRender;
}
RHI::DrawFilterTag RenderPipeline::GetDrawFilterTag() const
@@ -543,16 +515,6 @@ namespace AZ
return m_drawFilterMask;
}
void RenderPipeline::ConnectPrepareFrameHandler(FrameNotificationEvent::Handler& handler)
{
handler.Connect(m_prepareFrameEvent);
}
void RenderPipeline::ConnectEndFrameHandler(FrameNotificationEvent::Handler& handler)
{
handler.Connect(m_endFrameEvent);
}
void RenderPipeline::SetDrawFilterTag(RHI::DrawFilterTag tag)
{
m_drawFilterTag = tag;
+4 -17
View File
@@ -418,7 +418,7 @@ namespace AZ
}
}
void Scene::PrepareRender([[maybe_unused]]const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy)
void Scene::PrepareRender(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy)
{
AZ_PROFILE_SCOPE(RPI, "Scene: PrepareRender");
@@ -429,27 +429,20 @@ namespace AZ
SceneNotificationBus::Event(GetId(), &SceneNotification::OnBeginPrepareRender);
// Get active pipelines which need to be rendered and notify them of an impending frame.
// Get active pipelines which need to be rendered and notify them frame started
AZStd::vector<RenderPipelinePtr> activePipelines;
{
AZ_PROFILE_SCOPE(RPI, "Scene: OnPrepareFrame");
AZ_PROFILE_SCOPE(RPI, "Scene: OnStartFrame");
for (auto& pipeline : m_pipelines)
{
pipeline->OnPrepareFrame();
if (pipeline->NeedsRender())
{
activePipelines.push_back(pipeline);
pipeline->OnStartFrame(tickInfo);
}
}
}
// Get active pipelines which need to be rendered and notify them frame started
for (const auto& pipeline : activePipelines)
{
AZ_PROFILE_SCOPE(RPI, "Scene: OnStartFrame");
pipeline->OnStartFrame();
}
// Return if there is no active render pipeline
if (activePipelines.empty())
{
@@ -589,12 +582,10 @@ namespace AZ
void Scene::OnFrameEnd()
{
AZ_PROFILE_SCOPE(RPI, "Scene: OnFrameEnd");
bool didRender = false;
for (auto& pipeline : m_pipelines)
{
if (pipeline->NeedsRender())
{
didRender = true;
pipeline->OnFrameEnd();
}
}
@@ -602,10 +593,6 @@ namespace AZ
{
fp->OnRenderEnd();
}
if (didRender)
{
SceneNotificationBus::Event(GetId(), &SceneNotification::OnFrameEnd);
}
}
void Scene::UpdateSrgs()
@@ -241,10 +241,7 @@ namespace AZ
{
AZ_PROFILE_SCOPE(RPI, "View: FinalizeDrawLists");
m_drawListContext.FinalizeLists();
if (m_passesByDrawList)
{
SortFinalizedDrawLists();
}
SortFinalizedDrawLists();
}
void View::SortFinalizedDrawLists()
@@ -25,13 +25,14 @@ namespace AZ
, m_viewportSize(1, 1)
{
m_windowContext->Initialize(device, nativeWindow);
AzFramework::WindowRequestBus::Event(nativeWindow, [this](AzFramework::WindowRequestBus::Events* window)
{
m_viewportSize = window->GetClientAreaSize();
m_viewportDpiScaleFactor = window->GetDpiScaleFactor();
m_vsyncInterval = window->GetSyncInterval();
m_refreshRate = window->GetDisplayRefreshRate();
});
AzFramework::WindowRequestBus::EventResult(
m_viewportSize,
nativeWindow,
&AzFramework::WindowRequestBus::Events::GetClientAreaSize);
AzFramework::WindowRequestBus::EventResult(
m_viewportDpiScaleFactor,
nativeWindow,
&AzFramework::WindowRequestBus::Events::GetDpiScaleFactor);
AzFramework::WindowNotificationBus::Handler::BusConnect(nativeWindow);
AzFramework::ViewportRequestBus::Handler::BusConnect(id);
@@ -45,20 +46,6 @@ namespace AZ
m_viewMatrixChangedEvent.Signal(matrix);
});
m_prepareFrameHandler = RenderPipeline::FrameNotificationEvent::Handler(
[this]()
{
ViewportContextNotificationBus::Event(GetName(), &ViewportContextNotificationBus::Events::OnRenderTick);
ViewportContextIdNotificationBus::Event(GetId(), &ViewportContextIdNotificationBus::Events::OnRenderTick);
});
m_endFrameHandler = RenderPipeline::FrameNotificationEvent::Handler(
[this]()
{
ViewportContextNotificationBus::Event(GetName(), &ViewportContextNotificationBus::Events::OnFrameEnd);
ViewportContextIdNotificationBus::Event(GetId(), &ViewportContextIdNotificationBus::Events::OnFrameEnd);
});
SetRenderScene(renderScene);
}
@@ -124,38 +111,26 @@ namespace AZ
{
SceneNotificationBus::Handler::BusConnect(m_rootScene->GetId());
}
ResetCurrentPipeline();
m_currentPipeline.reset();
UpdatePipelineView();
UpdatePipelineRefreshRate();
}
m_sceneChangedEvent.Signal(scene);
}
float ViewportContext::GetFpsLimit() const
void ViewportContext::RenderTick()
{
return m_fpsLimit;
}
void ViewportContext::SetFpsLimit(float fpsLimit)
{
m_fpsLimit = fpsLimit;
UpdatePipelineRefreshRate();
}
float ViewportContext::GetTargetFrameRate() const
{
float targetFrameRate = GetFpsLimit();
const AZ::u32 vsyncInterval = GetVsyncInterval();
if (vsyncInterval != 0)
// add the current pipeline to next render tick if it's not already added.
if (m_currentPipeline && m_currentPipeline->GetRenderMode() != RenderPipeline::RenderMode::RenderOnce)
{
const float vsyncFrameRate = static_cast<float>(GetRefreshRate()) / static_cast<float>(vsyncInterval);
if (targetFrameRate == 0.f || vsyncFrameRate < targetFrameRate)
{
targetFrameRate = vsyncFrameRate;
}
m_currentPipeline->AddToRenderTickOnce();
}
return targetFrameRate;
}
void ViewportContext::OnBeginPrepareRender()
{
ViewportContextNotificationBus::Event(GetName(), &ViewportContextNotificationBus::Events::OnRenderTick);
ViewportContextIdNotificationBus::Event(GetId(), &ViewportContextIdNotificationBus::Events::OnRenderTick);
}
AZ::Name ViewportContext::GetName() const
@@ -183,16 +158,6 @@ namespace AZ
return m_viewportDpiScaleFactor;
}
uint32_t ViewportContext::GetVsyncInterval() const
{
return m_vsyncInterval;
}
uint32_t ViewportContext::GetRefreshRate() const
{
return m_refreshRate;
}
void ViewportContext::ConnectSizeChangedHandler(SizeChangedEvent::Handler& handler)
{
handler.Connect(m_sizeChangedEvent);
@@ -203,16 +168,6 @@ namespace AZ
handler.Connect(m_dpiScalingFactorChangedEvent);
}
void ViewportContext::ConnectVsyncIntervalChangedHandler(UintChangedEvent::Handler& handler)
{
handler.Connect(m_vsyncIntervalChangedEvent);
}
void ViewportContext::ConnectRefreshRateChangedHandler(UintChangedEvent::Handler& handler)
{
handler.Connect(m_refreshRateChangedEvent);
}
void ViewportContext::ConnectViewMatrixChangedHandler(MatrixChangedEvent::Handler& handler)
{
handler.Connect(m_viewMatrixChangedEvent);
@@ -308,43 +263,12 @@ namespace AZ
m_currentPipelineChangedEvent.Signal(m_currentPipeline);
}
if (m_currentPipeline)
if (auto pipeline = GetCurrentPipeline())
{
if (!m_prepareFrameHandler.IsConnected())
{
m_currentPipeline->ConnectPrepareFrameHandler(m_prepareFrameHandler);
m_currentPipeline->ConnectEndFrameHandler(m_endFrameHandler);
}
m_currentPipeline->SetDefaultView(m_defaultView);
pipeline->SetDefaultView(m_defaultView);
}
}
void ViewportContext::UpdatePipelineRefreshRate()
{
if (!m_currentPipeline)
{
return;
}
const float refreshRate = GetTargetFrameRate();
// If we have a truly unlimited framerate, just render every tick
if (refreshRate == 0.f)
{
m_currentPipeline->AddToRenderTick();
}
else
{
m_currentPipeline->AddToRenderTickAtInterval(AZStd::chrono::duration<float>(1.f / refreshRate));
}
}
void ViewportContext::ResetCurrentPipeline()
{
m_prepareFrameHandler.Disconnect();
m_endFrameHandler.Disconnect();
m_currentPipeline.reset();
}
RenderPipelinePtr ViewportContext::GetCurrentPipeline()
{
return m_currentPipeline;
@@ -357,9 +281,8 @@ namespace AZ
// in the event prioritization is added later
if (pipeline->GetWindowHandle() == m_windowContext->GetWindowHandle())
{
ResetCurrentPipeline();
m_currentPipeline.reset();
UpdatePipelineView();
UpdatePipelineRefreshRate();
}
}
@@ -367,9 +290,8 @@ namespace AZ
{
if (m_currentPipeline.get() == pipeline)
{
ResetCurrentPipeline();
m_currentPipeline.reset();
UpdatePipelineView();
UpdatePipelineRefreshRate();
}
}
@@ -383,30 +305,10 @@ namespace AZ
}
}
void ViewportContext::OnRefreshRateChanged(uint32_t refreshRate)
{
if (m_refreshRate != refreshRate)
{
m_refreshRate = refreshRate;
m_refreshRateChangedEvent.Signal(m_refreshRate);
UpdatePipelineRefreshRate();
}
}
void ViewportContext::OnDpiScaleFactorChanged(float dpiScaleFactor)
{
m_viewportDpiScaleFactor = dpiScaleFactor;
m_dpiScalingFactorChangedEvent.Signal(dpiScaleFactor);
}
void ViewportContext::OnVsyncIntervalChanged(uint32_t interval)
{
if (m_vsyncInterval != interval)
{
m_vsyncInterval = interval;
m_vsyncIntervalChangedEvent.Signal(m_vsyncInterval);
UpdatePipelineRefreshRate();
}
}
} // namespace RPI
} // namespace AZ
@@ -10,7 +10,6 @@
#include <QWidget>
#include <QElapsedTimer>
#include <QPointer>
#include <Atom/RPI.Public/Base.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <AzToolsFramework/Input/QtEventToAzInputManager.h>
@@ -22,8 +21,6 @@
#include <AzFramework/Windowing/WindowBus.h>
#include <AzCore/Component/TickBus.h>
#include <Atom/RPI.Public/AuxGeom/AuxGeomFeatureProcessorInterface.h>
#include <Atom/Bootstrap/BootstrapNotificationBus.h>
#include <AtomToolsFramework/Viewport/RenderViewportWidgetNotificationBus.h>
namespace AtomToolsFramework
{
@@ -38,8 +35,6 @@ namespace AtomToolsFramework
, public AzFramework::WindowRequestBus::Handler
, protected AzFramework::InputChannelEventListener
, protected AZ::TickBus::Handler
, protected AZ::Render::Bootstrap::NotificationBus::Handler
, protected AtomToolsFramework::RenderViewportWidgetNotificationBus::Handler
{
public:
//! Creates a RenderViewportWidget.
@@ -126,7 +121,6 @@ namespace AtomToolsFramework
// AZ::TickBus::Handler ...
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
int GetTickOrder() override;
// QWidget ...
void resizeEvent(QResizeEvent *event) override;
@@ -134,21 +128,9 @@ namespace AtomToolsFramework
void enterEvent(QEvent* event) override;
void leaveEvent(QEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void focusInEvent(QFocusEvent* event) override;
// AZ::Render::Bootstrap::NotificationBus::Handler ...
void OnFrameRateLimitChanged(float fpsLimit) override;
// AtomToolsFramework::RenderViewportWidgetNotificationBus::Handler ...
void OnInactiveViewportFrameRateChanged(float fpsLimit) override;
private:
AzFramework::NativeWindowHandle GetNativeWindowHandle() const;
void UpdateFrameRate();
void SetScreen(QScreen* screen);
void SendWindowResizeEvent();
void NotifyUpdateRefreshRate();
// The underlying ViewportContext, our entry-point to the Atom RPI.
AZ::RPI::ViewportContextPtr m_viewportContext;
@@ -169,11 +151,5 @@ namespace AtomToolsFramework
AZ::ScriptTimePoint m_time;
// Maps our internal Qt events into AzFramework InputChannels for our ViewportControllerList.
AzToolsFramework::QtEventToAzInputMapper* m_inputChannelMapper = nullptr;
// Stores our current screen, used for tracking the current refresh rate.
QScreen* m_screen = nullptr;
// Stores the last RenderViewportWidget that has received user focus.
// This is used for optional framerate throtting for "inactive" viewports via the
// ed_inactive_viewport_fps_limit CVAR.
AZ::EnvironmentVariable<RenderViewportWidget*> m_lastFocusedViewport;
};
} //namespace AtomToolsFramework
@@ -1,35 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
namespace AtomToolsFramework
{
//! Provides an interface for providing notifications specific to RenderViewportWidget.
//! @note Most behaviors in RenderViewportWidget are handled by its underyling
//! ViewportContext, this bus is specifically for functionality exclusive to the
//! Qt layer provided by RenderViewportWidget.
class RenderViewportWidgetNotifications : public AZ::EBusTraits
{
public:
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
//! Triggered when the idle frame rate limit for inactive viewports changed.
//! Controlled by the ed_inactive_viewport_fps_limit CVAR.
//! Active viewports are controlled by the r_fps_limit CVAR.
virtual void OnInactiveViewportFrameRateChanged([[maybe_unused]]float fpsLimit){}
protected:
~RenderViewportWidgetNotifications() = default;
};
using RenderViewportWidgetNotificationBus = AZ::EBus<RenderViewportWidgetNotifications>;
} // namespace AtomToolsFramework
@@ -6,42 +6,23 @@
*
*/
#include <Atom/Bootstrap/BootstrapRequestBus.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RPI.Public/View.h>
#include <AtomToolsFramework/Viewport/RenderViewportWidget.h>
#include <Atom/RPI.Public/ViewportContext.h>
#include <Atom/RPI.Public/ViewportContextBus.h>
#include <AtomToolsFramework/Viewport/RenderViewportWidget.h>
#include <AzCore/Console/Console.h>
#include <AzCore/Math/MathUtils.h>
#include <Atom/RPI.Public/View.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Viewport/ViewportControllerList.h>
#include <AzFramework/Viewport/ViewportScreen.h>
#include <AzToolsFramework/Viewport/ViewportTypes.h>
#include <AzCore/Math/MathUtils.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/Bootstrap/BootstrapRequestBus.h>
#include <QApplication>
#include <QBoxLayout>
#include <QCursor>
#include <QMouseEvent>
#include <QScreen>
#include <QTimer>
#include <QBoxLayout>
#include <QWindow>
static void OnInactiveViewportFrameRateChanged(const float& fpsLimit)
{
AtomToolsFramework::RenderViewportWidgetNotificationBus::Broadcast(
&AtomToolsFramework::RenderViewportWidgetNotificationBus::Events::OnInactiveViewportFrameRateChanged, fpsLimit);
}
AZ_CVAR(
float,
ed_inactive_viewport_fps_limit,
0,
OnInactiveViewportFrameRateChanged,
AZ::ConsoleFunctorFlags::Null,
"The maximum framerate to render viewports that don't have focus at");
static constexpr const char* LastFocusedViewportVariableName = "AtomToolsFramework::RenderViewportWidget::LastFocusedViewport";
#include <QMouseEvent>
namespace AtomToolsFramework
{
@@ -49,12 +30,6 @@ namespace AtomToolsFramework
: QWidget(parent)
, AzFramework::InputChannelEventListener(AzFramework::InputChannelEventListener::GetPriorityDefault())
{
m_lastFocusedViewport = AZ::Environment::FindVariable<RenderViewportWidget*>(LastFocusedViewportVariableName);
if (!m_lastFocusedViewport)
{
m_lastFocusedViewport = AZ::Environment::CreateVariable<RenderViewportWidget*>(LastFocusedViewportVariableName, nullptr);
}
if (shouldInitializeViewportContext)
{
InitializeViewportContext();
@@ -63,24 +38,13 @@ namespace AtomToolsFramework
setUpdatesEnabled(false);
setFocusPolicy(Qt::FocusPolicy::WheelFocus);
setMouseTracking(true);
// Wait a frame for our window handle to be constructed, then wire up our screen change signals.
QTimer::singleShot(
0,
[this]()
{
QObject::connect(windowHandle(), &QWindow::screenChanged, this, &RenderViewportWidget::SetScreen);
});
SetScreen(screen());
}
bool RenderViewportWidget::InitializeViewportContext(AzFramework::ViewportId id)
{
if (m_viewportContext != nullptr)
{
AZ_Assert(
id == AzFramework::InvalidViewportId || m_viewportContext->GetId() == id,
"Attempted to reinitialize RenderViewportWidget with a different ID");
AZ_Assert(id == AzFramework::InvalidViewportId || m_viewportContext->GetId() == id, "Attempted to reinitialize RenderViewportWidget with a different ID");
return true;
}
@@ -95,7 +59,7 @@ namespace AtomToolsFramework
// Before we do anything else, we must create a ViewportContext which will give us a ViewportId if we didn't manually specify one.
AZ::RPI::ViewportContextRequestsInterface::CreationParameters params;
params.device = AZ::RHI::RHISystemInterface::Get()->GetDevice();
params.windowHandle = GetNativeWindowHandle();
params.windowHandle = reinterpret_cast<AzFramework::NativeWindowHandle>(winId());
params.id = id;
AzFramework::WindowRequestBus::Handler::BusConnect(params.windowHandle);
m_viewportContext = viewportContextManager->CreateViewportContext(AZ::Name(), params);
@@ -116,46 +80,29 @@ namespace AtomToolsFramework
AzFramework::InputChannelEventListener::Connect();
AZ::TickBus::Handler::BusConnect();
AzFramework::WindowRequestBus::Handler::BusConnect(params.windowHandle);
AZ::Render::Bootstrap::NotificationBus::Handler::BusConnect();
AtomToolsFramework::RenderViewportWidgetNotificationBus::Handler::BusConnect();
m_inputChannelMapper = new AzToolsFramework::QtEventToAzInputMapper(this, id);
// Forward input events to our controller list.
QObject::connect(
m_inputChannelMapper, &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, this,
QObject::connect(m_inputChannelMapper, &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, this,
[this](const AzFramework::InputChannel* inputChannel, QEvent* event)
{
const AzFramework::NativeWindowHandle windowId = GetNativeWindowHandle();
if (m_controllerList->HandleInputChannelEvent(
AzFramework::ViewportControllerInputEvent{ GetId(), windowId, *inputChannel }))
{
// If the controller handled the input event, mark the event as accepted so it doesn't continue to propagate.
if (event)
{
event->setAccepted(true);
}
}
});
// Update our target frame rate. If we're the only viewport, become active.
if (m_lastFocusedViewport.Get() == nullptr)
{
m_lastFocusedViewport.Set(this);
}
UpdateFrameRate();
AzFramework::NativeWindowHandle windowId = reinterpret_cast<AzFramework::NativeWindowHandle>(winId());
if (m_controllerList->HandleInputChannelEvent(AzFramework::ViewportControllerInputEvent{GetId(), windowId, *inputChannel}))
{
// If the controller handled the input event, mark the event as accepted so it doesn't continue to propagate.
if (event)
{
event->setAccepted(true);
}
}
});
return true;
}
RenderViewportWidget::~RenderViewportWidget()
{
if (m_lastFocusedViewport.Get() == this)
{
m_lastFocusedViewport.Set(nullptr);
}
AzFramework::WindowRequestBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
AzFramework::InputChannelEventListener::Disconnect();
@@ -234,22 +181,17 @@ namespace AtomToolsFramework
bool shouldConsumeEvent = true;
const bool eventHandled = m_controllerList->HandleInputChannelEvent({ GetId(), GetNativeWindowHandle(), inputChannel });
AzFramework::NativeWindowHandle windowId = reinterpret_cast<AzFramework::NativeWindowHandle>(winId());
const bool eventHandled = m_controllerList->HandleInputChannelEvent({GetId(), windowId, inputChannel});
// If our controllers handled the event and it's one we can safely consume (i.e. it's not an Ended event that other viewports might
// need), consume it.
// If our controllers handled the event and it's one we can safely consume (i.e. it's not an Ended event that other viewports might need), consume it.
return eventHandled && shouldConsumeEvent;
}
void RenderViewportWidget::OnTick([[maybe_unused]] float deltaTime, AZ::ScriptTimePoint time)
void RenderViewportWidget::OnTick([[maybe_unused]]float deltaTime, AZ::ScriptTimePoint time)
{
m_time = time;
m_controllerList->UpdateViewport({ GetId(), AzFramework::FloatSeconds(deltaTime), m_time });
}
int RenderViewportWidget::GetTickOrder()
{
return AZ::ComponentTickBus::TICK_PRE_RENDER;
m_controllerList->UpdateViewport({GetId(), AzFramework::FloatSeconds(deltaTime), m_time});
}
void RenderViewportWidget::resizeEvent([[maybe_unused]] QResizeEvent* event)
@@ -277,75 +219,6 @@ namespace AtomToolsFramework
m_mousePosition = event->localPos();
}
void RenderViewportWidget::focusInEvent([[maybe_unused]] QFocusEvent* event)
{
RenderViewportWidget* lastFocusedViewport = m_lastFocusedViewport.Get();
if (lastFocusedViewport == this)
{
return;
}
RenderViewportWidget* previousFocusWidget = lastFocusedViewport;
m_lastFocusedViewport.Set(this);
// Ensure this viewport and whatever viewport last had focus (if any) respect
// the active / inactive viewport frame rate settings.
UpdateFrameRate();
if (previousFocusWidget != nullptr)
{
previousFocusWidget->UpdateFrameRate();
}
}
void RenderViewportWidget::OnFrameRateLimitChanged([[maybe_unused]] float fpsLimit)
{
UpdateFrameRate();
}
void RenderViewportWidget::OnInactiveViewportFrameRateChanged([[maybe_unused]] float fpsLimit)
{
UpdateFrameRate();
}
AzFramework::NativeWindowHandle RenderViewportWidget::GetNativeWindowHandle() const
{
return reinterpret_cast<AzFramework::NativeWindowHandle>(winId());
}
void RenderViewportWidget::UpdateFrameRate()
{
if (ed_inactive_viewport_fps_limit > 0.f && m_lastFocusedViewport.Get() != this)
{
m_viewportContext->SetFpsLimit(ed_inactive_viewport_fps_limit);
}
else
{
float fpsLimit = 0.f;
AZ::Render::Bootstrap::RequestBus::BroadcastResult(fpsLimit, &AZ::Render::Bootstrap::RequestBus::Events::GetFrameRateLimit);
m_viewportContext->SetFpsLimit(fpsLimit);
}
}
void RenderViewportWidget::SetScreen(QScreen* screen)
{
if (m_screen != screen)
{
if (m_screen)
{
QObject::disconnect(m_screen, &QScreen::refreshRateChanged, this, &RenderViewportWidget::NotifyUpdateRefreshRate);
}
if (screen)
{
QObject::connect(m_screen, &QScreen::refreshRateChanged, this, &RenderViewportWidget::NotifyUpdateRefreshRate);
}
NotifyUpdateRefreshRate();
m_screen = screen;
}
}
void RenderViewportWidget::SendWindowResizeEvent()
{
// Scale the size by the DPI of the platform to
@@ -353,14 +226,8 @@ namespace AtomToolsFramework
const QSize uiWindowSize = size();
const QSize windowSize = uiWindowSize * devicePixelRatioF();
AzFramework::WindowNotificationBus::Event(
GetNativeWindowHandle(), &AzFramework::WindowNotifications::OnWindowResized, windowSize.width(), windowSize.height());
}
void RenderViewportWidget::NotifyUpdateRefreshRate()
{
AzFramework::WindowNotificationBus::Event(
GetNativeWindowHandle(), &AzFramework::WindowNotificationBus::Events::OnRefreshRateChanged, GetDisplayRefreshRate());
const AzFramework::NativeWindowHandle windowId = reinterpret_cast<AzFramework::NativeWindowHandle>(winId());
AzFramework::WindowNotificationBus::Event(windowId, &AzFramework::WindowNotifications::OnWindowResized, windowSize.width(), windowSize.height());
}
AZ::Name RenderViewportWidget::GetCurrentContextName() const
@@ -418,7 +285,9 @@ namespace AtomToolsFramework
// Build camera state from Atom camera transforms
AzFramework::CameraState cameraState = AzFramework::CreateCameraFromWorldFromViewMatrix(
currentView->GetViewToWorldMatrix(), AZ::Vector2{ aznumeric_cast<float>(width()), aznumeric_cast<float>(height()) });
currentView->GetViewToWorldMatrix(),
AZ::Vector2{aznumeric_cast<float>(width()), aznumeric_cast<float>(height())}
);
AzFramework::SetCameraClippingVolumeFromPerspectiveFovMatrixRH(cameraState, currentView->GetViewToClipMatrix());
// Convert from Z-up
@@ -430,7 +299,8 @@ namespace AtomToolsFramework
AzFramework::ScreenPoint RenderViewportWidget::ViewportWorldToScreen(const AZ::Vector3& worldPosition)
{
if (AZ::RPI::ViewPtr currentView = m_viewportContext->GetDefaultView(); currentView == nullptr)
if (AZ::RPI::ViewPtr currentView = m_viewportContext->GetDefaultView();
currentView == nullptr)
{
return AzFramework::ScreenPoint(0, 0);
}
@@ -443,10 +313,12 @@ namespace AtomToolsFramework
const auto& cameraProjection = m_viewportContext->GetCameraProjectionMatrix();
const auto& cameraView = m_viewportContext->GetCameraViewMatrix();
const AZ::Vector4 normalizedScreenPosition{ screenPosition.m_x * 2.f / width() - 1.0f,
(height() - screenPosition.m_y) * 2.f / height() - 1.0f,
1.f - depth, // [GFX TODO] [ATOM-1501] Currently we always assume reverse depth
1.f };
const AZ::Vector4 normalizedScreenPosition {
screenPosition.m_x * 2.f / width() - 1.0f,
(height() - screenPosition.m_y) * 2.f / height() - 1.0f,
1.f - depth, // [GFX TODO] [ATOM-1501] Currently we always assume reverse depth
1.f
};
AZ::Matrix4x4 worldFromScreen = cameraProjection * cameraView;
worldFromScreen.InvertFull();
@@ -475,7 +347,7 @@ namespace AtomToolsFramework
AZ::Vector3 rayDirection = pos1.value() - pos0.value();
rayDirection.Normalize();
return AzToolsFramework::ViewportInteraction::ProjectedViewportRay{ rayOrigin, rayDirection };
return AzToolsFramework::ViewportInteraction::ProjectedViewportRay{rayOrigin, rayDirection};
}
float RenderViewportWidget::DeviceScalingFactor()
@@ -505,12 +377,12 @@ namespace AtomToolsFramework
AzFramework::WindowSize RenderViewportWidget::GetClientAreaSize() const
{
return AzFramework::WindowSize{ aznumeric_cast<uint32_t>(width()), aznumeric_cast<uint32_t>(height()) };
return AzFramework::WindowSize{aznumeric_cast<uint32_t>(width()), aznumeric_cast<uint32_t>(height())};
}
void RenderViewportWidget::ResizeClientArea(AzFramework::WindowSize clientAreaSize)
{
const QSize targetSize = QSize{ aznumeric_cast<int>(clientAreaSize.m_width), aznumeric_cast<int>(clientAreaSize.m_height) };
const QSize targetSize = QSize{aznumeric_cast<int>(clientAreaSize.m_width), aznumeric_cast<int>(clientAreaSize.m_height)};
resize(targetSize);
}
@@ -520,7 +392,7 @@ namespace AtomToolsFramework
return false;
}
void RenderViewportWidget::SetFullScreenState([[maybe_unused]] bool fullScreenState)
void RenderViewportWidget::SetFullScreenState([[maybe_unused]]bool fullScreenState)
{
// The RenderViewportWidget does not currently support full screen.
}
@@ -543,20 +415,11 @@ namespace AtomToolsFramework
uint32_t RenderViewportWidget::GetDisplayRefreshRate() const
{
return static_cast<uint32_t>(screen()->refreshRate());
return 60;
}
uint32_t RenderViewportWidget::GetSyncInterval() const
{
uint32_t interval = 1;
// Get vsync_interval from AzFramework::NativeWindow, which owns it.
// NativeWindow also handles broadcasting OnVsyncIntervalChanged to all
// WindowNotificationBus listeners.
if (auto console = AZ::Interface<AZ::IConsole>::Get())
{
console->GetCvarValue<uint32_t>("vsync_interval", interval);
}
return interval;
return 1;
}
} // namespace AtomToolsFramework
} //namespace AtomToolsFramework
@@ -28,7 +28,6 @@ set(FILES
Include/AtomToolsFramework/Util/MaterialPropertyUtil.h
Include/AtomToolsFramework/Util/Util.h
Include/AtomToolsFramework/Viewport/RenderViewportWidget.h
Include/AtomToolsFramework/Viewport/RenderViewportWidgetNotificationBus.h
Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h
Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h
Include/AtomToolsFramework/Window/AtomToolsMainWindow.h
@@ -182,7 +182,9 @@ namespace MaterialEditor
AZ_Error("MaterialViewportRenderer", m_shadowCatcherMaterial != nullptr, "Could not create shadow catcher material.");
AZ::Render::MaterialAssignmentMap shadowCatcherMaterials;
shadowCatcherMaterials[AZ::Render::DefaultMaterialAssignmentId].m_materialInstance = m_shadowCatcherMaterial;
auto& shadowCatcherMaterialAssignment = shadowCatcherMaterials[AZ::Render::DefaultMaterialAssignmentId];
shadowCatcherMaterialAssignment.m_materialInstance = m_shadowCatcherMaterial;
shadowCatcherMaterialAssignment.m_materialInstancePreCreated = true;
AZ::Render::MaterialComponentRequestBus::Event(m_shadowCatcherEntity->GetId(),
&AZ::Render::MaterialComponentRequestBus::Events::SetMaterialOverrides, shadowCatcherMaterials);
@@ -291,7 +293,9 @@ namespace MaterialEditor
MaterialDocumentRequestBus::EventResult(materialInstance, documentId, &MaterialDocumentRequestBus::Events::GetInstance);
AZ::Render::MaterialAssignmentMap materials;
materials[AZ::Render::DefaultMaterialAssignmentId].m_materialInstance = materialInstance;
auto& materialAssignment = materials[AZ::Render::DefaultMaterialAssignmentId];
materialAssignment.m_materialInstance = materialInstance;
materialAssignment.m_materialInstancePreCreated = true;
AZ::Render::MaterialComponentRequestBus::Event(m_modelEntity->GetId(),
&AZ::Render::MaterialComponentRequestBus::Events::SetMaterialOverrides, materials);
@@ -183,15 +183,6 @@ namespace AZ::Render
DrawFramerate();
}
void AtomViewportDisplayInfoSystemComponent::OnFrameEnd()
{
auto currentTime = AZStd::chrono::system_clock::now();
if (!m_fpsHistory.empty())
{
m_fpsHistory.back().m_endFrameTime = currentTime;
}
}
AtomBridge::ViewportInfoDisplayState AtomViewportDisplayInfoSystemComponent::GetDisplayState() const
{
return aznumeric_cast<AtomBridge::ViewportInfoDisplayState>(r_displayInfo.operator int());
@@ -257,11 +248,11 @@ namespace AZ::Render
void AtomViewportDisplayInfoSystemComponent::UpdateFramerate()
{
auto currentTime = AZStd::chrono::system_clock::now();
while (!m_fpsHistory.empty() && (currentTime - m_fpsHistory.front().m_beginFrameTime) > m_fpsInterval)
while (!m_fpsHistory.empty() && (currentTime - m_fpsHistory.front()) > m_fpsInterval)
{
m_fpsHistory.pop_front();
}
m_fpsHistory.push_back(FrameTimingInfo(currentTime));
m_fpsHistory.push_back(currentTime);
}
void AtomViewportDisplayInfoSystemComponent::DrawFramerate()
@@ -270,31 +261,25 @@ namespace AZ::Render
double minFPS = DBL_MAX;
double maxFPS = 0;
AZStd::chrono::duration<double> deltaTime;
AZStd::chrono::milliseconds totalFrameMS(0);
for (const auto& time : m_fpsHistory)
{
if (lastTime.has_value())
{
deltaTime = time.m_beginFrameTime - lastTime.value();
deltaTime = time - lastTime.value();
double fps = AZStd::chrono::seconds(1) / deltaTime;
minFPS = AZStd::min(minFPS, fps);
maxFPS = AZStd::max(maxFPS, fps);
}
lastTime = time.m_beginFrameTime;
if (time.m_endFrameTime.has_value())
{
totalFrameMS += time.m_endFrameTime.value() - time.m_beginFrameTime;
}
lastTime = time;
}
double averageFPS = 0;
double averageFrameMs = 0;
if (m_fpsHistory.size() > 1)
{
deltaTime = m_fpsHistory.back().m_beginFrameTime - m_fpsHistory.front().m_beginFrameTime;
averageFPS = AZStd::chrono::seconds(m_fpsHistory.size() - 1) / deltaTime;
averageFrameMs = aznumeric_cast<double>(totalFrameMS.count()) / (m_fpsHistory.size() - 1);
deltaTime = m_fpsHistory.back() - m_fpsHistory.front();
averageFPS = AZStd::chrono::seconds(m_fpsHistory.size()) / deltaTime;
averageFrameMs = 1000.0f/averageFPS;
}
const double frameIntervalSeconds = m_fpsInterval.count();
@@ -303,7 +288,7 @@ namespace AZ::Render
AZStd::string::format(
"FPS %.1f [%.0f..%.0f], %.1fms/frame, avg over %.1fs",
averageFPS,
minFPS == DBL_MAX ? 0.0 : minFPS,
minFPS,
maxFPS,
averageFrameMs,
frameIntervalSeconds),
@@ -45,7 +45,6 @@ namespace AZ
// AZ::RPI::ViewportContextNotificationBus::Handler overrides...
void OnRenderTick() override;
void OnFrameEnd() override;
// AZ::AtomBridge::AtomViewportInfoDisplayRequestBus::Handler overrides...
AtomBridge::ViewportInfoDisplayState GetDisplayState() const override;
@@ -62,8 +61,6 @@ namespace AZ
void DrawPassInfo();
void DrawFramerate();
void UpdateScene(AZ::RPI::ScenePtr scene);
static constexpr float BaseFontSize = 0.7f;
AZStd::string m_rendererDescription;
@@ -71,17 +68,7 @@ namespace AZ
AzFramework::FontDrawInterface* m_fontDrawInterface = nullptr;
float m_lineSpacing;
AZStd::chrono::duration<double> m_fpsInterval = AZStd::chrono::seconds(1);
struct FrameTimingInfo
{
AZStd::chrono::system_clock::time_point m_beginFrameTime;
AZStd::optional<AZStd::chrono::system_clock::time_point> m_endFrameTime;
explicit FrameTimingInfo(AZStd::chrono::system_clock::time_point beginFrameTime)
: m_beginFrameTime(beginFrameTime)
{
}
};
AZStd::deque<FrameTimingInfo> m_fpsHistory;
AZStd::deque<AZStd::chrono::system_clock::time_point> m_fpsHistory;
AZStd::optional<AZStd::chrono::system_clock::time_point> m_lastMemoryUpdate;
bool m_updateRootPassQuery = true;
};
@@ -151,7 +151,9 @@ namespace AZ
//! Returns the list of all ModelMaterialSlot's for the model, across all LODs.
virtual RPI::ModelMaterialSlotMap GetModelMaterialSlots() const = 0;
//! Returns the available, overridable material slots and the default assigned materials
virtual MaterialAssignmentMap GetMaterialAssignments() const = 0;
virtual AZStd::unordered_set<AZ::Name> GetModelUvNames() const = 0;
};
using MaterialReceiverRequestBus = EBus<MaterialReceiverRequests>;
@@ -161,6 +163,7 @@ namespace AZ
: public ComponentBus
{
public:
//! Notification that overridable material slots are available or have changed
virtual void OnMaterialAssignmentsChanged() = 0;
};
using MaterialReceiverNotificationBus = EBus<MaterialReceiverNotifications>;
@@ -88,6 +88,12 @@ namespace AZ
AZ::Data::AssetId materialAssetId = {};
MaterialComponentRequestBus::EventResult(
materialAssetId, m_entityId, &MaterialComponentRequestBus::Events::GetMaterialOverride, m_materialAssignmentId);
if (!materialAssetId.IsValid())
{
MaterialComponentRequestBus::EventResult(
materialAssetId, m_entityId, &MaterialComponentRequestBus::Events::GetDefaultMaterialAssetId,
m_materialAssignmentId);
}
if (!materialAssetId.IsValid())
{
@@ -728,11 +734,16 @@ namespace AZ
void MaterialPropertyInspector::UpdateUI()
{
AZ::Data::AssetId assetId;
AZ::Data::AssetId materialAssetId = {};
MaterialComponentRequestBus::EventResult(
assetId, m_entityId, &MaterialComponentRequestBus::Events::GetMaterialOverride, m_materialAssignmentId);
materialAssetId, m_entityId, &MaterialComponentRequestBus::Events::GetMaterialOverride, m_materialAssignmentId);
if (!materialAssetId.IsValid())
{
MaterialComponentRequestBus::EventResult(
materialAssetId, m_entityId, &MaterialComponentRequestBus::Events::GetDefaultMaterialAssetId, m_materialAssignmentId);
}
if (IsLoaded() && m_editData.m_materialAssetId == assetId)
if (IsLoaded() && m_editData.m_materialAssetId == materialAssetId)
{
LoadOverridesFromEntity();
}
@@ -118,10 +118,16 @@ namespace AZ
}
};
AZ::Data::AssetId EditorMaterialComponentSlot::GetActiveAssetId() const
{
return m_materialAsset.GetId().IsValid() ? m_materialAsset.GetId() : GetDefaultAssetId();
}
AZ::Data::AssetId EditorMaterialComponentSlot::GetDefaultAssetId() const
{
AZ::Data::AssetId assetId;
MaterialComponentRequestBus::EventResult(assetId, m_entityId, &MaterialComponentRequestBus::Events::GetDefaultMaterialAssetId, m_id);
MaterialComponentRequestBus::EventResult(
assetId, m_entityId, &MaterialComponentRequestBus::Events::GetDefaultMaterialAssetId, m_id);
return assetId;
}
@@ -135,7 +141,7 @@ namespace AZ
bool EditorMaterialComponentSlot::HasSourceData() const
{
// The slot only has valid source data if the source path is valid and the file has the correct extension
const AZStd::string& sourcePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(m_materialAsset.GetId());
const AZStd::string& sourcePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(GetActiveAssetId());
return !sourcePath.empty() && AZ::StringFunc::Path::IsExtension(sourcePath.c_str(), AZ::RPI::MaterialSourceData::Extension);
}
@@ -220,7 +226,7 @@ namespace AZ
void EditorMaterialComponentSlot::OpenMaterialEditor() const
{
const AZStd::string& sourcePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(m_materialAsset.GetId());
const AZStd::string& sourcePath = AZ::RPI::AssetUtils::GetSourcePathByAssetId(GetActiveAssetId());
if (!sourcePath.empty() && AZ::StringFunc::Path::IsExtension(sourcePath.c_str(), AZ::RPI::MaterialSourceData::Extension))
{
EditorMaterialSystemComponentRequestBus::Broadcast(
@@ -236,7 +242,7 @@ namespace AZ
void EditorMaterialComponentSlot::OpenUvNameMapInspector()
{
if (m_materialAsset.GetId().IsValid())
if (GetActiveAssetId().IsValid())
{
AZStd::unordered_set<AZ::Name> modelUvNames;
MaterialReceiverRequestBus::EventResult(modelUvNames, m_entityId, &MaterialReceiverRequestBus::Events::GetModelUvNames);
@@ -252,7 +258,7 @@ namespace AZ
};
if (EditorMaterialComponentInspector::OpenInspectorDialog(
m_materialAsset.GetId(), matModUvOverrides, modelUvNames, applyMatModUvOverrideChangedCallback))
GetActiveAssetId(), matModUvOverrides, modelUvNames, applyMatModUvOverrideChangedCallback))
{
OnDataChanged();
}
@@ -274,10 +280,10 @@ namespace AZ
action->setEnabled(HasSourceData());
action = menu.addAction("Edit Material Instance...", [this]() { OpenMaterialInspector(); });
action->setEnabled(m_materialAsset.GetId().IsValid());
action->setEnabled(GetActiveAssetId().IsValid());
action = menu.addAction("Edit Material Instance UV Map...", [this]() { OpenUvNameMapInspector(); });
action->setEnabled(m_materialAsset.GetId().IsValid());
action->setEnabled(GetActiveAssetId().IsValid());
menu.addSeparator();
@@ -30,6 +30,7 @@ namespace AZ
static void Reflect(ReflectContext* context);
static bool ConvertVersion(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement);
AZ::Data::AssetId GetActiveAssetId() const;
AZ::Data::AssetId GetDefaultAssetId() const;
AZStd::string GetLabel() const;
bool HasSourceData() const;
@@ -111,12 +111,14 @@ namespace AZ
m_queuedMaterialUpdateNotification = false;
MaterialComponentRequestBus::Handler::BusConnect(m_entityId);
MaterialReceiverNotificationBus::Handler::BusConnect(m_entityId);
LoadMaterials();
}
void MaterialComponentController::Deactivate()
{
MaterialComponentRequestBus::Handler::BusDisconnect();
MaterialReceiverNotificationBus::Handler::BusDisconnect();
TickBus::Handler::BusDisconnect();
ReleaseMaterials();
@@ -146,56 +148,26 @@ namespace AZ
void MaterialComponentController::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
AZStd::unordered_set<MaterialAssignmentId> propertyOverrides;
AZStd::swap(m_queuedPropertyOverrides, propertyOverrides);
AZStd::unordered_set<MaterialAssignmentId> materialsWithDirtyProperties;
AZStd::swap(m_materialsWithDirtyProperties, materialsWithDirtyProperties);
// Iterate through all MaterialAssignmentId's that have property overrides and attempt to apply them
// if material instance is already compiling, delay application of property overrides until next frame
for (const auto& materialAssignmentId : propertyOverrides)
for (const auto& materialAssignmentId : materialsWithDirtyProperties)
{
const auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
if (materialIt == m_configuration.m_materials.end())
if (materialIt != m_configuration.m_materials.end())
{
//Skip materials that do not exist in the map
continue;
}
auto materialInstance = materialIt->second.m_materialInstance;
if (!materialInstance)
{
//Skip materials with an invalid instances
continue;
}
if (!materialInstance->CanCompile())
{
//If a material cannot currently be compiled then it must be queued again
m_queuedPropertyOverrides.emplace(materialAssignmentId);
continue;
}
const auto& propertyOverrides2 = materialIt->second.m_propertyOverrides;
for (auto& propertyPair : propertyOverrides2)
{
if (propertyPair.second.empty())
if (!materialIt->second.ApplyProperties())
{
continue;
// If a material cannot currently be compiled then it must be queued again
m_materialsWithDirtyProperties.emplace(materialAssignmentId);
}
const auto& materialPropertyIndex = materialInstance->FindPropertyIndex(propertyPair.first);
if (materialPropertyIndex.IsNull())
{
continue;
}
materialInstance->SetPropertyValue(materialPropertyIndex, AZ::RPI::MaterialPropertyValue::FromAny(propertyPair.second));
}
materialInstance->Compile();
}
// Only disconnect from tick bus and send notification after all pending properties have been applied
if (m_queuedPropertyOverrides.empty())
if (m_materialsWithDirtyProperties.empty())
{
if (m_queuedMaterialUpdateNotification)
{
@@ -213,15 +185,35 @@ namespace AZ
Data::AssetBus::MultiHandler::BusDisconnect();
bool anyQueued = false;
for (auto& materialPair : m_configuration.m_materials)
auto queueAsset = [&anyQueued, this](AZ::Data::Asset<AZ::RPI::MaterialAsset>& materialAsset) -> bool
{
auto& materialAsset = materialPair.second.m_materialAsset;
if (materialAsset.GetId().IsValid() && !Data::AssetBus::MultiHandler::BusIsConnectedId(materialAsset.GetId()))
if (materialAsset.GetId().IsValid() && !this->Data::AssetBus::MultiHandler::BusIsConnectedId(materialAsset.GetId()))
{
anyQueued = true;
materialAsset.QueueLoad();
Data::AssetBus::MultiHandler::BusConnect(materialAsset.GetId());
this->Data::AssetBus::MultiHandler::BusConnect(materialAsset.GetId());
return true;
}
return false;
};
for (auto& materialPair : m_configuration.m_materials)
{
if (materialPair.second.m_materialInstancePreCreated)
{
continue;
}
materialPair.second.m_defaultMaterialAsset = {};
if (!queueAsset(materialPair.second.m_materialAsset))
{
// Only assign and load the default material if there was no material override and there are propoerties to apply
if (!materialPair.second.m_propertyOverrides.empty() || !materialPair.second.m_matModUvOverrides.empty())
{
materialPair.second.m_defaultMaterialAsset = AZ::Data::Asset<AZ::RPI::MaterialAsset>(
GetDefaultMaterialAssetId(materialPair.first), AZ::AzTypeInfo<AZ::RPI::MaterialAsset>::Uuid());
queueAsset(materialPair.second.m_defaultMaterialAsset);
}
}
}
@@ -234,10 +226,8 @@ namespace AZ
void MaterialComponentController::InitializeMaterialInstance(const Data::Asset<Data::AssetData>& asset)
{
bool allReady = true;
for (auto& materialPair : m_configuration.m_materials)
auto updateAsset = [&](AZ::Data::Asset<AZ::RPI::MaterialAsset>& materialAsset)
{
auto& materialAsset = materialPair.second.m_materialAsset;
if (materialAsset.GetId() == asset.GetId())
{
materialAsset = asset;
@@ -247,6 +237,12 @@ namespace AZ
{
allReady = false;
}
};
for (auto& materialPair : m_configuration.m_materials)
{
updateAsset(materialPair.second.m_materialAsset);
updateAsset(materialPair.second.m_defaultMaterialAsset);
}
if (allReady)
@@ -269,11 +265,7 @@ namespace AZ
for (auto& materialPair : m_configuration.m_materials)
{
if (materialPair.second.m_materialAsset.GetId().IsValid())
{
materialPair.second.m_materialAsset.Release();
materialPair.second.m_materialInstance = nullptr;
}
materialPair.second.Release();
}
MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsUpdated, m_configuration.m_materials);
@@ -441,12 +433,7 @@ namespace AZ
AZ::Data::AssetId MaterialComponentController::GetMaterialOverride(const MaterialAssignmentId& materialAssignmentId) const
{
auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
if (materialIt == m_configuration.m_materials.end())
{
return {};
}
return materialIt->second.m_materialAsset.GetId();
return materialIt != m_configuration.m_materials.end() ? materialIt->second.m_materialAsset.GetId() : AZ::Data::AssetId();
}
void MaterialComponentController::ClearMaterialOverride(const MaterialAssignmentId& materialAssignmentId)
@@ -460,18 +447,21 @@ namespace AZ
void MaterialComponentController::SetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZStd::any& value)
{
auto& materialAssignment = m_configuration.m_materials[materialAssignmentId];
const bool wasEmpty = materialAssignment.m_propertyOverrides.empty();
materialAssignment.m_propertyOverrides[AZ::Name(propertyName)] = value;
// When applying property overrides for the first time, new instance needs to be created in case the current instance is already used somewhere else to keep overrides local
if (materialAssignment.m_propertyOverrides.empty())
if (materialAssignment.RequiresLoading())
{
materialAssignment.m_propertyOverrides[AZ::Name(propertyName)] = value;
materialAssignment.RebuildInstance();
MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialInstanceCreated, materialAssignment);
QueueMaterialUpdateNotification();
LoadMaterials();
return;
}
else
if (wasEmpty != materialAssignment.m_propertyOverrides.empty())
{
materialAssignment.m_propertyOverrides[AZ::Name(propertyName)] = value;
materialAssignment.RebuildInstance();
MaterialComponentNotificationBus::Event(
m_entityId, &MaterialComponentNotifications::OnMaterialInstanceCreated, materialAssignment);
QueueMaterialUpdateNotification();
}
QueuePropertyChanges(materialAssignmentId);
@@ -704,6 +694,12 @@ namespace AZ
const bool wasEmpty = materialAssignment.m_propertyOverrides.empty();
materialAssignment.m_propertyOverrides = propertyOverrides;
if (materialAssignment.RequiresLoading())
{
LoadMaterials();
return;
}
if (wasEmpty != materialAssignment.m_propertyOverrides.empty())
{
materialAssignment.RebuildInstance();
@@ -713,14 +709,11 @@ namespace AZ
QueuePropertyChanges(materialAssignmentId);
}
MaterialPropertyOverrideMap MaterialComponentController::GetPropertyOverrides(const MaterialAssignmentId& materialAssignmentId) const
MaterialPropertyOverrideMap MaterialComponentController::GetPropertyOverrides(
const MaterialAssignmentId& materialAssignmentId) const
{
const auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
if (materialIt == m_configuration.m_materials.end())
{
return {};
}
return materialIt->second.m_propertyOverrides;
return materialIt != m_configuration.m_materials.end() ? materialIt->second.m_propertyOverrides : MaterialPropertyOverrideMap();
}
void MaterialComponentController::SetModelUvOverrides(
@@ -730,6 +723,12 @@ namespace AZ
const bool wasEmpty = materialAssignment.m_matModUvOverrides.empty();
materialAssignment.m_matModUvOverrides = modelUvOverrides;
if (materialAssignment.RequiresLoading())
{
LoadMaterials();
return;
}
if (wasEmpty != materialAssignment.m_matModUvOverrides.empty())
{
materialAssignment.RebuildInstance();
@@ -743,16 +742,24 @@ namespace AZ
const MaterialAssignmentId& materialAssignmentId) const
{
const auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
if (materialIt == m_configuration.m_materials.end())
return materialIt != m_configuration.m_materials.end() ? materialIt->second.m_matModUvOverrides : AZ::RPI::MaterialModelUvOverrideMap();
}
void MaterialComponentController::OnMaterialAssignmentsChanged()
{
for (const auto& materialPair : m_configuration.m_materials)
{
return {};
if (materialPair.second.RequiresLoading())
{
LoadMaterials();
return;
}
}
return materialIt->second.m_matModUvOverrides;
}
void MaterialComponentController::QueuePropertyChanges(const MaterialAssignmentId& materialAssignmentId)
{
m_queuedPropertyOverrides.emplace(materialAssignmentId);
m_materialsWithDirtyProperties.emplace(materialAssignmentId);
if (!TickBus::Handler::BusIsConnected())
{
TickBus::Handler::BusConnect();
@@ -22,6 +22,7 @@ namespace AZ
//! to provide material overrides on a per-entity basis.
class MaterialComponentController final
: MaterialComponentRequestBus::Handler
, MaterialReceiverNotificationBus::Handler
, Data::AssetBus::MultiHandler
, TickBus::Handler
{
@@ -100,6 +101,9 @@ namespace AZ
const MaterialAssignmentId& materialAssignmentId, const AZ::RPI::MaterialModelUvOverrideMap& modelUvOverrides) override;
AZ::RPI::MaterialModelUvOverrideMap GetModelUvOverrides(const MaterialAssignmentId& materialAssignmentId) const override;
//! MaterialReceiverNotificationBus::Handler overrides...
void OnMaterialAssignmentsChanged() override;
private:
AZ_DISABLE_COPY(MaterialComponentController);
@@ -121,7 +125,7 @@ namespace AZ
EntityId m_entityId;
MaterialComponentConfig m_configuration;
AZStd::unordered_set<MaterialAssignmentId> m_queuedPropertyOverrides;
AZStd::unordered_set<MaterialAssignmentId> m_materialsWithDirtyProperties;
bool m_queuedMaterialUpdateNotification = false;
};
} // namespace Render
@@ -8,21 +8,20 @@
#pragma once
#include "ColliderSubComponentMode.h"
#include <Editor/Source/ComponentModes/PhysXSubComponentModeBase.h>
#include <AzToolsFramework/Manipulators/ScaleManipulators.h>
namespace PhysX
{
/// Sub component mode for modifying the asset scale on a collider in the viewport.
class ColliderAssetScaleMode
: public PhysX::ColliderSubComponentMode
class ColliderAssetScaleMode : public PhysXSubComponentModeBase
{
public:
AZ_CLASS_ALLOCATOR_DECL
ColliderAssetScaleMode();
// ColliderSubComponentMode ...
// PhysXSubComponentModeBase ...
void Setup(const AZ::EntityComponentIdPair& idPair) override;
void Refresh(const AZ::EntityComponentIdPair& idPair) override;
void Teardown(const AZ::EntityComponentIdPair& idPair) override;
+3 -4
View File
@@ -8,19 +8,18 @@
#pragma once
#include "ColliderSubComponentMode.h"
#include <Editor/Source/ComponentModes/PhysXSubComponentModeBase.h>
#include <AzToolsFramework/ComponentModes/BoxViewportEdit.h>
namespace PhysX
{
/// Sub component mode for modifying the box dimensions on a collider.
class ColliderBoxMode
: public PhysX::ColliderSubComponentMode
class ColliderBoxMode : public PhysXSubComponentModeBase
{
public:
AZ_CLASS_ALLOCATOR_DECL
// ColliderSubComponentMode ...
// PhysXSubComponentModeBase ...
void Setup(const AZ::EntityComponentIdPair& idPair) override;
void Refresh(const AZ::EntityComponentIdPair& idPair) override;
void Teardown(const AZ::EntityComponentIdPair& idPair) override;
+3 -3
View File
@@ -8,7 +8,7 @@
#pragma once
#include "ColliderSubComponentMode.h"
#include <Editor/Source/ComponentModes/PhysXSubComponentModeBase.h>
#include <AzToolsFramework/ComponentModes/BoxViewportEdit.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
@@ -16,13 +16,13 @@ namespace PhysX
{
/// Sub component mode for modifying the height and radius on a capsule collider.
class ColliderCapsuleMode
: public PhysX::ColliderSubComponentMode
: public PhysXSubComponentModeBase
, private AzFramework::EntityDebugDisplayEventBus::Handler
{
public:
AZ_CLASS_ALLOCATOR_DECL
// ColliderSubComponentMode ...
// PhysXSubComponentModeBase ...
void Setup(const AZ::EntityComponentIdPair& idPair) override;
void Refresh(const AZ::EntityComponentIdPair& idPair) override;
void Teardown(const AZ::EntityComponentIdPair& idPair) override;
@@ -7,7 +7,6 @@
*/
#include "ColliderComponentMode.h"
#include "ColliderSubComponentMode.h"
#include "ColliderOffsetMode.h"
#include "ColliderRotationMode.h"
#include "ColliderBoxMode.h"
@@ -15,6 +14,7 @@
#include "ColliderCapsuleMode.h"
#include "ColliderAssetScaleMode.h"
#include <Editor/Source/ComponentModes/PhysXSubComponentModeBase.h>
#include <PhysX/EditorColliderComponentRequestBus.h>
#include <AzFramework/Physics/ShapeConfiguration.h>
@@ -15,7 +15,7 @@
namespace PhysX
{
class ColliderSubComponentMode;
class PhysXSubComponentModeBase;
//! ComponentMode for the Collider Component - Manages a list of Sub-Component Modes and
//! is responsible for switching between and activating them.
@@ -53,7 +53,7 @@ namespace PhysX
void CreateSubModes();
void ResetCurrentMode();
AZStd::unordered_map<SubMode, AZStd::unique_ptr<ColliderSubComponentMode>> m_subModes;
AZStd::unordered_map<SubMode, AZStd::unique_ptr<PhysXSubComponentModeBase>> m_subModes;
SubMode m_subMode = SubMode::Dimensions;
//! Create the Viewport UI cluster for sub mode selection.
+3 -4
View File
@@ -8,21 +8,20 @@
#pragma once
#include "ColliderSubComponentMode.h"
#include <Editor/Source/ComponentModes/PhysXSubComponentModeBase.h>
#include <AzToolsFramework/Manipulators/TranslationManipulators.h>
namespace PhysX
{
/// Sub component mode for modifying offset on a collider in the viewport.
class ColliderOffsetMode
: public PhysX::ColliderSubComponentMode
class ColliderOffsetMode : public PhysXSubComponentModeBase
{
public:
AZ_CLASS_ALLOCATOR_DECL
ColliderOffsetMode();
// ColliderSubComponentMode ...
// PhysXSubComponentModeBase ...
void Setup(const AZ::EntityComponentIdPair& idPair) override;
void Refresh(const AZ::EntityComponentIdPair& idPair) override;
void Teardown(const AZ::EntityComponentIdPair& idPair) override;
@@ -8,7 +8,7 @@
#pragma once
#include "ColliderSubComponentMode.h"
#include <Editor/Source/ComponentModes/PhysXSubComponentModeBase.h>
#include <AzToolsFramework/Manipulators/RotationManipulators.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
@@ -16,7 +16,7 @@ namespace PhysX
{
/// Sub component mode for modifying the rotation on a collider in the viewport.
class ColliderRotationMode
: public PhysX::ColliderSubComponentMode
: public PhysXSubComponentModeBase
, private AzFramework::EntityDebugDisplayEventBus::Handler
{
public:
@@ -24,7 +24,7 @@ namespace PhysX
ColliderRotationMode();
// ColliderSubComponentMode ...
// PhysXSubComponentModeBase ...
void Setup(const AZ::EntityComponentIdPair& idPair) override;
void Refresh(const AZ::EntityComponentIdPair& idPair) override;
void Teardown(const AZ::EntityComponentIdPair& idPair) override;
+3 -3
View File
@@ -8,7 +8,7 @@
#pragma once
#include "ColliderSubComponentMode.h"
#include <Editor/Source/ComponentModes/PhysXSubComponentModeBase.h>
#include <AzToolsFramework/ComponentModes/BoxViewportEdit.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
@@ -16,13 +16,13 @@ namespace PhysX
{
/// Sub component mode for modifying the box dimensions on a collider.
class ColliderSphereMode
: public PhysX::ColliderSubComponentMode
: public PhysXSubComponentModeBase
, private AzFramework::EntityDebugDisplayEventBus::Handler
{
public:
AZ_CLASS_ALLOCATOR_DECL
// ColliderSubComponentMode ...
// PhysXSubComponentModeBase ...
void Setup(const AZ::EntityComponentIdPair& idPair) override;
void Refresh(const AZ::EntityComponentIdPair& idPair) override;
void Teardown(const AZ::EntityComponentIdPair& idPair) override;
@@ -1,42 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
namespace AZ
{
class EntityComponentIdPair;
}
namespace PhysX
{
/// Interface to implement when adding a new editing mode
/// for collider component mode.
class ColliderSubComponentMode
{
public:
virtual ~ColliderSubComponentMode() {};
/// Called when the mode is entered to initialise the mode.
/// @param idPair The entity/component id pair.
virtual void Setup(const AZ::EntityComponentIdPair& idPair) = 0;
/// Called when the mode needs to refresh it's values.
/// @param idPair The entity/component id pair.
virtual void Refresh(const AZ::EntityComponentIdPair& idPair) = 0;
/// Called when the mode exits to perform cleanup.
/// @param idPair The entity/component id pair.
virtual void Teardown(const AZ::EntityComponentIdPair& idPair) = 0;
/// Called when reset hotkey is pressed.
/// Should reset values in the sub component mode to sensible defaults.
/// @param idPair The entity/component id pair.
virtual void ResetValues(const AZ::EntityComponentIdPair& idPair) = 0;
};
}
@@ -0,0 +1,17 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/std/utils.h>
namespace PhysX
{
//! Pair of floating point values for angular limits.
using AngleLimitsFloatPair = AZStd::pair<float, float>;
} // namespace PhysX
@@ -1,577 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Editor/EditorJointConfiguration.h>
#include <Editor/EditorJointComponentMode.h>
#include <Editor/EditorSubComponentModeAngleCone.h>
#include <Editor/EditorSubComponentModeAnglePair.h>
#include <Editor/EditorSubComponentModeLinear.h>
#include <Editor/EditorSubComponentModeRotation.h>
#include <Editor/EditorSubComponentModeSnapPosition.h>
#include <Editor/EditorSubComponentModeSnapRotation.h>
#include <Editor/EditorSubComponentModeVec3.h>
#include <PhysX/EditorJointBus.h>
namespace PhysX
{
namespace
{
//! Uri's for shortcut actions.
const AZ::Crc32 GoToNextModeActionUri = AZ_CRC("com.o3de.action.physx.joint.nextmode", 0xe9cf4ed6);
const AZ::Crc32 GoToPrevModeActionUri = AZ_CRC("com.o3de.action.physx.joint.prevmode", 0xe70f8daa);
}
const AZStd::string EditorJointComponentMode::s_parameterAngularPair = "Twist Limits";
const AZStd::string EditorJointComponentMode::s_parameterDamping = "Damping";
const AZStd::string EditorJointComponentMode::s_parameterMaxForce = "Maximum Force";
const AZStd::string EditorJointComponentMode::s_parameterMaxTorque = "Maximum Torque";
const AZStd::string EditorJointComponentMode::s_parameterPosition = "Position";
const AZStd::string EditorJointComponentMode::s_parameterRotation = "Rotation";
const AZStd::string EditorJointComponentMode::s_parameterSnapPosition = "Snap Position";
const AZStd::string EditorJointComponentMode::s_parameterSnapRotation = "Snap Rotation";
const AZStd::string EditorJointComponentMode::s_parameterStiffness = "Stiffness";
const AZStd::string EditorJointComponentMode::s_parameterSwingLimit = "Swing Limits";
const AZStd::string EditorJointComponentMode::s_parameterTolerance = "Tolerance";
const AZStd::string EditorJointComponentMode::s_parameterTransform = "Transform";
const AZStd::string EditorJointComponentMode::s_parameterComponentMode = "Component Mode";
const AZStd::string EditorJointComponentMode::s_parameterLeadEntity = "Lead Entity";
const AZStd::string EditorJointComponentMode::s_parameterSelectOnSnap = "Select on Snap";
EditorSubComponentModeConfig::EditorSubComponentModeConfig(const AZStd::string& name
, EditorSubComponentModeType type)
: m_name(name), m_type(type)
{
}
EditorSubComponentModeConfig::EditorSubComponentModeConfig(const AZStd::string& name
, EditorSubComponentModeType type
, float exponent
, float max
, float min)
: m_name(name), m_type(type), m_exponent(exponent), m_max(max), m_min(min)
{
}
EditorSubComponentModeConfig::EditorSubComponentModeConfig(const AZStd::string& name
, EditorSubComponentModeType type
, const AZ::Vector3& axis
, float max
, float min)
: m_name(name), m_type(type), m_axis(axis), m_max(max), m_min(min)
{
}
EditorSubComponentModeConfig::EditorSubComponentModeConfig(const AZStd::string& name
, EditorSubComponentModeType type
, float max
, float min)
: m_name(name), m_type(type), m_max(max), m_min(min)
{
}
EditorJointComponentMode::EditorJointComponentMode(
const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid& componentType)
: EditorBaseComponentMode(entityComponentIdPair, componentType)
, m_entityComponentIdPair(entityComponentIdPair)
, m_componentType(componentType)
{
EditorJointRequestBus::Event(
m_entityComponentIdPair
, &EditorJointRequests::SetBoolValue
, EditorJointComponentMode::s_parameterComponentMode
, true);
}
EditorJointComponentMode::~EditorJointComponentMode()
{
EditorJointRequestBus::Event(
m_entityComponentIdPair
, &EditorJointRequests::SetBoolValue
, EditorJointComponentMode::s_parameterComponentMode
, false);
}
bool EditorJointComponentMode::HandleMouseInteraction(
const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteraction)
{
if (mouseInteraction.m_mouseEvent == AzToolsFramework::ViewportInteraction::MouseEvent::Wheel &&
mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Ctrl())
{
NextMode();
return true;
}
// Propagate mouse interaction to sub-component mode.
if (m_currentSubComponentMode)
{
m_currentSubComponentMode->HandleMouseInteraction(mouseInteraction);
}
return false;
}
AZStd::vector<AzToolsFramework::ActionOverride> EditorJointComponentMode::PopulateActionsImpl()
{
AzToolsFramework::ActionOverride goToNextMode;
goToNextMode.SetUri(GoToNextModeActionUri);
goToNextMode.SetKeySequence(QKeySequence(Qt::Key_Tab));
goToNextMode.SetTitle("Next Mode");
goToNextMode.SetTip("Go to next mode");
goToNextMode.SetEntityComponentIdPair(GetEntityComponentIdPair());
goToNextMode.SetCallback([this]()
{
NextMode();
});
AzToolsFramework::ActionOverride goToPrevMode;
goToPrevMode.SetUri(GoToPrevModeActionUri);
goToPrevMode.SetKeySequence(QKeySequence(Qt::SHIFT + Qt::Key_Tab));
goToPrevMode.SetTitle("Previous Mode");
goToPrevMode.SetTip("Go to previous mode");
goToPrevMode.SetEntityComponentIdPair(GetEntityComponentIdPair());
goToPrevMode.SetCallback([this]()
{
PreviousMode();
});
return {goToNextMode, goToPrevMode};
}
void EditorJointComponentMode::NextMode()
{
const bool isForwardChange = true;
ChangeMode(isForwardChange);
}
void EditorJointComponentMode::PreviousMode()
{
const bool isForwardChange = false;
ChangeMode(isForwardChange);
}
void EditorJointComponentMode::ChangeMode(bool forwardChange)
{
if (m_configMap.empty())
{
return;
}
AZStd::string previousModeName;
if (m_currentSubComponentMode)
{
previousModeName = m_currentSubComponentMode->m_name;
m_currentSubComponentMode.reset();
}
ConfigMapIter configIter = m_configMap.begin();
if (!previousModeName.empty())
{
configIter = m_configMap.find(previousModeName);
}
AZ::u32 iterCount = 0;
do
{
if (forwardChange)
{
++configIter;
if (configIter == m_configMap.end())
{
configIter = m_configMap.begin();
}
}
else
{
if (configIter == m_configMap.begin())
{
configIter = m_configMap.end();
}
--configIter;
}
++iterCount;
} while (!IsSubComponentModeUsed(configIter->second.m_name) && iterCount != m_configMap.size());
if (iterCount == m_configMap.size()) // All sub component modes are not in use.
{
return;
}
SetCurrentSubComponentMode(configIter->second.m_name);
}
void EditorJointComponentMode::SetCurrentSubComponentMode(const AZStd::string& subComponentModeName)
{
ConfigMapIter configIter = m_configMap.find(subComponentModeName);
if (configIter == m_configMap.end())
{
AZ_Warning("EditorJointComponentMode"
, false
, "Attempt to set sub component mode which does not exist: %s"
, subComponentModeName.c_str());
return;
}
EditorSubComponentModeConfig config = configIter->second;
EditorJointRequestBus::EventResult(
config.m_selectLeadOnSnap, m_entityComponentIdPair
, &EditorJointRequests::GetBoolValue
, s_parameterSelectOnSnap);
m_currentSubComponentMode.reset();
switch (config.m_type)
{
case EditorSubComponentModeType::Linear:
SetSubComponentModeLinear(config);
break;
case EditorSubComponentModeType::AnglePair:
SetSubComponentModeAnglePair(config);
break;
case EditorSubComponentModeType::AngleCone:
SetSubComponentModeAngleCone(config);
break;
case EditorSubComponentModeType::Vec3:
SetSubComponentModeVec3(config);
break;
case EditorSubComponentModeType::Rotation:
SetSubComponentModeRotation(config);
break;
case EditorSubComponentModeType::SnapPosition:
SetSubComponentModeSnapPosition(config);
break;
case EditorSubComponentModeType::SnapRotation:
SetSubComponentModeSnapRotation(config);
break;
default:
AZ_Error("EditorJointComponentMode::SetCurrentSubComponentMode"
, false
, "Unsupported sub-component mode type.");
}
}
bool EditorJointComponentMode::IsSubComponentModeUsed(const AZStd::string& subComponentModeName)
{
bool isUsed = false;
EditorJointRequestBus::EventResult(
isUsed, m_entityComponentIdPair
, &EditorJointRequests::IsParameterUsed
, subComponentModeName);
return isUsed;
}
void EditorJointComponentMode::SetSubComponentModeAngleCone(const EditorSubComponentModeConfig& config)
{
m_currentSubComponentMode = AZStd::make_shared<EditorSubComponentModeAngleCone>(m_entityComponentIdPair
, m_componentType
, config.m_name
, config.m_max
, config.m_min);
}
void EditorJointComponentMode::SetSubComponentModeAnglePair(const EditorSubComponentModeConfig& config)
{
m_currentSubComponentMode = AZStd::make_shared<EditorSubComponentModeAnglePair>(m_entityComponentIdPair
, m_componentType
, config.m_name
, config.m_axis
, config.m_max
, config.m_min
, config.m_min
, -config.m_max);
}
void EditorJointComponentMode::SetSubComponentModeLinear(const EditorSubComponentModeConfig& config)
{
m_currentSubComponentMode = AZStd::make_shared<EditorSubComponentModeLinear>(m_entityComponentIdPair
, m_componentType
, config.m_name
, config.m_exponent
, config.m_max
, config.m_min);
}
void EditorJointComponentMode::SetSubComponentModeVec3(const EditorSubComponentModeConfig& config)
{
m_currentSubComponentMode = AZStd::make_shared<EditorSubComponentModeVec3>(m_entityComponentIdPair
, m_componentType
, config.m_name);
}
void EditorJointComponentMode::SetSubComponentModeRotation(const EditorSubComponentModeConfig& config)
{
m_currentSubComponentMode = AZStd::make_shared<EditorSubComponentModeRotation>(m_entityComponentIdPair
, m_componentType
, config.m_name);
}
void EditorJointComponentMode::SetSubComponentModeSnapPosition(const EditorSubComponentModeConfig& config)
{
m_currentSubComponentMode = AZStd::make_shared<EditorSubComponentModeSnapPosition>(m_entityComponentIdPair
, m_componentType
, config.m_name
, config.m_selectLeadOnSnap);
}
void EditorJointComponentMode::SetSubComponentModeSnapRotation(const EditorSubComponentModeConfig& config)
{
m_currentSubComponentMode = AZStd::make_shared<EditorSubComponentModeSnapRotation>(m_entityComponentIdPair
, m_componentType
, config.m_name);
}
EditorBallJointComponentMode::EditorBallJointComponentMode(
const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid& componentType)
: EditorJointComponentMode(entityComponentIdPair, componentType)
{
m_configMap = Configure();
NextMode();
}
EditorBallJointComponentMode::~EditorBallJointComponentMode()
{
if (m_currentSubComponentMode)
{
m_currentSubComponentMode.reset();
}
}
void EditorJointComponentMode::Refresh()
{
if (m_currentSubComponentMode)
{
m_currentSubComponentMode->Refresh();
}
}
AZStd::map<AZStd::string, EditorSubComponentModeConfig> EditorBallJointComponentMode::Configure()
{
AZStd::map<AZStd::string, EditorSubComponentModeConfig> configMap;
configMap.emplace(
EditorJointComponentMode::s_parameterPosition
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterPosition
, EditorSubComponentModeType::Vec3)
);
configMap.emplace(
EditorJointComponentMode::s_parameterRotation
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterRotation
, EditorSubComponentModeType::Rotation)
);
configMap.emplace(
EditorJointComponentMode::s_parameterSnapPosition
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterSnapPosition
, EditorSubComponentModeType::SnapPosition)
);
configMap.emplace(
EditorJointComponentMode::s_parameterSnapRotation
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterSnapRotation
, EditorSubComponentModeType::SnapRotation)
);
const float exponentBreakage = 1.0f;
configMap.emplace(
EditorJointComponentMode::s_parameterMaxForce
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterMaxForce
, EditorSubComponentModeType::Linear
, exponentBreakage
, PhysX::EditorJointConfig::s_breakageMax
, PhysX::EditorJointConfig::s_breakageMin)
);
configMap.emplace(
EditorJointComponentMode::s_parameterMaxTorque
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterMaxTorque
, EditorSubComponentModeType::Linear
, exponentBreakage
, PhysX::EditorJointConfig::s_breakageMax
, PhysX::EditorJointConfig::s_breakageMin)
);
const float exponentSpring = 2.0f;
configMap.emplace(
EditorJointComponentMode::s_parameterDamping
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterDamping
, EditorSubComponentModeType::Linear
, exponentSpring
, PhysX::EditorJointLimitConeConfig::s_springMax
, PhysX::EditorJointLimitConeConfig::s_springMin)
);
configMap.emplace(
EditorJointComponentMode::s_parameterStiffness
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterStiffness
, EditorSubComponentModeType::Linear
, exponentSpring
, PhysX::EditorJointLimitConeConfig::s_springMax
, PhysX::EditorJointLimitConeConfig::s_springMin)
);
// Cone tip to base is always X-axis.
//The angle cone defines the limitations for rotation about the Y and Z axes.
configMap.emplace(
EditorJointComponentMode::s_parameterSwingLimit
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterSwingLimit
, EditorSubComponentModeType::AngleCone
, PhysX::EditorJointLimitConeConfig::s_angleMax
, PhysX::EditorJointLimitConeConfig::s_angleMin)
);
return configMap;
}
EditorFixedJointComponentMode::EditorFixedJointComponentMode(
const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid& componentType)
: EditorJointComponentMode(entityComponentIdPair, componentType)
{
m_configMap = Configure();
NextMode();
}
EditorFixedJointComponentMode::~EditorFixedJointComponentMode()
{
if (m_currentSubComponentMode)
{
m_currentSubComponentMode.reset();
}
}
AZStd::map<AZStd::string, EditorSubComponentModeConfig> EditorFixedJointComponentMode::Configure()
{
AZStd::map<AZStd::string, EditorSubComponentModeConfig> configMap;
configMap.emplace(
EditorJointComponentMode::s_parameterPosition
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterPosition
, EditorSubComponentModeType::Vec3)
);
configMap.emplace(
EditorJointComponentMode::s_parameterRotation
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterRotation
, EditorSubComponentModeType::Rotation)
);
const float exponentBreakage = 1.0f;
configMap.emplace(
EditorJointComponentMode::s_parameterMaxForce
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterMaxForce
, EditorSubComponentModeType::Linear
, exponentBreakage
, PhysX::EditorJointConfig::s_breakageMax
, PhysX::EditorJointConfig::s_breakageMin)
);
configMap.emplace(
EditorJointComponentMode::s_parameterMaxTorque
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterMaxTorque
, EditorSubComponentModeType::Linear
, exponentBreakage
, PhysX::EditorJointConfig::s_breakageMax
, PhysX::EditorJointConfig::s_breakageMin)
);
return configMap;
}
EditorHingeJointComponentMode::EditorHingeJointComponentMode(
const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid& componentType)
: EditorJointComponentMode(entityComponentIdPair, componentType)
{
m_configMap = Configure();
NextMode();
}
EditorHingeJointComponentMode::~EditorHingeJointComponentMode()
{
if (m_currentSubComponentMode)
{
m_currentSubComponentMode.reset();
}
}
AZStd::map<AZStd::string, EditorSubComponentModeConfig> EditorHingeJointComponentMode::Configure()
{
AZStd::map<AZStd::string, EditorSubComponentModeConfig> configMap;
configMap.emplace(
EditorJointComponentMode::s_parameterPosition
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterPosition
, EditorSubComponentModeType::Vec3)
);
configMap.emplace(
EditorJointComponentMode::s_parameterRotation
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterRotation
, EditorSubComponentModeType::Rotation)
);
const float exponentBreakage = 1.0f;
configMap.emplace(
EditorJointComponentMode::s_parameterMaxForce
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterMaxForce
, EditorSubComponentModeType::Linear
, exponentBreakage
, PhysX::EditorJointConfig::s_breakageMax
, PhysX::EditorJointConfig::s_breakageMin)
);
configMap.emplace(
EditorJointComponentMode::s_parameterMaxTorque
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterMaxTorque
, EditorSubComponentModeType::Linear
, exponentBreakage
, PhysX::EditorJointConfig::s_breakageMax
, PhysX::EditorJointConfig::s_breakageMin)
);
const float exponentSpring = 2.0f;
configMap.emplace(
EditorJointComponentMode::s_parameterDamping
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterDamping
, EditorSubComponentModeType::Linear
, exponentSpring
, PhysX::EditorJointLimitPairConfig::s_springMax
, PhysX::EditorJointLimitPairConfig::s_springMin)
);
configMap.emplace(
EditorJointComponentMode::s_parameterStiffness
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterStiffness
, EditorSubComponentModeType::Linear
, exponentSpring
, PhysX::EditorJointLimitPairConfig::s_springMax
, PhysX::EditorJointLimitPairConfig::s_springMin)
);
AZ::Vector3 axis = AZ::Vector3::CreateAxisX(); // PhysX revolute joints uses the x-axis by default
configMap.emplace(
EditorJointComponentMode::s_parameterAngularPair
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterAngularPair
, EditorSubComponentModeType::AnglePair
, axis
, PhysX::EditorJointLimitPairConfig::s_angleMax
, PhysX::EditorJointLimitPairConfig::s_angleMin)
);
configMap.emplace(
EditorJointComponentMode::s_parameterSnapPosition
, EditorSubComponentModeConfig(EditorJointComponentMode::s_parameterSnapPosition
, EditorSubComponentModeType::SnapPosition)
);
return configMap;
}
} // namespace LmbrCentral
@@ -1,173 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/std/containers/map.h>
#include <AzToolsFramework/ComponentMode/EditorBaseComponentMode.h>
#include <Editor/EditorSubComponentModeBase.h>
namespace PhysX
{
enum class EditorSubComponentModeType : AZ::u8
{
Linear, /// sub-component mode to modify a single linear value, e.g. a float value.
AnglePair, /// sub-component mode to modify a pair of float values representing angles.
AngleCone, /// sub-component mode to modify a constraint's swing limits and local transformation.
Rotation, /// sub-component mode to modify local transformation.
SnapPosition, /// sub-component mode to modify local position using a point-and-snap feature in the viewport.
SnapRotation, /// sub-component mode to modify local rotation using a point-and-snap feature in the viewport.
Vec3 /// sub-component mode to modify a Vector3 value.
};
/// Contains configuration of a sub-component mode. Shared by different types of sub-component mode.
/// Alternative implementation of this struct using AZStd::Variant is pending the development of the rest of the joint types.
struct EditorSubComponentModeConfig
{
EditorSubComponentModeConfig() = default;
EditorSubComponentModeConfig(const AZStd::string& name
, EditorSubComponentModeType type);
EditorSubComponentModeConfig(const AZStd::string& name
, EditorSubComponentModeType type
, float exponent
, float max
, float min);
EditorSubComponentModeConfig(const AZStd::string& name
, EditorSubComponentModeType type
, const AZ::Vector3& axis
, float max
, float min);
EditorSubComponentModeConfig(const AZStd::string& name
, EditorSubComponentModeType type
, float max
, float min);
AZStd::string m_name;
EditorSubComponentModeType m_type;
AZ::Vector3 m_axis = AZ::Vector3::CreateAxisX();
float m_exponent = 1.0f;
float m_max = FLT_MAX;
float m_min = -FLT_MAX;
bool m_selectLeadOnSnap = true; ///< A user may use the snap-to-position component mode to snap the position of a joint to an entity. This flag indicates if the snapped-to entity would be selected as a joint's lead when that happens.
};
/// Generic component mode that supports multiple sub-component modes.
class EditorJointComponentMode
: public AzToolsFramework::ComponentModeFramework::EditorBaseComponentMode
{
public:
static const AZStd::string s_parameterAngularPair;
static const AZStd::string s_parameterDamping;
static const AZStd::string s_parameterMaxForce;
static const AZStd::string s_parameterMaxTorque;
static const AZStd::string s_parameterPosition;
static const AZStd::string s_parameterRotation;
static const AZStd::string s_parameterSnapPosition;
static const AZStd::string s_parameterSnapRotation;
static const AZStd::string s_parameterStiffness;
static const AZStd::string s_parameterSwingLimit;
static const AZStd::string s_parameterTolerance;
static const AZStd::string s_parameterTransform;
static const AZStd::string s_parameterComponentMode;
static const AZStd::string s_parameterLeadEntity;
static const AZStd::string s_parameterSelectOnSnap;
EditorJointComponentMode(
const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid& componentType);
~EditorJointComponentMode();
// EditorBaseComponentMode
void Refresh() override;
/// Returns map of sub-component mode configurations required by this component mode.
virtual AZStd::map<AZStd::string, EditorSubComponentModeConfig> Configure() = 0;
protected:
/// AzToolsFramework::ViewportInteraction::MouseViewportRequests
bool HandleMouseInteraction(
const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteraction) override;
/// EditorBaseComponentMode
AZStd::vector<AzToolsFramework::ActionOverride> PopulateActionsImpl() override;
/// Changes to the next sub-component mode found in m_configMap.
void NextMode();
/// Changes to the previous sub-component mode found in m_configMap.
void PreviousMode();
/// Changes to the next or previous sub-component mode found in m_configMap.
void ChangeMode(bool forwardChange);
/// Replaces m_currentSubComponentMode with a new one instantiated using the configuration identified by the input subComponentModeName.
void SetCurrentSubComponentMode(const AZStd::string& subComponentModeName);
AZStd::shared_ptr<EditorSubComponentModeBase> m_currentSubComponentMode = nullptr; ///< The active sub-component mode in this component mode.
AZ::Uuid m_componentType = AZ::Uuid::CreateNull();
AZStd::map<AZStd::string, EditorSubComponentModeConfig> m_configMap; ///< Contains sub-component mode configurations supported by this component mode.
AZ::EntityComponentIdPair m_entityComponentIdPair;
private:
bool IsSubComponentModeUsed(const AZStd::string& subComponentModeName);
void SetSubComponentModeAngleCone(const EditorSubComponentModeConfig& config);
void SetSubComponentModeAnglePair(const EditorSubComponentModeConfig& config);
void SetSubComponentModeLinear(const EditorSubComponentModeConfig& config);
void SetSubComponentModeVec3(const EditorSubComponentModeConfig& config);
void SetSubComponentModeRotation(const EditorSubComponentModeConfig& config);
void SetSubComponentModeSnapPosition(const EditorSubComponentModeConfig& config);
void SetSubComponentModeSnapRotation(const EditorSubComponentModeConfig& config);
};
/// Ball joint specific component mode. Configure() is overriden to set up the required sub-component modes.
class EditorBallJointComponentMode
: public EditorJointComponentMode
{
public:
EditorBallJointComponentMode(
const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid& componentType);
~EditorBallJointComponentMode();
// EditorJointComponentMode
AZStd::map<AZStd::string, EditorSubComponentModeConfig> Configure() override;
};
/// Fixed joint specific component mode. Configure() is overriden to set up the required sub-component modes.
class EditorFixedJointComponentMode
: public EditorJointComponentMode
{
public:
EditorFixedJointComponentMode(
const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid& componentType);
~EditorFixedJointComponentMode();
// EditorJointComponentMode
AZStd::map<AZStd::string, EditorSubComponentModeConfig> Configure() override;
};
/// Hinge joint specific component mode. Configure() is overriden to set up the required sub-component modes.
class EditorHingeJointComponentMode
: public EditorJointComponentMode
{
public:
EditorHingeJointComponentMode(
const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid& componentType);
~EditorHingeJointComponentMode();
// EditorJointComponentMode
AZStd::map<AZStd::string, EditorSubComponentModeConfig> Configure() override;
};
using ConfigMap = AZStd::map<AZStd::string, EditorSubComponentModeConfig>;
using ConfigMapIter = ConfigMap::iterator;
using ConfigMapReverseIter = ConfigMap::reverse_iterator;
} // namespace PhysX
@@ -1,68 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
#include <Editor/EditorJointTypeDrawer.h>
namespace PhysX
{
EditorJointTypeDrawer::EditorJointTypeDrawer(EditorJointType jointType,
AzFramework::EntityContextId entityContextId,
const AZStd::string& subComponentModeName):
m_subComponentModeName(subComponentModeName)
{
AzFramework::ViewportDebugDisplayEventBus::Handler::BusConnect(entityContextId);
EditorJointTypeDrawerBus::Handler::BusConnect(EditorJointTypeDrawerId(jointType,
EditorSubComponentModeNameCrc(subComponentModeName)));
}
EditorJointTypeDrawer::~EditorJointTypeDrawer()
{
EditorJointTypeDrawerBus::Handler::BusDisconnect();
AzFramework::ViewportDebugDisplayEventBus::Handler::BusDisconnect();
}
void EditorJointTypeDrawer::DisplayViewport2d(
const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay)
{
const AZ::u32 stateBefore = debugDisplay.GetState();
const AzFramework::CameraState cameraState = AzToolsFramework::GetCameraState(viewportInfo.m_viewportId);
const float xOffsetCurrentMode = 125.0f;
const float yOffsetCurrentMode = 55.0f;
const float xOffsetHotKeys = 125.0f;
const float yOffsetHotKeys = 30.0f;
const float viewportWidthHalf = cameraState.m_viewportSize.GetX() / 2.0f;
const float viewportHeight = cameraState.m_viewportSize.GetY();
debugDisplay.SetColor(AZ::Color(1.0f, 1.0f, 1.0f, 1.0f));
AZStd::string screenTextCurrentMode = "Edit mode: " + m_subComponentModeName;
float xPos = viewportWidthHalf - xOffsetCurrentMode;
float yPos = viewportHeight - yOffsetCurrentMode;
float textSize = 2.0f;
debugDisplay.Draw2dTextLabel(xPos, yPos, textSize, screenTextCurrentMode.c_str());
AZStd::string screenTextHotKeys = "<Tab> or <Shift+Tab> to change modes";
xPos = viewportWidthHalf - xOffsetHotKeys;
yPos = viewportHeight - yOffsetHotKeys;
textSize = 1.2f;
debugDisplay.Draw2dTextLabel(xPos, yPos, textSize, screenTextHotKeys.c_str());
debugDisplay.SetState(stateBefore);
}
AZStd::shared_ptr<EditorJointTypeDrawer> EditorJointTypeDrawer::GetEditorJointTypeDrawer()
{
return shared_from_this();
}
} // namespace PhysX
@@ -1,41 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/std/smart_ptr/enable_shared_from_this.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <Editor/EditorJointTypeDrawerBus.h>
namespace PhysX
{
/// This class enables drawing in the viewport once for the component modes of multiple components in one entity.
/// Until the component mode framework allows a way to do this, a work-around like this class is necessary.
/// An instance of this class is created for each pair of component type and sub-component mode.
class EditorJointTypeDrawer
: public EditorJointTypeDrawerBus::Handler
, public AZStd::enable_shared_from_this<EditorJointTypeDrawer>
, private AzFramework::ViewportDebugDisplayEventBus::Handler
{
public:
EditorJointTypeDrawer(EditorJointType id,
AzFramework::EntityContextId entityContextId,
const AZStd::string& subComponentModeName);
~EditorJointTypeDrawer();
private:
// AzFramework::ViewportDebugDisplayEventBus
void DisplayViewport2d(
const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay) override;
// PhysX::EditorJointTypeDrawerBus
AZStd::shared_ptr<EditorJointTypeDrawer> GetEditorJointTypeDrawer() override;
AZStd::string m_subComponentModeName;///< Name of the sub component mode. E.g. Position, Rotation, Snap Position, etc.
};
} // namespace PhysX
@@ -1,31 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
namespace PhysX
{
class EditorJointTypeDrawer;
using EditorJointType = AZ::Uuid;
using EditorSubComponentModeNameCrc = AZ::Crc32;
using EditorJointTypeDrawerId = AZStd::pair<EditorJointType, EditorSubComponentModeNameCrc>;
/// The sub-component mode of a component type uses this bus (by invoking GetEditorJointTypeDrawer) to retrieve a drawer.
/// If nothing is returned, it creates an instance of the drawer that will be shared by other instances of the same component type.
class EditorJointTypeDrawerRequests : public AZ::EBusTraits
{
public:
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
using BusIdType = EditorJointTypeDrawerId;
virtual AZStd::shared_ptr<EditorJointTypeDrawer> GetEditorJointTypeDrawer() = 0;
};
using EditorJointTypeDrawerBus = AZ::EBus<EditorJointTypeDrawerRequests>;
} // namespace PhysX
@@ -1,453 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Component/TransformBus.h>
#include <AzToolsFramework/Manipulators/AngularManipulator.h>
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/Manipulators/ManipulatorView.h>
#include <AzToolsFramework/Manipulators/PlanarManipulator.h>
#include <Editor/EditorJointComponentMode.h>
#include <Editor/EditorSubComponentModeAngleCone.h>
#include <PhysX/EditorJointBus.h>
#include <Source/Utils.h>
namespace
{
const float ArrowLength = 2.0f;
const float ConeHeight = 3.0f;
const float XRotationManipulatorRadius = 2.0f;
const float XRotationManipulatorWidth = 0.05f;
}
namespace PhysX
{
struct SharedRotationState
{
AZ::Vector3 m_axis;
AZ::Quaternion m_savedOrientation = AZ::Quaternion::CreateIdentity();
AngleLimitsFloatPair m_valuePair;
};
EditorSubComponentModeAngleCone::EditorSubComponentModeAngleCone(
const AZ::EntityComponentIdPair& entityComponentIdPair
, const AZ::Uuid& componentType
, const AZStd::string& name
, float max
, float min)
: EditorSubComponentModeBase(entityComponentIdPair, componentType, name)
, m_max(max)
, m_min(min)
{
AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId());
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentId
, &EditorJointRequests::GetTransformValue
, PhysX::EditorJointComponentMode::s_parameterTransform);
const AZ::Quaternion localRotation = localTransform.GetRotation();
// Initialize manipulators used to resize the base of the cone.
m_yLinearManipulator = AzToolsFramework::LinearManipulator::MakeShared(worldTransform);
m_yLinearManipulator->AddEntityComponentIdPair(m_entityComponentId);
m_yLinearManipulator->SetAxis(AZ::Vector3::CreateAxisZ());
m_zLinearManipulator = AzToolsFramework::LinearManipulator::MakeShared(worldTransform);
m_zLinearManipulator->AddEntityComponentIdPair(m_entityComponentId);
m_zLinearManipulator->SetAxis(AZ::Vector3::CreateAxisY());
m_yzPlanarManipulator = AzToolsFramework::PlanarManipulator::MakeShared(worldTransform);
m_yzPlanarManipulator->AddEntityComponentIdPair(m_entityComponentId);
m_yzPlanarManipulator->SetAxes(AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ());
ConfigureLinearView(ArrowLength
, AZ::Color(1.0f, 0.0f, 0.0f, 1.0f)
, AZ::Color(0.0f, 1.0f, 0.0f, 1.0f)
, AZ::Color(0.0f, 0.0f, 1.0f, 1.0f));
ConfigurePlanarView(AZ::Color(0.0f, 1.0f, 0.0f, 1.0f)
, AZ::Color(0.0f, 0.0f, 1.0f, 1.0f));
// Position and orientate manipulators
AZ::Transform displacementTransform = localTransform;
AZ::Vector3 displacementTranslate = localRotation.TransformVector(AZ::Vector3(ConeHeight, 0.0f, 0.0f));
displacementTransform.SetTranslation(localTransform.GetTranslation() + displacementTranslate);
m_yLinearManipulator->SetLocalTransform(displacementTransform);
m_zLinearManipulator->SetLocalTransform(displacementTransform);
m_yzPlanarManipulator->SetLocalTransform(displacementTransform);
// Initialize rotation manipulator for rotating cone
m_xRotationManipulator = AzToolsFramework::AngularManipulator::MakeShared(worldTransform);
m_xRotationManipulator->AddEntityComponentIdPair(m_entityComponentId);
m_xRotationManipulator->SetAxis(AZ::Vector3::CreateAxisX());
m_xRotationManipulator->SetLocalTransform(localTransform);
const AZ::Color xRotationManipulatorColor = AZ::Color(1.0f, 0.0f, 0.0f, 1.0f);
m_xRotationManipulator->SetView(AzToolsFramework::CreateManipulatorViewCircle(
*m_xRotationManipulator, xRotationManipulatorColor,
XRotationManipulatorRadius, XRotationManipulatorWidth, AzToolsFramework::DrawHalfDottedCircle));
AZStd::shared_ptr<SharedRotationState> sharedRotationState =
AZStd::make_shared<SharedRotationState>();
struct SharedState
{
AngleLimitsFloatPair m_startValues;
};
auto sharedState = AZStd::make_shared<SharedState>();
m_yLinearManipulator->InstallLeftMouseDownCallback(
[this, sharedState](const AzToolsFramework::LinearManipulator::Action& /*action*/) mutable
{
AngleLimitsFloatPair currentValue;
EditorJointRequestBus::EventResult(
currentValue, m_entityComponentId
, &EditorJointRequests::GetLinearValuePair
, m_name);
sharedState->m_startValues = currentValue;
});
m_yLinearManipulator->InstallMouseMoveCallback(
[this, sharedState](const AzToolsFramework::LinearManipulator::Action& action)
{
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentId
, &EditorJointRequests::GetTransformValue
, PhysX::EditorJointComponentMode::s_parameterTransform);
const AZ::Quaternion localRotation = localTransform.GetRotation();
const float axisDisplacement = action.LocalPositionOffset().Dot(localRotation.TransformVector(action.m_fixed.m_axis));
const float originalBaseY = tan(AZ::DegToRad(sharedState->m_startValues.first)) * ConeHeight;
const float newBaseY = originalBaseY + axisDisplacement;
const float newAngle = AZ::GetClamp(AZ::RadToDeg(atan(newBaseY / ConeHeight)), m_min, m_max);
EditorJointRequestBus::Event(
m_entityComponentId
, &EditorJointRequests::SetLinearValuePair
, m_name
, AngleLimitsFloatPair(newAngle, sharedState->m_startValues.second));
m_yLinearManipulator->SetBoundsDirty();
});
m_zLinearManipulator->InstallLeftMouseDownCallback(
[this, sharedState](const AzToolsFramework::LinearManipulator::Action& /*action*/) mutable
{
AngleLimitsFloatPair currentValue;
EditorJointRequestBus::EventResult(
currentValue, m_entityComponentId
, &EditorJointRequests::GetLinearValuePair
, m_name);
sharedState->m_startValues = currentValue;
});
m_zLinearManipulator->InstallMouseMoveCallback(
[this, sharedState](const AzToolsFramework::LinearManipulator::Action& action)
{
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentId
, &EditorJointRequests::GetTransformValue
, PhysX::EditorJointComponentMode::s_parameterTransform);
const AZ::Quaternion localRotation = localTransform.GetRotation();
const float axisDisplacement = action.LocalPositionOffset().Dot(localRotation.TransformVector(action.m_fixed.m_axis));
const float originalBaseZ = tan(AZ::DegToRad(sharedState->m_startValues.second)) * ConeHeight;
const float newBaseZ = originalBaseZ + axisDisplacement;
const float newAngle = AZ::GetClamp(AZ::RadToDeg(atan(newBaseZ / ConeHeight)), m_min, m_max);
EditorJointRequestBus::Event(
m_entityComponentId
, &EditorJointRequests::SetLinearValuePair
, m_name
, AngleLimitsFloatPair(sharedState->m_startValues.first, newAngle));
m_zLinearManipulator->SetBoundsDirty();
});
m_yzPlanarManipulator->InstallLeftMouseDownCallback(
[this, sharedState](const AzToolsFramework::PlanarManipulator::Action& /*action*/) mutable
{
AngleLimitsFloatPair currentValue;
EditorJointRequestBus::EventResult(
currentValue, m_entityComponentId
, &EditorJointRequests::GetLinearValuePair
, m_name);
sharedState->m_startValues = currentValue;
});
m_yzPlanarManipulator->InstallMouseMoveCallback(
[this, sharedState](const AzToolsFramework::PlanarManipulator::Action& action)
{
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentId
, &EditorJointRequests::GetTransformValue
, PhysX::EditorJointComponentMode::s_parameterTransform);
const AZ::Quaternion localRotation = localTransform.GetRotation();
const float axisDisplacementY = action.LocalPositionOffset().Dot(localRotation.TransformVector(AZ::Vector3::CreateAxisY()));
const float axisDisplacementZ = action.LocalPositionOffset().Dot(localRotation.TransformVector(AZ::Vector3::CreateAxisZ()));
const float axisDisplacement = axisDisplacementZ > axisDisplacementY? axisDisplacementZ : axisDisplacementY;
const float originalBaseY = tan(AZ::DegToRad(sharedState->m_startValues.first)) * ConeHeight;
const float newBaseY = originalBaseY + axisDisplacement;
const float newAngleY = AZ::GetClamp(AZ::RadToDeg(atan(newBaseY / ConeHeight)), m_min, m_max);
const float originalBaseZ = tan(AZ::DegToRad(sharedState->m_startValues.second)) * ConeHeight;
const float newBaseZ = originalBaseZ + axisDisplacement;
const float newAngleZ = AZ::GetClamp(AZ::RadToDeg(atan(newBaseZ / ConeHeight)), m_min, m_max);
EditorJointRequestBus::Event(
m_entityComponentId
, &EditorJointRequests::SetLinearValuePair
, m_name
, AngleLimitsFloatPair(newAngleY, newAngleZ));
m_yzPlanarManipulator->SetBoundsDirty();
});
struct SharedStateXRotate
{
AZ::Transform m_startTM;
};
auto sharedStateXRotate = AZStd::make_shared<SharedStateXRotate>();
auto mouseDownCallback = [this, sharedRotationState](const AzToolsFramework::AngularManipulator::Action& action) mutable -> void
{
AZ::Quaternion normalizedStart = action.m_start.m_rotation.GetNormalized();
sharedRotationState->m_axis = AZ::Vector3(normalizedStart.GetX(), normalizedStart.GetY(), normalizedStart.GetZ());
sharedRotationState->m_savedOrientation = AZ::Quaternion::CreateIdentity();
AngleLimitsFloatPair currentValue;
EditorJointRequestBus::EventResult(
currentValue, m_entityComponentId
, &EditorJointRequests::GetLinearValuePair
, m_name);
sharedRotationState->m_valuePair = currentValue;
};
auto mouseDownRotateXCallback = [this, sharedStateXRotate]([[maybe_unused]] const AzToolsFramework::AngularManipulator::Action& action) mutable -> void
{
PhysX::EditorJointRequestBus::EventResult(sharedStateXRotate->m_startTM
, m_entityComponentId
, &PhysX::EditorJointRequests::GetTransformValue
, PhysX::EditorJointComponentMode::s_parameterTransform);
};
m_xRotationManipulator->InstallLeftMouseDownCallback(mouseDownRotateXCallback);
m_xRotationManipulator->InstallMouseMoveCallback(
[this, sharedStateXRotate]
(const AzToolsFramework::AngularManipulator::Action& action) mutable -> void
{
const AZ::Quaternion manipulatorOrientation = action.m_start.m_rotation * action.m_current.m_delta;
AZ::Transform newTransform = AZ::Transform::CreateIdentity();
newTransform = sharedStateXRotate->m_startTM * AZ::Transform::CreateFromQuaternion(action.m_current.m_delta);
PhysX::EditorJointRequestBus::Event(m_entityComponentId
, &PhysX::EditorJointRequests::SetVector3Value
, PhysX::EditorJointComponentMode::s_parameterPosition
, newTransform.GetTranslation());
PhysX::EditorJointRequestBus::Event(m_entityComponentId
, &PhysX::EditorJointRequests::SetVector3Value
, PhysX::EditorJointComponentMode::s_parameterRotation
, newTransform.GetRotation().GetEulerDegrees());
m_yLinearManipulator->SetLocalOrientation(manipulatorOrientation);
m_zLinearManipulator->SetLocalOrientation(manipulatorOrientation);
m_yLinearManipulator->SetAxis(action.m_current.m_delta.TransformVector(AZ::Vector3::CreateAxisY()));
m_zLinearManipulator->SetAxis(action.m_current.m_delta.TransformVector(AZ::Vector3::CreateAxisZ()));
m_xRotationManipulator->SetLocalOrientation(manipulatorOrientation);
m_yLinearManipulator->SetBoundsDirty();
m_zLinearManipulator->SetBoundsDirty();
m_xRotationManipulator->SetBoundsDirty();
});
m_xRotationManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
m_yLinearManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
m_zLinearManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
m_yzPlanarManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(m_entityComponentId.GetEntityId());
Refresh();
}
EditorSubComponentModeAngleCone::~EditorSubComponentModeAngleCone()
{
AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
m_xRotationManipulator->Unregister();
m_yLinearManipulator->Unregister();
m_zLinearManipulator->Unregister();
m_yzPlanarManipulator->Unregister();
}
void EditorSubComponentModeAngleCone::Refresh()
{
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentId
, &EditorJointRequests::GetTransformValue
, PhysX::EditorJointComponentMode::s_parameterTransform);
float coneHeight = ConeHeight;
AngleLimitsFloatPair yzSwingAngleLimits;
EditorJointRequestBus::EventResult(
yzSwingAngleLimits, m_entityComponentId
, &EditorJointRequests::GetLinearValuePair
, m_name);
// Draw inverted cone (negative cone height) if angles are larger than 90 deg.
if (yzSwingAngleLimits.first > 90.0f || yzSwingAngleLimits.second > 90.0f)
{
coneHeight = -ConeHeight;
}
// reposition manipulators
const AZ::Quaternion localRotation = localTransform.GetRotation();
const AZ::Vector3 linearManipulatorOffset = localTransform.GetTranslation() + localRotation.TransformVector(AZ::Vector3(coneHeight, 0.0f, 0.0f));
m_xRotationManipulator->SetLocalTransform(localTransform);
m_xRotationManipulator->SetBoundsDirty();
localTransform.SetTranslation(linearManipulatorOffset);
m_yLinearManipulator->SetLocalTransform(localTransform);
m_zLinearManipulator->SetLocalTransform(localTransform);
m_yzPlanarManipulator->SetLocalTransform(localTransform);
m_yLinearManipulator->SetBoundsDirty();
m_zLinearManipulator->SetBoundsDirty();
m_yzPlanarManipulator->SetBoundsDirty();
}
void EditorSubComponentModeAngleCone::ConfigureLinearView(
float axisLength, [[maybe_unused]] const AZ::Color& axis1Color, const AZ::Color& axis2Color,
const AZ::Color& axis3Color)
{
const float coneLength = 0.28f;
const float coneRadius = 0.07f;
const auto configureLinearView = [coneLength, axisLength, coneRadius](
AzToolsFramework::LinearManipulator* linearManipulator, const AZ::Color& color)
{
AzToolsFramework::ManipulatorViews views;
views.emplace_back(CreateManipulatorViewLine(
*linearManipulator, color, axisLength, AzToolsFramework::ManipulatorLineBoundWidth()));
views.emplace_back(CreateManipulatorViewCone(
*linearManipulator, color, linearManipulator->GetAxis() * (axisLength - coneLength),
coneLength, coneRadius));
linearManipulator->SetViews(AZStd::move(views));
};
configureLinearView(m_yLinearManipulator.get(), axis2Color);
configureLinearView(m_zLinearManipulator.get(), axis3Color);
}
void EditorSubComponentModeAngleCone::ConfigurePlanarView(const AZ::Color& planeColor,
const AZ::Color& plane2Color)
{
const float planeSize = 0.6f;
AzToolsFramework::ManipulatorViews views;
views.emplace_back(CreateManipulatorViewQuad(
*m_yzPlanarManipulator
, planeColor
, plane2Color
, planeSize));
m_yzPlanarManipulator->SetViews(AZStd::move(views));
}
void EditorSubComponentModeAngleCone::DisplayEntityViewport(
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay)
{
AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId());
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentId
, &EditorJointRequests::GetTransformValue
, PhysX::EditorJointComponentMode::s_parameterTransform);
AZ::u32 stateBefore = debugDisplay.GetState();
debugDisplay.CullOff();
debugDisplay.PushMatrix(worldTransform);
debugDisplay.PushMatrix(localTransform);
const float xAxisArrowLength = 2.0f;
debugDisplay.SetColor(AZ::Color(1.0f, 0.0f, 0.0f, 1.0f));
debugDisplay.DrawArrow(AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(xAxisArrowLength, 0.0f, 0.0f));
AngleLimitsFloatPair yzSwingAngleLimits;
EditorJointRequestBus::EventResult(
yzSwingAngleLimits, m_entityComponentId
, &EditorJointRequests::GetLinearValuePair
, m_name);
const AZ::u32 numEllipseSamples = 16;
AZ::Vector3 ellipseSamples[numEllipseSamples];
float coneHeight = ConeHeight;
// Draw inverted cone if angles are larger than 90 deg.
if (yzSwingAngleLimits.first > 90.0f || yzSwingAngleLimits.second > 90.0f)
{
coneHeight = -ConeHeight;
}
// Compute points along perimeter of cone base
const float coney = tanf(AZ::DegToRad(yzSwingAngleLimits.first)) * coneHeight;
const float conez = tanf(AZ::DegToRad(yzSwingAngleLimits.second)) * coneHeight;
const float step = AZ::Constants::TwoPi / numEllipseSamples;
for (size_t i = 0; i < numEllipseSamples; ++i)
{
const float angleStep = step * i;
ellipseSamples[i].SetX(coneHeight);
ellipseSamples[i].SetY(conez * sin(angleStep));
ellipseSamples[i].SetZ(coney * cos(angleStep));
}
// draw cone
for (size_t i = 0; i < numEllipseSamples; ++i)
{
size_t nextIndex = i + 1;
if (i == numEllipseSamples - 1)
{
nextIndex = 0;
}
// draw cone sides
debugDisplay.SetColor(AZ::Color(1.0f, 1.0f, 1.0f, 0.2f));
debugDisplay.DrawTri(AZ::Vector3(0.0f, 0.0f, 0.0f), ellipseSamples[i], ellipseSamples[nextIndex]);
// draw parameter of cone base
debugDisplay.SetColor(AZ::Color(0.4f, 0.4f, 0.4f, 0.4f));
debugDisplay.DrawLine(ellipseSamples[i], ellipseSamples[nextIndex]);
}
// draw axis lines at base of cone, and from tip to base.
debugDisplay.SetColor(AZ::Color(0.5f, 0.5f, 0.5f, 0.6f));
debugDisplay.DrawLine(ellipseSamples[0], ellipseSamples[numEllipseSamples/2]);
debugDisplay.DrawLine(ellipseSamples[numEllipseSamples*3/4], ellipseSamples[numEllipseSamples/4]);
debugDisplay.DrawLine(AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(coneHeight, 0.0f, 0.0f));
debugDisplay.PopMatrix();//pop local transform
debugDisplay.PopMatrix();//pop world transform
debugDisplay.SetState(stateBefore);
// reposition and reorientate manipulators
Refresh();
}
} // namespace PhysX
@@ -1,63 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Editor/EditorSubComponentModeBase.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <AzToolsFramework/Viewport/ViewportTypes.h>
namespace AzToolsFramework
{
class AngularManipulator;
class LinearManipulator;
class PlanarManipulator;
}
namespace PhysX
{
class EditorSubComponentModeAngleCone
: public PhysX::EditorSubComponentModeBase
, private AzFramework::EntityDebugDisplayEventBus::Handler
{
public:
EditorSubComponentModeAngleCone(
const AZ::EntityComponentIdPair& entityComponentIdPair
, const AZ::Uuid& componentType
, const AZStd::string& name
, float max
, float min);
~EditorSubComponentModeAngleCone();
// PhysX::EditorSubComponentModeBase
void Refresh() override;
private:
// AzFramework::EntityDebugDisplayEventBus
void DisplayEntityViewport(
const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay) override;
void ConfigureLinearView(
float axisLength,
const AZ::Color& axis1Color, const AZ::Color& axis2Color,
const AZ::Color& axis3Color = AZ::Color(0.0f, 0.0f, 1.0f, 0.5f));
void ConfigurePlanarView(const AZ::Color& planeColor = AZ::Color(0.0f, 1.0f, 0.0f, 0.5f)
, const AZ::Color& plane2Color = AZ::Color(0.0f, 0.0f, 1.0f, 0.5f));
AZStd::shared_ptr<AzToolsFramework::AngularManipulator> m_xRotationManipulator;
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> m_yLinearManipulator;
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> m_zLinearManipulator;
AZStd::shared_ptr<AzToolsFramework::PlanarManipulator> m_yzPlanarManipulator;
float m_max = FLT_MAX;
float m_min = 0.0f;
};
} // namespace PhysX
@@ -1,318 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Component/TransformBus.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/Manipulators/ManipulatorView.h>
#include <Editor/EditorJointComponentMode.h>
#include <Editor/EditorSubComponentModeAnglePair.h>
#include <Source/Utils.h>
namespace
{
const float Alpha = 0.6f;
const AZ::Color ColorDefault = AZ::Color(1.0f, 1.0f, 1.0f, Alpha);
const AZ::Color ColorFirst = AZ::Color(1.0f, 0.0f, 0.0f, Alpha);
const AZ::Color ColorSecond = AZ::Color(0.0f, 1.0f, 0.0f, Alpha);
const AZ::Color ColorSweepArc = AZ::Color(1.0f, 1.0f, 1.0f, Alpha);
const float SweepLineDisplaceFactor = 0.5f;
const float SweepLineThickness = 1.0f;
const float SweepLineGranularity = 1.0f;
}
namespace PhysX
{
EditorSubComponentModeAnglePair::EditorSubComponentModeAnglePair(
const AZ::EntityComponentIdPair& entityComponentIdPair
, const AZ::Uuid& componentType
, const AZStd::string& name
, const AZ::Vector3& axis
, float firstMax
, float firstMin
, float secondMax
, float secondMin)
: EditorSubComponentModeBase(entityComponentIdPair, componentType, name)
, m_axis(axis)
, m_firstMax(firstMax)
, m_firstMin(firstMin)
, m_secondMax(secondMax)
, m_secondMin(secondMin)
{
AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId());
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentId
, &EditorJointRequests::GetTransformValue
, PhysX::EditorJointComponentMode::s_parameterTransform);
const AZ::Quaternion localRotation = localTransform.GetRotation();
AZ::Vector3 displacement = m_axis;
AZ::Transform displacementTransform = localTransform;
AZ::Vector3 displacementTranslate = localRotation.TransformVector(displacement);
displacementTransform.SetTranslation(localTransform.GetTranslation() + displacementTranslate);
m_firstManipulator = AzToolsFramework::AngularManipulator::MakeShared(worldTransform);
m_firstManipulator->AddEntityComponentIdPair(m_entityComponentId);
m_firstManipulator->SetAxis(m_axis);
m_firstManipulator->SetLocalTransform(displacementTransform);
displacement = -m_axis;
displacementTranslate = localRotation.TransformVector(displacement);
displacementTransform.SetTranslation(localTransform.GetTranslation() + displacementTranslate);
m_secondManipulator = AzToolsFramework::AngularManipulator::MakeShared(worldTransform);
m_secondManipulator->AddEntityComponentIdPair(m_entityComponentId);
m_secondManipulator->SetAxis(m_axis);
m_secondManipulator->SetLocalTransform(displacementTransform);
const float manipulatorRadius = 2.0f;
const float manipulatorWidth = 0.05f;
m_firstManipulator->SetView(AzToolsFramework::CreateManipulatorViewCircle(
*m_firstManipulator, ColorFirst,
manipulatorRadius, manipulatorWidth, AzToolsFramework::DrawHalfDottedCircle));
m_secondManipulator->SetView(AzToolsFramework::CreateManipulatorViewCircle(
*m_secondManipulator, ColorSecond,
manipulatorRadius, manipulatorWidth, AzToolsFramework::DrawHalfDottedCircle));
Refresh();
AZStd::shared_ptr<SharedRotationState> sharedRotationState =
AZStd::make_shared<SharedRotationState>();
auto mouseDownCallback = [this, sharedRotationState](const AzToolsFramework::AngularManipulator::Action& action) mutable -> void
{
const AZ::Quaternion normalizedStart = action.m_start.m_rotation.GetNormalized();
sharedRotationState->m_axis = AZ::Vector3(normalizedStart.GetX(), normalizedStart.GetY(), normalizedStart.GetZ());
sharedRotationState->m_savedOrientation = AZ::Quaternion::CreateIdentity();
AngleLimitsFloatPair currentValue;
EditorJointRequestBus::EventResult(
currentValue, m_entityComponentId
, &EditorJointRequests::GetLinearValuePair
, m_name);
sharedRotationState->m_valuePair = currentValue;
};
m_firstManipulator->InstallLeftMouseDownCallback(mouseDownCallback);
m_secondManipulator->InstallLeftMouseDownCallback(mouseDownCallback);
m_firstManipulator->InstallMouseMoveCallback(
[this, sharedRotationState]
(const AzToolsFramework::AngularManipulator::Action& action) mutable -> void
{
float angleDelta;
AZ::Quaternion manipulatorOrientation;
const float newValue = MouseMove(sharedRotationState, action, true, angleDelta, manipulatorOrientation);
if (newValue > m_firstMax || newValue < m_firstMin)
{
return;
}
m_firstManipulator->SetLocalOrientation(manipulatorOrientation);
const float newFirstValue = AZ::GetClamp(sharedRotationState->m_valuePair.first + angleDelta, m_firstMin, m_firstMax);
EditorJointRequestBus::Event(
m_entityComponentId
, &EditorJointRequests::SetLinearValuePair
, m_name
, AngleLimitsFloatPair(newFirstValue, sharedRotationState->m_valuePair.second));
m_firstManipulator->SetBoundsDirty();
});
m_secondManipulator->InstallMouseMoveCallback(
[this, sharedRotationState]
(const AzToolsFramework::AngularManipulator::Action& action) mutable -> void
{
float angleDelta;
AZ::Quaternion manipulatorOrientation;
const float newValue = MouseMove(sharedRotationState, action, false, angleDelta, manipulatorOrientation);
if (newValue > m_secondMax || newValue < m_secondMin)
{
return; //Not handling values exceeding limits
}
m_secondManipulator->SetLocalOrientation(manipulatorOrientation);
float newSecondValue = AZ::GetClamp(sharedRotationState->m_valuePair.second + angleDelta, m_secondMin, m_secondMax);
EditorJointRequestBus::Event(
m_entityComponentId
, &EditorJointRequests::SetLinearValuePair
, m_name
, AngleLimitsFloatPair(sharedRotationState->m_valuePair.first, newSecondValue));
m_secondManipulator->SetBoundsDirty();
});
m_firstManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
m_secondManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(m_entityComponentId.GetEntityId());
}
EditorSubComponentModeAnglePair::~EditorSubComponentModeAnglePair()
{
AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
m_firstManipulator->Unregister();
m_secondManipulator->Unregister();
}
void EditorSubComponentModeAnglePair::Refresh()
{
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentId
, &EditorJointRequests::GetTransformValue
, PhysX::EditorJointComponentMode::s_parameterTransform);
const AZ::Quaternion localRotation = localTransform.GetRotation();
AZ::Transform displacementTransform = localTransform;
AZ::Vector3 displacement = m_axis;
AZ::Vector3 displacementTranslate = localRotation.TransformVector(displacement);
displacementTransform.SetTranslation(localTransform.GetTranslation() + displacementTranslate);
m_firstManipulator->SetLocalTransform(displacementTransform);
displacement = -m_axis;
displacementTranslate = localRotation.TransformVector(displacement);
displacementTransform.SetTranslation(localTransform.GetTranslation() + displacementTranslate);
m_secondManipulator->SetLocalTransform(displacementTransform);
m_firstManipulator->SetBoundsDirty();
m_secondManipulator->SetBoundsDirty();
}
void EditorSubComponentModeAnglePair::DisplayEntityViewport(
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay)
{
AngleLimitsFloatPair currentValue;
EditorJointRequestBus::EventResult(
currentValue, m_entityComponentId
, &EditorJointRequests::GetLinearValuePair
, m_name);
const float size = 2.0f;
AZ::Vector3 axisPoint = m_axis * size * 0.5f;
AZStd::array<AZ::Vector3, 4> points = {
-axisPoint
, axisPoint
, axisPoint
, -axisPoint
};
if (abs(m_axis.GetX() - 1.0f) < FLT_EPSILON)
{
points[2].SetZ(size);
points[3].SetZ(size);
}
else if (abs(m_axis.GetY() - 1.0f) < FLT_EPSILON)
{
points[2].SetX(size);
points[3].SetX(size);
}
else if (abs(m_axis.GetZ() - 1.0f) < FLT_EPSILON)
{
points[2].SetX(size);
points[3].SetX(size);
}
AZ::u32 stateBefore = debugDisplay.GetState();
debugDisplay.CullOff();
debugDisplay.SetAlpha(Alpha);
AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId());
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentId
, &EditorJointRequests::GetTransformValue
, PhysX::EditorJointComponentMode::s_parameterTransform);
debugDisplay.PushMatrix(worldTransform);
debugDisplay.PushMatrix(localTransform);
debugDisplay.SetColor(ColorSweepArc);
const AZ::Vector3 zeroVector = AZ::Vector3::CreateZero();
const AZ::Vector3 posPosition = m_axis * SweepLineDisplaceFactor;
const AZ::Vector3 negPosition = -posPosition;
debugDisplay.DrawArc(posPosition, SweepLineThickness, -currentValue.first, currentValue.first, SweepLineGranularity, -m_axis);
debugDisplay.DrawArc(zeroVector, SweepLineThickness, -currentValue.first, currentValue.first, SweepLineGranularity, -m_axis);
debugDisplay.DrawArc(negPosition, SweepLineThickness, -currentValue.first, currentValue.first, SweepLineGranularity, -m_axis);
debugDisplay.DrawArc(posPosition, SweepLineThickness, 0.0f, abs(currentValue.second), SweepLineGranularity, -m_axis);
debugDisplay.DrawArc(zeroVector, SweepLineThickness, 0.0f, abs(currentValue.second), SweepLineGranularity, -m_axis);
debugDisplay.DrawArc(negPosition, SweepLineThickness, 0.0f, abs(currentValue.second), SweepLineGranularity, -m_axis);
AZ::Quaternion firstRotate = AZ::Quaternion::CreateFromAxisAngle(m_axis, AZ::DegToRad(currentValue.first));
AZ::Transform firstTM = AZ::Transform::CreateFromQuaternion(firstRotate);
debugDisplay.PushMatrix(firstTM);
debugDisplay.SetColor(ColorFirst);
debugDisplay.DrawQuad(points[0], points[1], points[2], points[3]);
debugDisplay.PopMatrix();
AZ::Quaternion secondRotate = AZ::Quaternion::CreateFromAxisAngle(m_axis, AZ::DegToRad(currentValue.second));
AZ::Transform secondTM = AZ::Transform::CreateFromQuaternion(secondRotate);
debugDisplay.PushMatrix(secondTM);
debugDisplay.SetColor(ColorSecond);
debugDisplay.DrawQuad(points[0], points[1], points[2], points[3]);
debugDisplay.PopMatrix();
debugDisplay.SetColor(ColorDefault);
debugDisplay.DrawQuad(points[0], points[1], points[2], points[3]);
debugDisplay.PopMatrix(); // pop local transform
debugDisplay.PopMatrix(); // pop global transform
debugDisplay.SetState(stateBefore);
// reposition and reorientate manipulators
Refresh();
}
float EditorSubComponentModeAnglePair::MouseMove(AZStd::shared_ptr<SharedRotationState>& sharedRotationState
, const AzToolsFramework::AngularManipulator::Action& action
, bool isFirstValue
, float& angleDelta
, AZ::Quaternion& manipulatorOrientation)
{
sharedRotationState->m_savedOrientation = action.m_current.m_delta.GetInverseFull();
angleDelta = 0.0f;
AZ::Vector3 axis = m_axis;
sharedRotationState->m_savedOrientation.ConvertToAxisAngle(axis, angleDelta);
// Polarity of axis is switched by ConvertToAxisAngle call depending on direction of rotation
if (abs(m_axis.GetX() - 1.0f) < FLT_EPSILON)
{
angleDelta = AZ::RadToDeg(angleDelta) * axis.GetX();
}
else if (abs(m_axis.GetY() - 1.0f) < FLT_EPSILON)
{
angleDelta = AZ::RadToDeg(angleDelta) * axis.GetY();
}
else if (abs(m_axis.GetZ() - 1.0f) < FLT_EPSILON)
{
angleDelta = AZ::RadToDeg(angleDelta) * axis.GetZ();
}
manipulatorOrientation = action.m_start.m_rotation * action.m_current.m_delta;
if (isFirstValue)
{
return sharedRotationState->m_valuePair.first + angleDelta;
}
else
{
return sharedRotationState->m_valuePair.second + angleDelta;
}
}
} // namespace PhysX
@@ -1,66 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Editor/EditorSubComponentModeBase.h>
#include <PhysX/EditorJointBus.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzToolsFramework/Manipulators/AngularManipulator.h>
namespace PhysX
{
class EditorSubComponentModeAnglePair
: public PhysX::EditorSubComponentModeBase
, private AzFramework::EntityDebugDisplayEventBus::Handler
{
public:
EditorSubComponentModeAnglePair(
const AZ::EntityComponentIdPair& entityComponentIdPair
, const AZ::Uuid& componentType
, const AZStd::string& name
, const AZ::Vector3& axis
, float firstMax
, float firstMin
, float secondMax
, float secondMin);
~EditorSubComponentModeAnglePair();
// PhysX::EditorSubComponentModeBase
void Refresh() override;
private:
struct SharedRotationState
{
AZ::Vector3 m_axis;
AZ::Quaternion m_savedOrientation = AZ::Quaternion::CreateIdentity();
AngleLimitsFloatPair m_valuePair;
};
// AzFramework::EntityDebugDisplayEventBus
void DisplayEntityViewport(
const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay) override;
float MouseMove(AZStd::shared_ptr<SharedRotationState>& sharedRotationState
, const AzToolsFramework::AngularManipulator::Action& action
, bool isFirstValue
, float& angleDelta
, AZ::Quaternion& manipulatorOrientation);
AZStd::shared_ptr<AzToolsFramework::AngularManipulator> m_firstManipulator;
AZStd::shared_ptr<AzToolsFramework::AngularManipulator> m_secondManipulator;
AZ::Vector3 m_axis = AZ::Vector3::CreateAxisX();
float m_firstMax = FLT_MAX;
float m_firstMin = -FLT_MAX;
float m_secondMax = FLT_MAX;
float m_secondMin = -FLT_MAX;
};
} // namespace PhysX
@@ -1,37 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <Editor/EditorJointTypeDrawer.h>
#include <Editor/EditorSubComponentModeBase.h>
#include <AzCore/std/smart_ptr/make_shared.h>
namespace PhysX
{
EditorSubComponentModeBase::EditorSubComponentModeBase(
const AZ::EntityComponentIdPair& entityComponentIdPair
, const AZ::Uuid& componentType
, const AZStd::string& name)
: m_entityComponentId(entityComponentIdPair),
m_name(name)
{
// The first time this is called, no object will respond to this bus call as no object is connected at the address,
// and m_jointTypeDrawer will remain as a nullptr.
EditorJointTypeDrawerBus::EventResult(m_jointTypeDrawer,
EditorJointTypeDrawerId(componentType, EditorSubComponentModeNameCrc(name)),
&EditorJointTypeDrawerBus::Events::GetEditorJointTypeDrawer);
if (!m_jointTypeDrawer)
{
// Once this is called, the bus call to GetEditorJointTypeDrawer above will no longer get a null response, until m_jointTypeDrawer is destroyed.
m_jointTypeDrawer = AZStd::make_shared<EditorJointTypeDrawer>(componentType,
AzToolsFramework::GetEntityContextId(),
m_name);
}
}
} // namespace PhysX
@@ -1,55 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Component/ComponentBus.h>
namespace AzFramework
{
class DebugDisplayRequests;
struct ViewportInfo;
}
namespace AzToolsFramework
{
namespace ViewportInteraction
{
struct MouseInteractionEvent;
}
}
namespace PhysX
{
class EditorJointTypeDrawer;
/// Base class for (joints) sub-component modes.
class EditorSubComponentModeBase
{
public:
EditorSubComponentModeBase(
const AZ::EntityComponentIdPair& entityComponentIdPair,
const AZ::Uuid& componentType,
const AZStd::string& name);
virtual ~EditorSubComponentModeBase() = default;
/// Additional mouse handling by sub-component mode. Does not absorb mouse event.
virtual void HandleMouseInteraction(
[[maybe_unused]] const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteraction) {};
virtual void Refresh() = 0;
AZStd::string m_name;///< Name of sub-component mode.
protected:
AZ::EntityComponentIdPair m_entityComponentId;///< Entity Id and component pair.
private:
AZStd::shared_ptr<EditorJointTypeDrawer> m_jointTypeDrawer;///< Drawer that draws component type specific objects in the viewport.
};
} // namespace PhysX
@@ -1,133 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Component/TransformBus.h>
#include <AzCore/std/smart_ptr/intrusive_ptr.h>
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <Editor/EditorJointComponentMode.h>
#include <Editor/EditorSubComponentModeLinear.h>
#include <PhysX/EditorJointBus.h>
#include <Source/Utils.h>
namespace PhysX
{
EditorSubComponentModeLinear::EditorSubComponentModeLinear(
const AZ::EntityComponentIdPair& entityComponentIdPair
, const AZ::Uuid& componentType
, const AZStd::string& name
, float exponent
, float max
, float min)
: EditorSubComponentModeBase(entityComponentIdPair, componentType, name)
, m_exponent(exponent)
, m_inverseExponent(1.0f/exponent)
, m_max(max)
, m_min(min)
{
AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId());
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentId
, &EditorJointRequests::GetTransformValue
, PhysX::EditorJointComponentMode::s_parameterTransform);
m_manipulator = AzToolsFramework::LinearManipulator::MakeShared(worldTransform);
m_manipulator->AddEntityComponentIdPair(m_entityComponentId);
m_manipulator->SetAxis(AZ::Vector3::CreateAxisX());
m_manipulator->SetLocalTransform(localTransform);
Refresh();
const AZ::Color manipulatorColor(0.3f, 0.3f, 0.3f, 1.0f);
const float manipulatorSize = 0.05f;
AzToolsFramework::ManipulatorViews views;
views.emplace_back(AzToolsFramework::CreateManipulatorViewQuadBillboard(manipulatorColor
, manipulatorSize));
m_manipulator->SetViews(AZStd::move(views));
struct SharedState
{
float m_startingValue = 0.0f;
};
auto sharedState = AZStd::make_shared<SharedState>();
m_manipulator->InstallLeftMouseDownCallback(
[this, sharedState](const AzToolsFramework::LinearManipulator::Action& /*action*/) mutable
{
float currentValue = 0.0f;
EditorJointRequestBus::EventResult(
currentValue, m_entityComponentId
, &EditorJointRequests::GetLinearValue
, m_name);
sharedState->m_startingValue = currentValue;
});
m_manipulator->InstallMouseMoveCallback(
[this, sharedState](const AzToolsFramework::LinearManipulator::Action& action)
{
const float axisDisplacement = action.LocalPositionOffset().Dot(action.m_fixed.m_axis);
float newValue = AZ::GetClamp(sharedState->m_startingValue + DisplacementToDeltaValue(axisDisplacement), m_min, m_max);
EditorJointRequestBus::Event(
m_entityComponentId
, &EditorJointRequests::SetLinearValue
, m_name
, newValue);
const AZ::Vector3 localPosition = action.LocalPosition().GetMax(AZ::Vector3(0.01f, 0.0f, 0.0f));
m_manipulator->SetLocalTransform(AZ::Transform::CreateTranslation(localPosition));
m_manipulator->SetBoundsDirty();
});
m_manipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
}
EditorSubComponentModeLinear::~EditorSubComponentModeLinear()
{
m_manipulator->Unregister();
}
void EditorSubComponentModeLinear::Refresh()
{
float currentValue = 0.0f;
EditorJointRequestBus::EventResult(
currentValue, m_entityComponentId
, &EditorJointRequests::GetLinearValue
, m_name);
m_manipulator->SetLocalTransform(AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX() * ValueToDisplacement(currentValue)));
}
float EditorSubComponentModeLinear::DisplacementToDeltaValue(float displacement) const
{
if (displacement > 0.0f)
{
return powf(displacement, m_exponent);
}
else if (displacement < 0.0f)
{
return -powf(fabsf(displacement), m_exponent);
}
else
{
return 0.0f;
}
}
float EditorSubComponentModeLinear::ValueToDisplacement(float value) const
{
return powf(value, m_inverseExponent);
}
} // namespace PhysX
@@ -1,46 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Editor/EditorSubComponentModeBase.h>
namespace AzToolsFramework
{
class LinearManipulator;
}
namespace PhysX
{
class EditorSubComponentModeLinear
: public PhysX::EditorSubComponentModeBase
{
public:
EditorSubComponentModeLinear(
const AZ::EntityComponentIdPair& entityComponentIdPair
, const AZ::Uuid& componentType
, const AZStd::string& name
, float exponent
, float max
, float min);
~EditorSubComponentModeLinear();
// PhysX::EditorSubComponentModeBase
void Refresh() override;
private:
float DisplacementToDeltaValue(float displacement) const;
float ValueToDisplacement(float value) const;
float m_exponent = 1.0f;
float m_inverseExponent = 1.0f;
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> m_manipulator;
float m_max = FLT_MAX;
float m_min = -FLT_MAX;
};
} // namespace PhysX
@@ -1,151 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Component/TransformBus.h>
#include <AzToolsFramework/Manipulators/AngularManipulator.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/Manipulators/ManipulatorView.h>
#include <Editor/EditorJointComponentMode.h>
#include <PhysX/EditorJointBus.h>
#include <Editor/EditorSubComponentModeRotation.h>
#include <Source/Utils.h>
namespace PhysX
{
EditorSubComponentModeRotation::EditorSubComponentModeRotation(
const AZ::EntityComponentIdPair& entityComponentIdPair
, const AZ::Uuid& componentType
, const AZStd::string& name)
: EditorSubComponentModeBase(entityComponentIdPair, componentType, name)
{
CreateManipulators();
RegisterManipulators();
AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(m_entityComponentId.GetEntityId());
}
EditorSubComponentModeRotation::~EditorSubComponentModeRotation()
{
AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
UnregisterManipulators();
}
void EditorSubComponentModeRotation::Refresh()
{
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentId
, &EditorJointRequests::GetTransformValue
, PhysX::EditorJointComponentMode::s_parameterTransform);
for (auto rotationManipulator : m_rotationManipulators)
{
rotationManipulator->SetLocalTransform(localTransform);
rotationManipulator->SetBoundsDirty();
}
}
void EditorSubComponentModeRotation::DisplayEntityViewport(
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo,
[[maybe_unused]] AzFramework::DebugDisplayRequests& debugDisplay)
{
Refresh(); //Update position and orientation of manipulators.
}
void EditorSubComponentModeRotation::CreateManipulators()
{
AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId());
const AZ::Quaternion worldRotation = worldTransform.GetRotation();
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentId
, &EditorJointRequests::GetTransformValue
, PhysX::EditorJointComponentMode::s_parameterTransform);
const AZStd::array<AZ::Vector3, 3> axes = { AZ::Vector3::CreateAxisX()
, AZ::Vector3::CreateAxisY()
, AZ::Vector3::CreateAxisZ()};
const AZStd::array <AZ::Color, 3> colors = { AZ::Color(1.0f, 0.0f, 0.0f, 1.0f)
, AZ::Color(0.0f, 1.0f, 0.0f, 1.0f)
, AZ::Color(0.0f, 0.0f, 1.0f, 1.0f)};
for (AZ::u32 i = 0; i < 3; ++i)
{
m_rotationManipulators[i] = AzToolsFramework::AngularManipulator::MakeShared(worldTransform);
m_rotationManipulators[i]->AddEntityComponentIdPair(m_entityComponentId);
m_rotationManipulators[i]->SetAxis(axes[i]);
m_rotationManipulators[i]->SetLocalTransform(localTransform);
const float manipulatorRadius = 2.0f;
m_rotationManipulators[i]->SetView(AzToolsFramework::CreateManipulatorViewCircle(
*m_rotationManipulators[i], colors[i], manipulatorRadius,
AzToolsFramework::ManipulatorCicleBoundWidth(), AzToolsFramework::DrawHalfDottedCircle));
}
Refresh();
InstallManipulatorMouseCallbacks();
}
void EditorSubComponentModeRotation::InstallManipulatorMouseCallbacks()
{
struct SharedState
{
AZ::Transform m_startTM;
};
auto sharedState = AZStd::make_shared<SharedState>();
auto mouseDownRotateXCallback = [this, sharedState]([[maybe_unused]] const AzToolsFramework::AngularManipulator::Action& action) mutable -> void
{
PhysX::EditorJointRequestBus::EventResult(sharedState->m_startTM
, m_entityComponentId
, &PhysX::EditorJointRequests::GetTransformValue
, PhysX::EditorJointComponentMode::s_parameterTransform);
};
for (AZ::u32 index = 0; index < 3; ++index)
{
m_rotationManipulators[index]->InstallLeftMouseDownCallback(mouseDownRotateXCallback);
m_rotationManipulators[index]->InstallMouseMoveCallback(
[this, index, sharedState]
(const AzToolsFramework::AngularManipulator::Action& action) mutable -> void
{
const AZ::Quaternion manipulatorOrientation = action.m_start.m_rotation * action.m_current.m_delta;
AZ::Transform newTransform = AZ::Transform::CreateIdentity();
newTransform = sharedState->m_startTM * AZ::Transform::CreateFromQuaternion(action.m_current.m_delta);
PhysX::EditorJointRequestBus::Event(m_entityComponentId
, &PhysX::EditorJointRequests::SetVector3Value
, PhysX::EditorJointComponentMode::s_parameterRotation
, newTransform.GetRotation().GetEulerDegrees());
m_rotationManipulators[index]->SetLocalOrientation(manipulatorOrientation);
m_rotationManipulators[index]->SetBoundsDirty();
});
}
}
void EditorSubComponentModeRotation::RegisterManipulators()
{
for (auto rotationManipulator : m_rotationManipulators)
{
rotationManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
}
}
void EditorSubComponentModeRotation::UnregisterManipulators()
{
for (auto rotationManipulator : m_rotationManipulators)
{
rotationManipulator->Unregister();
}
}
}
@@ -1,48 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Editor/EditorSubComponentModeBase.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
namespace AzToolsFramework
{
class AngularManipulator;
}
namespace PhysX
{
class EditorSubComponentModeRotation
: public PhysX::EditorSubComponentModeBase
, private AzFramework::EntityDebugDisplayEventBus::Handler
{
public:
EditorSubComponentModeRotation(
const AZ::EntityComponentIdPair& entityComponentIdPair
, const AZ::Uuid& componentType
, const AZStd::string& name);
~EditorSubComponentModeRotation();
// PhysX::EditorSubComponentModeBase
void Refresh() override;
private:
// AzFramework::EntityDebugDisplayEventBus
void DisplayEntityViewport(
const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay) override;
void CreateManipulators();
void InstallManipulatorMouseCallbacks();
void RegisterManipulators();
void UnregisterManipulators();
AZStd::array<AZStd::shared_ptr<AzToolsFramework::AngularManipulator>, 3> m_rotationManipulators;
};
} // namespace PhysX
@@ -1,97 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Math/Color.h>
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <Editor/EditorJointComponentMode.h>
#include <Editor/EditorSubComponentModeSnapPosition.h>
#include <PhysX/EditorJointBus.h>
#include <Source/Utils.h>
namespace PhysX
{
EditorSubComponentModeSnapPosition::EditorSubComponentModeSnapPosition(
const AZ::EntityComponentIdPair& entityComponentIdPair
, const AZ::Uuid& componentType
, const AZStd::string& name
, bool selectLeadOnSnap)
: EditorSubComponentModeSnap(entityComponentIdPair, componentType, name)
, m_selectLeadOnSnap(selectLeadOnSnap)
{
InitMouseDownCallBack();
m_manipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(m_entityComponentId.GetEntityId());
}
EditorSubComponentModeSnapPosition::~EditorSubComponentModeSnapPosition()
{
AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
m_manipulator->Unregister();
}
void EditorSubComponentModeSnapPosition::DisplaySpecificSnapType(
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay,
const AZ::Vector3& jointPosition,
const AZ::Vector3& snapDirection,
const float snapLength)
{
const float arrowLength = 1.0f;
const float iconGap = 1.0f;
const AZ::Vector3 iconPosition = jointPosition +
(snapDirection * (snapLength + arrowLength + iconGap));
debugDisplay.SetColor(AZ::Colors::Red);
debugDisplay.DrawArrow(iconPosition, iconPosition + AZ::Vector3(arrowLength, 0.0f, 0.0f));
debugDisplay.SetColor(AZ::Colors::Green);
debugDisplay.DrawArrow(iconPosition, iconPosition + AZ::Vector3(0.2f, arrowLength, 0.2f));
debugDisplay.SetColor(AZ::Colors::Blue);
debugDisplay.DrawArrow(iconPosition, iconPosition + AZ::Vector3(0.0f, 0.0f, arrowLength));
}
void EditorSubComponentModeSnapPosition::InitMouseDownCallBack()
{
m_manipulator->InstallLeftMouseDownCallback(
[this](const AzToolsFramework::LinearManipulator::Action& /*action*/) mutable
{
if (!m_pickedEntity.IsValid())
{
return;
}
const AZ::Vector3 newLocalPosition = PhysX::Utils::ComputeJointLocalTransform(
PhysX::Utils::GetEntityWorldTransformWithScale(m_pickedEntity),
PhysX::Utils::GetEntityWorldTransformWithScale(m_entityComponentId.GetEntityId())).GetTranslation();
PhysX::EditorJointRequestBus::Event(m_entityComponentId
, &PhysX::EditorJointRequests::SetVector3Value
, PhysX::EditorJointComponentMode::s_parameterPosition
, newLocalPosition);
const bool selectedEntityIsNotJointEntity = m_pickedEntity != m_entityComponentId.GetEntityId();
AZ_Error("EditorSubComponentModeSnapPosition",
selectedEntityIsNotJointEntity,
"Joint's lead entity cannot be the same as the entity in which the joint resides. Select lead entity on snap failed.");
if (m_selectLeadOnSnap &&
selectedEntityIsNotJointEntity)
{
PhysX::EditorJointRequestBus::Event(m_entityComponentId
, &PhysX::EditorJointRequests::SetEntityIdValue
, PhysX::EditorJointComponentMode::s_parameterLeadEntity
, m_pickedEntity);
}
m_manipulator->SetBoundsDirty();
});
}
} // namespace PhysX
@@ -1,42 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Editor/EditorSubComponentModeSnap.h>
namespace PhysX
{
/// This sub-component mode gets an entity position from its base class on mouse down
/// and sets a position (AZ::Vector3) value in the component that uses it.
class EditorSubComponentModeSnapPosition
: public EditorSubComponentModeSnap
{
public:
EditorSubComponentModeSnapPosition(
const AZ::EntityComponentIdPair& entityComponentIdPair
, const AZ::Uuid& componentType
, const AZStd::string& name
, bool selectLeadOnSnap);
~EditorSubComponentModeSnapPosition() override;
protected:
// PhysX::EditorSubComponentModeSnap
void DisplaySpecificSnapType(
const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay,
const AZ::Vector3& jointPosition,
const AZ::Vector3& snapDirection,
float snapLength) override;
void InitMouseDownCallBack() override;
private:
bool m_selectLeadOnSnap = true;
};
} // namespace PhysX
@@ -1,122 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Editor/EditorJointComponentMode.h>
#include <Editor/EditorSubComponentModeSnapRotation.h>
#include <PhysX/EditorJointBus.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Math/Color.h>
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
namespace PhysX
{
EditorSubComponentModeSnapRotation::EditorSubComponentModeSnapRotation(
const AZ::EntityComponentIdPair& entityComponentIdPair
, const AZ::Uuid& componentType
, const AZStd::string& name)
: EditorSubComponentModeSnap(entityComponentIdPair, componentType, name)
{
InitMouseDownCallBack();
m_manipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(m_entityComponentId.GetEntityId());
}
EditorSubComponentModeSnapRotation::~EditorSubComponentModeSnapRotation()
{
AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
m_manipulator->Unregister();
}
void EditorSubComponentModeSnapRotation::DisplaySpecificSnapType(
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay,
const AZ::Vector3& jointPosition,
const AZ::Vector3& snapDirection,
const float snapLength)
{
const float circleRadius = 0.5f;
const float iconGap = 1.0f;
const AZ::Vector3 iconPosition = jointPosition +
(snapDirection * (snapLength + circleRadius * 2.0f + iconGap));
debugDisplay.SetColor(AZ::Colors::Red);
debugDisplay.DrawCircle(iconPosition, circleRadius, 0);
debugDisplay.SetColor(AZ::Colors::Green);
debugDisplay.DrawCircle(iconPosition, circleRadius, 1);
debugDisplay.SetColor(AZ::Colors::Blue);
debugDisplay.DrawCircle(iconPosition, circleRadius, 2);
}
void EditorSubComponentModeSnapRotation::InitMouseDownCallBack()
{
m_manipulator->InstallLeftMouseDownCallback(
[this]([[maybe_unused]] const AzToolsFramework::LinearManipulator::Action& action) mutable
{
if (!m_pickedEntity.IsValid())
{
return;
}
AZ::EntityId leadEntityId;
PhysX::EditorJointRequestBus::EventResult(leadEntityId,
m_entityComponentId,
&PhysX::EditorJointRequests::GetEntityIdValue,
PhysX::EditorJointComponentMode::s_parameterLeadEntity);
if (leadEntityId.IsValid() && m_pickedEntity == leadEntityId)
{
AZ_Warning("EditorsubComponentModeSnapRotation",
false,
"The entity %s is the lead of the joint. Please snap rotation (or orientation) of joint to another entity that is not the lead entity.",
GetPickedEntityName().c_str());
return;
}
AZ::Transform worldTransform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(
worldTransform, m_entityComponentId.GetEntityId(), &AZ::TransformInterface::GetWorldTM);
worldTransform.ExtractUniformScale();
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentId
, &EditorJointRequests::GetTransformValue
, PhysX::EditorJointComponentMode::s_parameterTransform);
AZ::Transform pickedEntityTransform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(
pickedEntityTransform, m_pickedEntity, &AZ::TransformInterface::GetWorldTM);
const AZ::Transform worldTransformInv = worldTransform.GetInverse();
const AZ::Vector3 pickedLocalPosition = worldTransformInv.TransformVector(pickedEntityTransform.GetTranslation()) - localTransform.GetTranslation();
if (abs(pickedLocalPosition.GetLength()) < FLT_EPSILON)
{
AZ_Warning("EditorsubComponentModeSnapRotation",
false,
"The entity %s is too close to the joint position. Please snap rotation to an entity that is not at the position of the joint.",
GetPickedEntityName().c_str());
return;
}
const AZ::Vector3 targetDirection = pickedLocalPosition.GetNormalized();
const AZ::Vector3 sourceDirection = AZ::Vector3::CreateAxisX();
const AZ::Quaternion newLocalRotation = AZ::Quaternion::CreateShortestArc(sourceDirection, targetDirection);
PhysX::EditorJointRequestBus::Event(m_entityComponentId
, &PhysX::EditorJointRequests::SetVector3Value
, PhysX::EditorJointComponentMode::s_parameterRotation //using rotation parameter name to set the local rotation value
, newLocalRotation.GetEulerDegrees());
m_manipulator->SetBoundsDirty();
});
}
} // namespace PhysX
@@ -1,38 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Editor/EditorSubComponentModeSnap.h>
namespace PhysX
{
/// TThis sub-component mode gets an entity position from its base class on mouse down
/// and sets a rotation (AZ::Quaternion) value in the component that uses it.
class EditorSubComponentModeSnapRotation
: public EditorSubComponentModeSnap
{
public:
EditorSubComponentModeSnapRotation(
const AZ::EntityComponentIdPair& entityComponentIdPair
, const AZ::Uuid& componentType
, const AZStd::string& name);
~EditorSubComponentModeSnapRotation() override;
protected:
// PhysX::EditorSubComponentModeSnap
void DisplaySpecificSnapType(
const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay,
const AZ::Vector3& jointPosition,
const AZ::Vector3& snapDirection,
float snapLength) override;
void InitMouseDownCallBack() override;
};
} // namespace PhysX
@@ -1,91 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Component/TransformBus.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/Manipulators/ManipulatorView.h>
#include <Editor/EditorSubComponentModeVec3.h>
#include <PhysX/EditorJointBus.h>
#include <Source/Utils.h>
namespace PhysX
{
EditorSubComponentModeVec3::EditorSubComponentModeVec3(
const AZ::EntityComponentIdPair& entityComponentIdPair
, const AZ::Uuid& componentType
, const AZStd::string& name)
: EditorSubComponentModeBase(entityComponentIdPair, componentType, name)
, m_translationManipulators(AzToolsFramework::TranslationManipulators::Dimensions::Three,
AZ::Transform::Identity(), AZ::Vector3::CreateOne())
{
AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId());
AZ::Vector3 localTranslation;
EditorJointRequestBus::EventResult(
localTranslation, m_entityComponentId
, &EditorJointRequests::GetVector3Value
, m_name);
m_translationManipulators.SetSpace(worldTransform);
m_translationManipulators.SetLocalPosition(localTranslation);
m_translationManipulators.AddEntityComponentIdPair(m_entityComponentId);
m_translationManipulators.Register(AzToolsFramework::g_mainManipulatorManagerId);
AzToolsFramework::ConfigureTranslationManipulatorAppearance3d(&m_translationManipulators);
m_translationManipulators.InstallLinearManipulatorMouseMoveCallback([this](
const AzToolsFramework::LinearManipulator::Action& action)
{
OnManipulatorMoved(action.LocalPosition());
});
m_translationManipulators.InstallPlanarManipulatorMouseMoveCallback([this](
const AzToolsFramework::PlanarManipulator::Action& action)
{
OnManipulatorMoved(action.LocalPosition());
});
m_translationManipulators.InstallSurfaceManipulatorMouseMoveCallback([this](
const AzToolsFramework::SurfaceManipulator::Action& action)
{
OnManipulatorMoved(action.LocalPosition());
});
}
EditorSubComponentModeVec3::~EditorSubComponentModeVec3()
{
m_translationManipulators.Unregister();
}
void EditorSubComponentModeVec3::Refresh()
{
AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId());
AZ::Vector3 localTranslation;
EditorJointRequestBus::EventResult(
localTranslation, m_entityComponentId
, &EditorJointRequests::GetVector3Value
, m_name);
m_translationManipulators.SetSpace(worldTransform);
m_translationManipulators.SetLocalPosition(localTranslation);
m_translationManipulators.SetBoundsDirty();
}
void EditorSubComponentModeVec3::OnManipulatorMoved(const AZ::Vector3& position)
{
m_translationManipulators.SetLocalPosition(position);
EditorJointRequestBus::Event(
m_entityComponentId
, &EditorJointRequests::SetVector3Value
, m_name
, position);
}
} // namespace PhysX
@@ -1,34 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzToolsFramework/Manipulators/TranslationManipulators.h>
#include <Editor/EditorSubComponentModeBase.h>
namespace PhysX
{
class EditorSubComponentModeVec3
: public PhysX::EditorSubComponentModeBase
{
public:
EditorSubComponentModeVec3(
const AZ::EntityComponentIdPair& entityComponentIdPair
, const AZ::Uuid& componentType
, const AZStd::string& name);
~EditorSubComponentModeVec3();
// PhysX::EditorSubComponentModeBase
void Refresh() override;
private:
void OnManipulatorMoved(const AZ::Vector3& position);
AzToolsFramework::TranslationManipulators m_translationManipulators;
};
} // namespace PhysX
@@ -0,0 +1,601 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Editor/Source/ComponentModes/Joints/JointsComponentMode.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <Editor/EditorJointConfiguration.h>
#include <Editor/Source/ComponentModes/Joints/JointsSubComponentModeAngleCone.h>
#include <Editor/Source/ComponentModes/Joints/JointsSubComponentModeAnglePair.h>
#include <Editor/Source/ComponentModes/Joints/JointsSubComponentModeLinearFloat.h>
#include <Editor/Source/ComponentModes/Joints/JointsSubComponentModeRotation.h>
#include <Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnapPosition.h>
#include <Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnapRotation.h>
#include <Editor/Source/ComponentModes/Joints/JointsSubComponentModeTranslate.h>
namespace PhysX
{
AZ_CLASS_ALLOCATOR_IMPL(JointsComponentMode, AZ::SystemAllocator, 0);
namespace SubModeData
{
const AZ::Crc32 SwitchToTranslationSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.switchtotranslationsubmode");
static const char* TranslationTitle = "Switch to Position Mode";
static const char* TranslationToolTip = "Position Mode - Change the position of the joint.";
const AZ::Crc32 SwitchToRotationSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.switchtorotationsubmode");
static const char* RotationTitle = "Switch to Rotation Mode";
static const char* RotationToolTip = "Rotation Mode- Change the rotation of the joint.";
const AZ::Crc32 SwitchToMaxForceSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.switchtomaxforce");
static const char* MaxForceTitle = "Switch to Max Force Mode";
static const char* MaxForceToolTip = "Max Force Mode - Change the maximum force allowed before the joint breaks.";
const AZ::Crc32 SwitchToMaxTorqueSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.switchtomaxtorque");
static const char* MaxTorqueTitle = "Switch to Max Torque Mode";
static const char* MaxTorqueToolTip = "Max Torque Mode - Change the maximum torque allowed before the joint breaks.";
const AZ::Crc32 SwitchToDampingSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.switchtodamping");
static const char* DampingTitle = "Switch to Damping Mode";
static const char* DampingToolTip = "Damping Mode - Change the damping strength of the joint when beyond the limit.";
const AZ::Crc32 SwitchToStiffnessSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.switchtostiffness");
static const char* StiffnessTitle = "Switch to Stiffness Mode";
static const char* StiffnessToolTip = "Stiffness Mode - Change the stiffness strength of the joint when beyond the limit.";
const AZ::Crc32 SwitchToTwistLimitsSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.switchtotwistlimits");
static const char* TwistLimitsTitle = "Switch to Twist Limits Mode";
static const char* TwistLimitsToolTip = "Twist Limits Mode - Change the limits of the joint.";
const AZ::Crc32 SwitchToSwingLimitsSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.switchtoswinglimits");
static const char* SwingLimitsTitle = "Switch to Swing Limits Mode";
static const char* SwingLimitsToolTip = "Swing Limits Mode - Change the limits of the joint.";
const AZ::Crc32 SwitchToSnapPositionSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.switchtosnapposition");
static const char* SnapPositionTitle = "Switch to Snap Position Mode";
static const char* SnapPositionToolTip = "Snap Position Mode - Snap the position of the joint to another Entity.";
const AZ::Crc32 SwitchToSnapRotationSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.switchtosnaprotation");
static const char* SnapRotationTitle = "Switch to Snap Rotation Mode";
static const char* SnapRotationToolTip = "Snap Rotation Mode - Snap the rotation of the joint toward another Entity.";
const AZ::Crc32 ResetSubMode = AZ_CRC_CE("com.o3de.action.physx.joints.resetsubmode");
static const char* ResetTitle = "Reset Current Mode";
static const char* ResetToolTip = "Reset changes made during this mode edit.";
} // namespace ActionData
namespace Internal
{
static AzToolsFramework::ViewportUi::ButtonId RegisterClusterButton(
AzToolsFramework::ViewportUi::ClusterId clusterId, const char* iconName, const char* tooltip)
{
AzToolsFramework::ViewportUi::ButtonId buttonId;
AzToolsFramework::ViewportUi::ViewportUiRequestBus::EventResult(
buttonId, AzToolsFramework::ViewportUi::DefaultViewportId,
&AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::CreateClusterButton, clusterId,
AZStd::string::format(":/stylesheet/img/UI20/toolbar/%s.svg", iconName));
AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event(
AzToolsFramework::ViewportUi::DefaultViewportId,
&AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, clusterId, buttonId, tooltip);
return buttonId;
}
void RefreshUI()
{
// The reason this is in a free function is because ColliderComponentMode
// privately inherits from ToolsApplicationNotificationBus. Trying to invoke
// the bus inside the class scope causes the compiler to complain it's not accessible
// to due private inheritance.
// Using the global namespace operator :: should have fixed that, except there
// is a bug in the Microsoft compiler meaning it doesn't work. So this is a work around.
AzToolsFramework::ToolsApplicationNotificationBus::Broadcast(
&AzToolsFramework::ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, AzToolsFramework::Refresh_Values);
}
} // namespace Internal
JointsComponentMode::JointsComponentMode(const AZ::EntityComponentIdPair& entityComponentIdPair, AZ::Uuid componentType)
: AzToolsFramework::ComponentModeFramework::EditorBaseComponentMode(entityComponentIdPair, componentType)
{
m_modeSelectionClusterIds.assign(static_cast<int>(ClusterGroups::GroupCount), AzToolsFramework::ViewportUi::InvalidClusterId);
SetupSubModes(entityComponentIdPair);
EditorJointRequestBus::Event(
entityComponentIdPair, &EditorJointRequests::SetBoolValue, JointsComponentModeCommon::ParamaterNames::ComponentMode, true);
}
JointsComponentMode::~JointsComponentMode()
{
EditorJointRequestBus::Event(
GetEntityComponentIdPair(), &EditorJointRequests::SetBoolValue, JointsComponentModeCommon::ParamaterNames::ComponentMode,
false);
TeardownSubModes();
m_subModes[m_subMode]->Teardown(GetEntityComponentIdPair());
}
void JointsComponentMode::Refresh()
{
m_subModes[m_subMode]->Refresh(GetEntityComponentIdPair());
}
AZStd::vector<AzToolsFramework::ActionOverride> JointsComponentMode::PopulateActionsImpl()
{
const AZ::EntityComponentIdPair entityComponentIdPair = GetEntityComponentIdPair();
AZStd::vector<JointsComponentModeCommon::SubModeParamaterState> subModesState;
EditorJointRequestBus::EventResult(subModesState, entityComponentIdPair, &EditorJointRequests::GetSubComponentModesState);
auto makeActionOverride = [](const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Crc32& actionUri,
const QString& title, const QString& tip,const AZStd::function<void()>&& callback)
{
AzToolsFramework::ActionOverride actionOverride;
actionOverride.SetTitle(title);
actionOverride.SetTip(tip);
actionOverride.SetUri(actionUri);
actionOverride.SetEntityComponentIdPair(entityComponentIdPair);
actionOverride.SetCallback(callback);
return actionOverride;
};
AZStd::vector<AzToolsFramework::ActionOverride> actions;
// translation action
{
AzToolsFramework::ActionOverride translateAction = makeActionOverride(
entityComponentIdPair, SubModeData::SwitchToTranslationSubMode,
SubModeData::TranslationTitle, SubModeData::TranslationToolTip,
[this]()
{
SetCurrentMode(
JointsComponentModeCommon::SubComponentModes::ModeType::Translation,
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::Translation]);
});
translateAction.SetKeySequence(QKeySequence(Qt::Key_1));
actions.emplace_back(translateAction);
}
// rotation action
{
AzToolsFramework::ActionOverride rotationAction = makeActionOverride(
entityComponentIdPair, SubModeData::SwitchToRotationSubMode,
SubModeData::RotationTitle, SubModeData::RotationToolTip,
[this]()
{
SetCurrentMode(
JointsComponentModeCommon::SubComponentModes::ModeType::Rotation,
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::Rotation]);
});
rotationAction.SetKeySequence(QKeySequence(Qt::Key_2));
actions.emplace_back(rotationAction);
}
//setup action for other enabled options
for (auto [modeType, parameterString] : subModesState)
{
switch (modeType)
{
case JointsComponentModeCommon::SubComponentModes::ModeType::MaxForce:
{
actions.emplace_back(makeActionOverride(
entityComponentIdPair, SubModeData::SwitchToMaxForceSubMode,
SubModeData::MaxForceTitle, SubModeData::MaxForceToolTip,
[this]()
{
SetCurrentMode(
JointsComponentModeCommon::SubComponentModes::ModeType::MaxForce,
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::MaxForce]);
}));
}
break;
case JointsComponentModeCommon::SubComponentModes::ModeType::MaxTorque:
{
actions.emplace_back(makeActionOverride(
entityComponentIdPair, SubModeData::SwitchToMaxTorqueSubMode,
SubModeData::MaxTorqueTitle, SubModeData::MaxTorqueToolTip,
[this]()
{
SetCurrentMode(
JointsComponentModeCommon::SubComponentModes::ModeType::MaxTorque,
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::MaxTorque]);
}));
}
break;
case JointsComponentModeCommon::SubComponentModes::ModeType::Damping:
{
actions.emplace_back(makeActionOverride(
entityComponentIdPair, SubModeData::SwitchToDampingSubMode,
SubModeData::DampingTitle, SubModeData::DampingToolTip,
[this]()
{
SetCurrentMode(
JointsComponentModeCommon::SubComponentModes::ModeType::Damping,
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::Damping]);
}));
}
break;
case JointsComponentModeCommon::SubComponentModes::ModeType::Stiffness:
{
actions.emplace_back(makeActionOverride(
entityComponentIdPair, SubModeData::SwitchToStiffnessSubMode,
SubModeData::StiffnessTitle, SubModeData::StiffnessToolTip,
[this]()
{
SetCurrentMode(
JointsComponentModeCommon::SubComponentModes::ModeType::Stiffness,
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::Stiffness]);
}));
}
break;
case JointsComponentModeCommon::SubComponentModes::ModeType::TwistLimits:
{
actions.emplace_back(makeActionOverride(
entityComponentIdPair, SubModeData::SwitchToTwistLimitsSubMode,
SubModeData::TwistLimitsTitle, SubModeData::TwistLimitsToolTip,
[this]()
{
SetCurrentMode(
JointsComponentModeCommon::SubComponentModes::ModeType::TwistLimits,
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::TwistLimits]);
}));
}
break;
case JointsComponentModeCommon::SubComponentModes::ModeType::SwingLimits:
{
actions.emplace_back(makeActionOverride(
entityComponentIdPair, SubModeData::SwitchToSwingLimitsSubMode,
SubModeData::SwingLimitsTitle, SubModeData::SwingLimitsToolTip,
[this]()
{
SetCurrentMode(
JointsComponentModeCommon::SubComponentModes::ModeType::SwingLimits,
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::SwingLimits]);
}));
}
break;
case JointsComponentModeCommon::SubComponentModes::ModeType::SnapPosition:
{
actions.emplace_back(makeActionOverride(
entityComponentIdPair, SubModeData::SwitchToSnapPositionSubMode,
SubModeData::SnapPositionTitle, SubModeData::SnapPositionToolTip,
[this]()
{
SetCurrentMode(
JointsComponentModeCommon::SubComponentModes::ModeType::SnapPosition,
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::SnapPosition]);
}));
}
break;
case JointsComponentModeCommon::SubComponentModes::ModeType::SnapRotation:
{
actions.emplace_back(makeActionOverride(
entityComponentIdPair, SubModeData::SwitchToSnapRotationSubMode,
SubModeData::SnapRotationTitle, SubModeData::SnapRotationToolTip,
[this]()
{
SetCurrentMode(
JointsComponentModeCommon::SubComponentModes::ModeType::SnapRotation,
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::SnapRotation]);
}));
}
break;
}
}
// reset values
{
AzToolsFramework::ActionOverride resetValuesAction = makeActionOverride(
entityComponentIdPair, SubModeData::ResetSubMode,
SubModeData::ResetTitle, SubModeData::ResetToolTip,
[this]()
{
ResetCurrentMode();
});
resetValuesAction.SetKeySequence(QKeySequence(Qt::Key_R));
actions.emplace_back(resetValuesAction);
}
return actions;
}
AZStd::vector<AzToolsFramework::ViewportUi::ClusterId> JointsComponentMode::PopulateViewportUiImpl()
{
return AZStd::vector<AzToolsFramework::ViewportUi::ClusterId>(m_modeSelectionClusterIds.begin(), m_modeSelectionClusterIds.end());
}
void JointsComponentMode::SetCurrentMode(JointsComponentModeCommon::SubComponentModes::ModeType newMode, ButtonData& buttonData)
{
if (auto subMode = m_subModes.find(newMode);
subMode != m_subModes.end())
{
const AZ::EntityComponentIdPair entityComponentIdPair = GetEntityComponentIdPair();
m_subModes[m_subMode]->Teardown(entityComponentIdPair);
m_subMode = newMode;
subMode->second->Setup(entityComponentIdPair);
// if this button is on a different cluster. clear the active state.
if (m_activeButton.m_clusterId != buttonData.m_clusterId)
{
AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event(
AzToolsFramework::ViewportUi::DefaultViewportId,
&AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::ClearClusterActiveButton, m_activeButton.m_clusterId);
}
AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event(
AzToolsFramework::ViewportUi::DefaultViewportId,
&AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterActiveButton, buttonData.m_clusterId,
buttonData.m_buttonId);
m_activeButton = buttonData;
}
else
{
AZ_Assert(false, "PhysXJoints Uninitialized joint component mode selected.");
}
}
bool JointsComponentMode::HandleMouseInteraction(const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteraction)
{
// Propagate mouse interaction to sub-component mode.
if (m_subModes[m_subMode])
{
m_subModes[m_subMode]->HandleMouseInteraction(mouseInteraction);
}
return false;
}
void JointsComponentMode::SetupSubModes(const AZ::EntityComponentIdPair& entityComponentIdPair)
{
//create the 3 cluster groups
for (auto& clusterId : m_modeSelectionClusterIds)
{
AzToolsFramework::ViewportUi::ViewportUiRequestBus::EventResult(
clusterId, AzToolsFramework::ViewportUi::DefaultViewportId,
&AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::CreateCluster,
AzToolsFramework::ViewportUi::Alignment::TopLeft);
}
//retrieve the enabled sub components from the entity
AZStd::vector<JointsComponentModeCommon::SubModeParamaterState> subModesState;
EditorJointRequestBus::EventResult(subModesState, entityComponentIdPair, &EditorJointRequests::GetSubComponentModesState);
const AzToolsFramework::ViewportUi::ClusterId group1ClusterId = GetClusterId(ClusterGroups::Group1);
const AzToolsFramework::ViewportUi::ClusterId group2ClusterId = GetClusterId(ClusterGroups::Group2);
//hide cluster 2, if something is added to it. it will make is visible
AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event(
AzToolsFramework::ViewportUi::DefaultViewportId, &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterVisible,
group2ClusterId, false);
const AzToolsFramework::ViewportUi::ClusterId group3ClusterId = GetClusterId(ClusterGroups::Group3);
// hide cluster 3, if something is added to it. it will make is visible
AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event(
AzToolsFramework::ViewportUi::DefaultViewportId, &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterVisible,
group3ClusterId, false);
//translation and rotation are enabled for all joints in group 1
m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::Translation] =
AZStd::make_unique<JointsSubComponentModeTranslation>();
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::Translation] =
ButtonData{ group1ClusterId, Internal::RegisterClusterButton(group1ClusterId, "Move", SubModeData::TranslationToolTip) };
m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::Rotation] = AZStd::make_unique<JointsSubComponentModeRotation>();
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::Rotation] =
ButtonData{ group1ClusterId, Internal::RegisterClusterButton(group1ClusterId, "Rotate", SubModeData::RotationTitle) };
//some constants for some modes
const float ExponentBreakage = 1.0f;
const float ExponentSpring = 2.0f;
//setup the remaining modes if they're in the enabled list.
for (auto [modeType, parameterString] : subModesState)
{
switch (modeType)
{
case JointsComponentModeCommon::SubComponentModes::ModeType::MaxForce:
{
m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::MaxForce] =
AZStd::make_unique<JointsSubComponentModeLinearFloat>(
parameterString, ExponentBreakage, EditorJointConfig::s_breakageMax, EditorJointConfig::s_breakageMin);
const AzToolsFramework::ViewportUi::ButtonId buttonId =
Internal::RegisterClusterButton(group3ClusterId, "joints/MaxForce", SubModeData::MaxForceToolTip);
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::MaxForce] =
ButtonData{ group3ClusterId, buttonId };
AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event(
AzToolsFramework::ViewportUi::DefaultViewportId,
&AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterVisible, group3ClusterId, true);
}
break;
case JointsComponentModeCommon::SubComponentModes::ModeType::MaxTorque:
{
m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::MaxTorque] =
AZStd::make_unique<JointsSubComponentModeLinearFloat>(
parameterString, ExponentBreakage, EditorJointConfig::s_breakageMax, EditorJointConfig::s_breakageMin);
const AzToolsFramework::ViewportUi::ButtonId buttonId =
Internal::RegisterClusterButton(group3ClusterId, "joints/MaxTorque", SubModeData::MaxTorqueToolTip);
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::MaxTorque] =
ButtonData{ group3ClusterId, buttonId };
AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event(
AzToolsFramework::ViewportUi::DefaultViewportId,
&AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterVisible, group3ClusterId, true);
}
break;
case JointsComponentModeCommon::SubComponentModes::ModeType::Damping:
{
m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::Damping] =
AZStd::make_unique<JointsSubComponentModeLinearFloat>(
parameterString, ExponentSpring, EditorJointLimitBase::s_springMax, EditorJointLimitBase::s_springMin);
const AzToolsFramework::ViewportUi::ButtonId buttonId =
Internal::RegisterClusterButton(group2ClusterId, "joints/Damping", SubModeData::DampingToolTip);
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::Damping] = ButtonData{ group2ClusterId, buttonId };
AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event(
AzToolsFramework::ViewportUi::DefaultViewportId,
&AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterVisible, group2ClusterId, true);
}
break;
case JointsComponentModeCommon::SubComponentModes::ModeType::Stiffness:
{
m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::Stiffness] =
AZStd::make_unique<JointsSubComponentModeLinearFloat>(
parameterString, ExponentSpring, EditorJointLimitBase::s_springMax, EditorJointLimitBase::s_springMin);
const AzToolsFramework::ViewportUi::ButtonId buttonId =
Internal::RegisterClusterButton(group2ClusterId, "joints/Stiffness", SubModeData::StiffnessToolTip);
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::Stiffness] =
ButtonData{ group2ClusterId, buttonId };
AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event(
AzToolsFramework::ViewportUi::DefaultViewportId,
&AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterVisible, group2ClusterId, true);
}
break;
case JointsComponentModeCommon::SubComponentModes::ModeType::TwistLimits:
{
m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::TwistLimits] =
AZStd::make_unique<JointsSubComponentModeAnglePair>(
parameterString,
AZ::Vector3::CreateAxisX(), // PhysX revolute joints uses the x-axis by default
EditorJointLimitPairConfig::s_angleMax, EditorJointLimitPairConfig::s_angleMin);
const AzToolsFramework::ViewportUi::ButtonId buttonId =
Internal::RegisterClusterButton(group2ClusterId, "joints/TwistLimits", SubModeData::TwistLimitsToolTip);
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::TwistLimits] =
ButtonData{ group2ClusterId, buttonId };
AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event(
AzToolsFramework::ViewportUi::DefaultViewportId,
&AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterVisible, group2ClusterId, true);
}
break;
case JointsComponentModeCommon::SubComponentModes::ModeType::SwingLimits:
{
m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::SwingLimits] =
AZStd::make_unique<JointsSubComponentModeAngleCone>(
parameterString, EditorJointLimitPairConfig::s_angleMax, EditorJointLimitPairConfig::s_angleMin);
const AzToolsFramework::ViewportUi::ButtonId buttonId =
Internal::RegisterClusterButton(group2ClusterId, "joints/SwingLimits", SubModeData::SwingLimitsToolTip);
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::SwingLimits] =
ButtonData{ group2ClusterId, buttonId };
AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event(
AzToolsFramework::ViewportUi::DefaultViewportId,
&AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::SetClusterVisible, group2ClusterId, true);
}
break;
case JointsComponentModeCommon::SubComponentModes::ModeType::SnapPosition:
{
m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::SnapPosition] =
AZStd::make_unique<JointsSubComponentModeSnapPosition>();
const AzToolsFramework::ViewportUi::ButtonId buttonId =
Internal::RegisterClusterButton(group1ClusterId, "joints/SnapPosition", SubModeData::SnapPositionToolTip);
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::SnapPosition] =
ButtonData{ group1ClusterId, buttonId };
}
break;
case JointsComponentModeCommon::SubComponentModes::ModeType::SnapRotation:
{
m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::SnapRotation] =
AZStd::make_unique<JointsSubComponentModeSnapRotation>();
const AzToolsFramework::ViewportUi::ButtonId buttonId =
Internal::RegisterClusterButton(group1ClusterId, "joints/SnapRotation", SubModeData::SnapRotationToolTip);
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::SnapRotation] =
ButtonData{ group1ClusterId, buttonId };
}
break;
}
}
//register click handler for the buttons
m_modeSelectionHandlers.push_back(AZ::Event<AzToolsFramework::ViewportUi::ButtonId>::Handler(
[this](AzToolsFramework::ViewportUi::ButtonId buttonId)
{
for (auto itr : m_buttonData)
{
if (itr.second.m_clusterId == GetClusterId(ClusterGroups::Group1) && itr.second.m_buttonId == buttonId)
{
SetCurrentMode(itr.first, itr.second);
break;
}
}
}));
m_modeSelectionHandlers.push_back(AZ::Event<AzToolsFramework::ViewportUi::ButtonId>::Handler(
[this](AzToolsFramework::ViewportUi::ButtonId buttonId)
{
for (auto itr : m_buttonData)
{
if (itr.second.m_clusterId == GetClusterId(ClusterGroups::Group2) && itr.second.m_buttonId == buttonId)
{
SetCurrentMode(itr.first, itr.second);
break;
}
}
}));
m_modeSelectionHandlers.push_back(AZ::Event<AzToolsFramework::ViewportUi::ButtonId>::Handler(
[this](AzToolsFramework::ViewportUi::ButtonId buttonId)
{
for (auto itr : m_buttonData)
{
if (itr.second.m_clusterId == GetClusterId(ClusterGroups::Group3) && itr.second.m_buttonId == buttonId)
{
SetCurrentMode(itr.first, itr.second);
break;
}
}
}));
for (int i = 0; i < static_cast<int>(ClusterGroups::GroupCount); i++)
{
AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event(
AzToolsFramework::ViewportUi::DefaultViewportId,
&AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::RegisterClusterEventHandler, m_modeSelectionClusterIds[i],
m_modeSelectionHandlers[i]);
}
// set the translate as enabled by default.
SetCurrentMode(
JointsComponentModeCommon::SubComponentModes::ModeType::Translation,
m_buttonData[JointsComponentModeCommon::SubComponentModes::ModeType::Translation]);
m_subModes[JointsComponentModeCommon::SubComponentModes::ModeType::Translation]->Setup(GetEntityComponentIdPair());
m_subMode = JointsComponentModeCommon::SubComponentModes::ModeType::Translation;
}
void JointsComponentMode::ResetCurrentMode()
{
const AZ::EntityComponentIdPair entityComponentIdPair = GetEntityComponentIdPair();
m_subModes[m_subMode]->ResetValues(entityComponentIdPair);
m_subModes[m_subMode]->Refresh(entityComponentIdPair);
Internal::RefreshUI();
}
void JointsComponentMode::TeardownSubModes()
{
for (auto clusterid : m_modeSelectionClusterIds)
{
AzToolsFramework::ViewportUi::ViewportUiRequestBus::Event(
AzToolsFramework::ViewportUi::DefaultViewportId, &AzToolsFramework::ViewportUi::ViewportUiRequestBus::Events::RemoveCluster,
clusterid);
}
}
AzToolsFramework::ViewportUi::ClusterId JointsComponentMode::GetClusterId(ClusterGroups group)
{
return m_modeSelectionClusterIds[static_cast<int>(group)];
}
} // namespace PhysX
@@ -0,0 +1,79 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/EBus/Event.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzToolsFramework/ComponentMode/EditorBaseComponentMode.h>
#include <AzToolsFramework/ViewportUi/ViewportUiRequestBus.h>
#include <Editor/Source/ComponentModes/Joints/JointsComponentModeCommon.h>
#include <Editor/Source/ComponentModes/PhysXSubComponentModeBase.h>
namespace AZ
{
class EntityComponentIdPair;
}
namespace PhysX
{
//! Class responsible for managing component mode for joints.
class JointsComponentMode final
: public AzToolsFramework::ComponentModeFramework::EditorBaseComponentMode
{
public:
AZ_CLASS_ALLOCATOR_DECL;
JointsComponentMode(const AZ::EntityComponentIdPair& entityComponentIdPair, AZ::Uuid componentType);
~JointsComponentMode();
// EditorBaseComponentMode ...
void Refresh() override;
AZStd::vector<AzToolsFramework::ActionOverride> PopulateActionsImpl() override;
AZStd::vector<AzToolsFramework::ViewportUi::ClusterId> PopulateViewportUiImpl() override;
private:
//! Used to identify the group of component modes.
enum class ClusterGroups
{
Group1 = 0, //!< Position Joint, Rotate Joint, Snap Position, Snap Rotation.
Group2, //!< Damping, Stiffness, Twist Limits, Swing Limits.
Group3, //!< Max Force, Max Torque.
GroupCount
};
//! Used to track the cluster that a specific button is apart of.
struct ButtonData
{
AzToolsFramework::ViewportUi::ClusterId m_clusterId;
AzToolsFramework::ViewportUi::ButtonId m_buttonId;
};
void SetCurrentMode(JointsComponentModeCommon::SubComponentModes::ModeType newMode, ButtonData& buttonData);
bool HandleMouseInteraction(const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteraction) override;
void SetupSubModes(const AZ::EntityComponentIdPair& entityComponentIdPair);
void ResetCurrentMode();
void TeardownSubModes();
AzToolsFramework::ViewportUi::ClusterId GetClusterId(ClusterGroups group);
AZStd::fixed_vector<AzToolsFramework::ViewportUi::ClusterId, static_cast<int>(ClusterGroups::GroupCount)> m_modeSelectionClusterIds; //!< List of the cluster ui's. The sub modes are split across 3 groups and each group is it's own cluster ui.
AZStd::unordered_map<JointsComponentModeCommon::SubComponentModes::ModeType, ButtonData> m_buttonData; //!< Mapping of joint component modes to the button data.
AZStd::fixed_vector<AZ::Event<AzToolsFramework::ViewportUi::ButtonId>::Handler, static_cast<int>(ClusterGroups::GroupCount)>
m_modeSelectionHandlers; //!< Input handlers for each cluster UI.
ButtonData m_activeButton; //!< The current highlighted button data.
JointsComponentModeCommon::SubComponentModes::ModeType m_subMode = JointsComponentModeCommon::SubComponentModes::ModeType::Translation; //!< The current component mode that is active.
AZStd::unordered_map<JointsComponentModeCommon::SubComponentModes::ModeType, AZStd::unique_ptr<PhysXSubComponentModeBase>> m_subModes; //!< The logic handlers for each component mode.
};
}
@@ -0,0 +1,27 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Editor/Source/ComponentModes/Joints/JointsComponentModeCommon.h>
namespace PhysX::JointsComponentModeCommon
{
const AZStd::string_view ParamaterNames::TwistLimits = "Twist Limits";
const AZStd::string_view ParamaterNames::Damping = "Damping";
const AZStd::string_view ParamaterNames::MaxForce = "Maximum Force";
const AZStd::string_view ParamaterNames::MaxTorque = "Maximum Torque";
const AZStd::string_view ParamaterNames::Position = "Position";
const AZStd::string_view ParamaterNames::Rotation = "Rotation";
const AZStd::string_view ParamaterNames::SnapPosition = "Snap Position";
const AZStd::string_view ParamaterNames::SnapRotation = "Snap Rotation";
const AZStd::string_view ParamaterNames::Stiffness = "Stiffness";
const AZStd::string_view ParamaterNames::SwingLimit = "Swing Limits";
const AZStd::string_view ParamaterNames::Transform = "Transform";
const AZStd::string_view ParamaterNames::ComponentMode = "Component Mode";
const AZStd::string_view ParamaterNames::LeadEntity = "Lead Entity";
} // namespace PhysX::JointsComponentModeCommon
@@ -0,0 +1,70 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Math/Quaternion.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/std/string/string.h>
#include <Editor/EditorJointCommon.h>
namespace PhysX::JointsComponentModeCommon
{
namespace SubComponentModes
{
//! Used to identify a specific sub component mode used with joints
enum class ModeType : AZ::u32
{
Translation = 0,
Rotation,
MaxForce,
MaxTorque,
Damping,
Stiffness,
TwistLimits,
SwingLimits,
SnapPosition,
SnapRotation,
ModeCount
};
//! Shared data structure used with Angle Cone and Angle Pair sub component modes.
//! Holds information about the axis and limits of the angle.
struct AngleModesSharedRotationState
{
AZ::Vector3 m_axis; //!< The Axis of rotation to apply the limits.
AZ::Quaternion m_savedOrientation = AZ::Quaternion::CreateIdentity(); //!< The angle delta of the last mouse action.
PhysX::AngleLimitsFloatPair m_valuePair; //!< the current limits of the angle.
};
} // namespace SubComponentModes
//! Name Identifiers for the joint components sub modes.
struct ParamaterNames
{
static const AZStd::string_view TwistLimits;
static const AZStd::string_view Damping;
static const AZStd::string_view MaxForce;
static const AZStd::string_view MaxTorque;
static const AZStd::string_view Position;
static const AZStd::string_view Rotation;
static const AZStd::string_view SnapPosition;
static const AZStd::string_view SnapRotation;
static const AZStd::string_view Stiffness;
static const AZStd::string_view SwingLimit;
static const AZStd::string_view Transform;
static const AZStd::string_view ComponentMode;
static const AZStd::string_view LeadEntity;
};
//! A pairing of Sub component Names, and Id.
struct SubModeParamaterState
{
SubComponentModes::ModeType m_modeType = SubComponentModes::ModeType::ModeCount; //!< The Id of the sub component mode.
AZStd::string m_parameterName; //!< The name of the sub component mode.
};
} // namespace PhysX::JointsComponentModeCommon
@@ -0,0 +1,432 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Editor/Source/ComponentModes/Joints/JointsSubComponentModeAngleCone.h>
#include <AzCore/Component/ComponentBus.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzToolsFramework/Manipulators/AngularManipulator.h>
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/Manipulators/ManipulatorView.h>
#include <AzToolsFramework/Manipulators/PlanarManipulator.h>
#include <Editor/Source/ComponentModes/Joints/JointsComponentModeCommon.h>
#include <PhysX/EditorJointBus.h>
#include <Source/Utils.h>
namespace PhysX
{
AZ_CLASS_ALLOCATOR_IMPL(JointsSubComponentModeAngleCone, AZ::SystemAllocator, 0);
namespace Internal
{
const float ArrowLength = 2.0f;
const float ConeHeight = 3.0f;
const float XRotationManipulatorRadius = 2.0f;
const float XRotationManipulatorWidth = 0.05f;
} // namespace Internal
JointsSubComponentModeAngleCone::JointsSubComponentModeAngleCone(
const AZStd::string& propertyName, float max, float min)
: m_propertyName(propertyName)
, m_max(max)
, m_min(min)
{
}
void JointsSubComponentModeAngleCone::Setup(const AZ::EntityComponentIdPair& idPair)
{
m_entityComponentIdPair = idPair;
EditorJointRequestBus::EventResult(
m_resetPostion, m_entityComponentIdPair, &PhysX::EditorJointRequests::GetVector3Value, JointsComponentModeCommon::ParamaterNames::Position);
EditorJointRequestBus::EventResult(
m_resetRotation, m_entityComponentIdPair, &PhysX::EditorJointRequests::GetVector3Value, JointsComponentModeCommon::ParamaterNames::Rotation);
EditorJointRequestBus::EventResult(
m_resetLimits, m_entityComponentIdPair, &EditorJointRequests::GetLinearValuePair, m_propertyName);
AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentIdPair.GetEntityId());
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentIdPair, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform);
const AZ::Quaternion localRotation = localTransform.GetRotation();
// Initialize manipulators used to resize the base of the cone.
m_yLinearManipulator = AzToolsFramework::LinearManipulator::MakeShared(worldTransform);
m_yLinearManipulator->AddEntityComponentIdPair(m_entityComponentIdPair);
m_yLinearManipulator->SetAxis(AZ::Vector3::CreateAxisZ());
m_zLinearManipulator = AzToolsFramework::LinearManipulator::MakeShared(worldTransform);
m_zLinearManipulator->AddEntityComponentIdPair(m_entityComponentIdPair);
m_zLinearManipulator->SetAxis(AZ::Vector3::CreateAxisY());
m_yzPlanarManipulator = AzToolsFramework::PlanarManipulator::MakeShared(worldTransform);
m_yzPlanarManipulator->AddEntityComponentIdPair(m_entityComponentIdPair);
m_yzPlanarManipulator->SetAxes(AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ());
ConfigureLinearView(
Internal::ArrowLength, AZ::Color(1.0f, 0.0f, 0.0f, 1.0f), AZ::Color(0.0f, 1.0f, 0.0f, 1.0f), AZ::Color(0.0f, 0.0f, 1.0f, 1.0f));
ConfigurePlanarView(AZ::Color(0.0f, 1.0f, 0.0f, 1.0f), AZ::Color(0.0f, 0.0f, 1.0f, 1.0f));
// Position and orientate manipulators
AZ::Transform displacementTransform = localTransform;
AZ::Vector3 displacementTranslate = localRotation.TransformVector(AZ::Vector3(Internal::ConeHeight, 0.0f, 0.0f));
displacementTransform.SetTranslation(localTransform.GetTranslation() + displacementTranslate);
m_yLinearManipulator->SetLocalTransform(displacementTransform);
m_zLinearManipulator->SetLocalTransform(displacementTransform);
m_yzPlanarManipulator->SetLocalTransform(displacementTransform);
// Initialize rotation manipulator for rotating cone
m_xRotationManipulator = AzToolsFramework::AngularManipulator::MakeShared(worldTransform);
m_xRotationManipulator->AddEntityComponentIdPair(m_entityComponentIdPair);
m_xRotationManipulator->SetAxis(AZ::Vector3::CreateAxisX());
m_xRotationManipulator->SetLocalTransform(localTransform);
const AZ::Color xRotationManipulatorColor = AZ::Color(1.0f, 0.0f, 0.0f, 1.0f);
m_xRotationManipulator->SetView(AzToolsFramework::CreateManipulatorViewCircle(
*m_xRotationManipulator, xRotationManipulatorColor, Internal::XRotationManipulatorRadius, Internal::XRotationManipulatorWidth,
AzToolsFramework::DrawHalfDottedCircle));
AZStd::shared_ptr<JointsComponentModeCommon::SubComponentModes::AngleModesSharedRotationState> sharedRotationState =
AZStd::make_shared<JointsComponentModeCommon::SubComponentModes::AngleModesSharedRotationState>();
struct SharedState
{
AngleLimitsFloatPair m_startValues;
};
auto sharedState = AZStd::make_shared<SharedState>();
m_yLinearManipulator->InstallLeftMouseDownCallback(
[this, sharedState](const AzToolsFramework::LinearManipulator::Action& /*action*/) mutable
{
AngleLimitsFloatPair currentValue;
EditorJointRequestBus::EventResult(
currentValue, m_entityComponentIdPair, &EditorJointRequests::GetLinearValuePair, m_propertyName);
sharedState->m_startValues = currentValue;
});
m_yLinearManipulator->InstallMouseMoveCallback(
[this, sharedState](const AzToolsFramework::LinearManipulator::Action& action)
{
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentIdPair, &EditorJointRequests::GetTransformValue,
JointsComponentModeCommon::ParamaterNames::Transform);
const AZ::Quaternion localRotation = localTransform.GetRotation();
const float axisDisplacement = action.LocalPositionOffset().Dot(localRotation.TransformVector(action.m_fixed.m_axis));
const float originalBaseY = tan(AZ::DegToRad(sharedState->m_startValues.first)) * Internal::ConeHeight;
const float newBaseY = originalBaseY + axisDisplacement;
const float newAngle = AZ::GetClamp(AZ::RadToDeg(atan(newBaseY / Internal::ConeHeight)), m_min, m_max);
EditorJointRequestBus::Event(
m_entityComponentIdPair, &EditorJointRequests::SetLinearValuePair, m_propertyName,
AngleLimitsFloatPair(newAngle, sharedState->m_startValues.second));
m_yLinearManipulator->SetBoundsDirty();
});
m_zLinearManipulator->InstallLeftMouseDownCallback(
[this, sharedState](const AzToolsFramework::LinearManipulator::Action& /*action*/) mutable
{
AngleLimitsFloatPair currentValue;
EditorJointRequestBus::EventResult(
currentValue, m_entityComponentIdPair, &EditorJointRequests::GetLinearValuePair, m_propertyName);
sharedState->m_startValues = currentValue;
});
m_zLinearManipulator->InstallMouseMoveCallback(
[this, sharedState](const AzToolsFramework::LinearManipulator::Action& action)
{
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentIdPair, &EditorJointRequests::GetTransformValue,
JointsComponentModeCommon::ParamaterNames::Transform);
const AZ::Quaternion localRotation = localTransform.GetRotation();
const float axisDisplacement = action.LocalPositionOffset().Dot(localRotation.TransformVector(action.m_fixed.m_axis));
const float originalBaseZ = tan(AZ::DegToRad(sharedState->m_startValues.second)) * Internal::ConeHeight;
const float newBaseZ = originalBaseZ + axisDisplacement;
const float newAngle = AZ::GetClamp(AZ::RadToDeg(atan(newBaseZ / Internal::ConeHeight)), m_min, m_max);
EditorJointRequestBus::Event(
m_entityComponentIdPair, &EditorJointRequests::SetLinearValuePair, m_propertyName,
AngleLimitsFloatPair(sharedState->m_startValues.first, newAngle));
m_zLinearManipulator->SetBoundsDirty();
});
m_yzPlanarManipulator->InstallLeftMouseDownCallback(
[this, sharedState]([[maybe_unused]]const AzToolsFramework::PlanarManipulator::Action& action) mutable
{
AngleLimitsFloatPair currentValue;
EditorJointRequestBus::EventResult(
currentValue, m_entityComponentIdPair, &EditorJointRequests::GetLinearValuePair, m_propertyName);
sharedState->m_startValues = currentValue;
});
m_yzPlanarManipulator->InstallMouseMoveCallback(
[this, sharedState](const AzToolsFramework::PlanarManipulator::Action& action)
{
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentIdPair, &EditorJointRequests::GetTransformValue,
JointsComponentModeCommon::ParamaterNames::Transform);
const AZ::Quaternion localRotation = localTransform.GetRotation();
const float axisDisplacementY = action.LocalPositionOffset().Dot(localRotation.TransformVector(AZ::Vector3::CreateAxisY()));
const float axisDisplacementZ = action.LocalPositionOffset().Dot(localRotation.TransformVector(AZ::Vector3::CreateAxisZ()));
const float axisDisplacement = axisDisplacementZ > axisDisplacementY ? axisDisplacementZ : axisDisplacementY;
const float originalBaseY = tan(AZ::DegToRad(sharedState->m_startValues.first)) * Internal::ConeHeight;
const float newBaseY = originalBaseY + axisDisplacement;
const float newAngleY = AZ::GetClamp(AZ::RadToDeg(atan(newBaseY / Internal::ConeHeight)), m_min, m_max);
const float originalBaseZ = tan(AZ::DegToRad(sharedState->m_startValues.second)) * Internal::ConeHeight;
const float newBaseZ = originalBaseZ + axisDisplacement;
const float newAngleZ = AZ::GetClamp(AZ::RadToDeg(atan(newBaseZ / Internal::ConeHeight)), m_min, m_max);
EditorJointRequestBus::Event(
m_entityComponentIdPair, &EditorJointRequests::SetLinearValuePair, m_propertyName,
AngleLimitsFloatPair(newAngleY, newAngleZ));
m_yzPlanarManipulator->SetBoundsDirty();
});
struct SharedStateXRotate
{
AZ::Transform m_startTM;
};
auto sharedStateXRotate = AZStd::make_shared<SharedStateXRotate>();
auto mouseDownCallback = [this, sharedRotationState](const AzToolsFramework::AngularManipulator::Action& action) mutable -> void
{
AZ::Quaternion normalizedStart = action.m_start.m_rotation.GetNormalized();
sharedRotationState->m_axis = AZ::Vector3(normalizedStart.GetX(), normalizedStart.GetY(), normalizedStart.GetZ());
sharedRotationState->m_savedOrientation = AZ::Quaternion::CreateIdentity();
AngleLimitsFloatPair currentValue;
EditorJointRequestBus::EventResult(
currentValue, m_entityComponentIdPair, &EditorJointRequests::GetLinearValuePair, m_propertyName);
sharedRotationState->m_valuePair = currentValue;
};
auto mouseDownRotateXCallback =
[this, sharedStateXRotate]([[maybe_unused]] const AzToolsFramework::AngularManipulator::Action& action) mutable -> void
{
PhysX::EditorJointRequestBus::EventResult(
sharedStateXRotate->m_startTM, m_entityComponentIdPair, &PhysX::EditorJointRequests::GetTransformValue,
JointsComponentModeCommon::ParamaterNames::Transform);
};
m_xRotationManipulator->InstallLeftMouseDownCallback(mouseDownRotateXCallback);
m_xRotationManipulator->InstallMouseMoveCallback(
[this, sharedStateXRotate](const AzToolsFramework::AngularManipulator::Action& action) mutable -> void
{
const AZ::Quaternion manipulatorOrientation = action.m_start.m_rotation * action.m_current.m_delta;
AZ::Transform newTransform = AZ::Transform::CreateIdentity();
newTransform = sharedStateXRotate->m_startTM * AZ::Transform::CreateFromQuaternion(action.m_current.m_delta);
PhysX::EditorJointRequestBus::Event(
m_entityComponentIdPair, &PhysX::EditorJointRequests::SetVector3Value, JointsComponentModeCommon::ParamaterNames::Position,
newTransform.GetTranslation());
PhysX::EditorJointRequestBus::Event(
m_entityComponentIdPair, &PhysX::EditorJointRequests::SetVector3Value, JointsComponentModeCommon::ParamaterNames::Rotation,
newTransform.GetRotation().GetEulerDegrees());
m_yLinearManipulator->SetLocalOrientation(manipulatorOrientation);
m_zLinearManipulator->SetLocalOrientation(manipulatorOrientation);
m_yLinearManipulator->SetAxis(action.m_current.m_delta.TransformVector(AZ::Vector3::CreateAxisY()));
m_zLinearManipulator->SetAxis(action.m_current.m_delta.TransformVector(AZ::Vector3::CreateAxisZ()));
m_xRotationManipulator->SetLocalOrientation(manipulatorOrientation);
m_yLinearManipulator->SetBoundsDirty();
m_zLinearManipulator->SetBoundsDirty();
m_xRotationManipulator->SetBoundsDirty();
});
m_xRotationManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
m_yLinearManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
m_zLinearManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
m_yzPlanarManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(m_entityComponentIdPair.GetEntityId());
Refresh(m_entityComponentIdPair);
}
void JointsSubComponentModeAngleCone::Refresh(const AZ::EntityComponentIdPair& idPair)
{
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, idPair, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform);
float coneHeight = Internal::ConeHeight;
AngleLimitsFloatPair yzSwingAngleLimits;
EditorJointRequestBus::EventResult(yzSwingAngleLimits, idPair, &EditorJointRequests::GetLinearValuePair, m_propertyName);
// Draw inverted cone (negative cone height) if angles are larger than 90 deg.
if (yzSwingAngleLimits.first > 90.0f || yzSwingAngleLimits.second > 90.0f)
{
coneHeight = -Internal::ConeHeight;
}
// reposition manipulators
const AZ::Quaternion localRotation = localTransform.GetRotation();
const AZ::Vector3 linearManipulatorOffset =
localTransform.GetTranslation() + localRotation.TransformVector(AZ::Vector3(coneHeight, 0.0f, 0.0f));
m_xRotationManipulator->SetLocalTransform(localTransform);
localTransform.SetTranslation(linearManipulatorOffset);
m_yLinearManipulator->SetLocalTransform(localTransform);
m_zLinearManipulator->SetLocalTransform(localTransform);
m_yzPlanarManipulator->SetLocalTransform(localTransform);
}
void JointsSubComponentModeAngleCone::Teardown(const AZ::EntityComponentIdPair& idPair)
{
AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
m_xRotationManipulator->RemoveEntityComponentIdPair(idPair);
m_xRotationManipulator->Unregister();
m_yLinearManipulator->RemoveEntityComponentIdPair(idPair);
m_yLinearManipulator->Unregister();
m_zLinearManipulator->RemoveEntityComponentIdPair(idPair);
m_zLinearManipulator->Unregister();
m_yzPlanarManipulator->RemoveEntityComponentIdPair(idPair);
m_yzPlanarManipulator->Unregister();
}
void JointsSubComponentModeAngleCone::ResetValues(const AZ::EntityComponentIdPair& idPair)
{
EditorJointRequestBus::Event(
idPair, &PhysX::EditorJointRequests::SetVector3Value, JointsComponentModeCommon::ParamaterNames::Position, m_resetPostion);
EditorJointRequestBus::Event(
idPair, &PhysX::EditorJointRequests::SetVector3Value, JointsComponentModeCommon::ParamaterNames::Rotation, m_resetRotation);
EditorJointRequestBus::Event(idPair, &EditorJointRequests::SetLinearValuePair, m_propertyName, m_resetLimits);
}
void JointsSubComponentModeAngleCone::ConfigureLinearView(
float axisLength, [[maybe_unused]] const AZ::Color& axis1Color, const AZ::Color& axis2Color, const AZ::Color& axis3Color)
{
const float coneLength = 0.28f;
const float coneRadius = 0.07f;
const auto configureLinearView =
[coneLength, axisLength, coneRadius](AzToolsFramework::LinearManipulator* linearManipulator, const AZ::Color& color)
{
AzToolsFramework::ManipulatorViews views;
views.emplace_back(CreateManipulatorViewLine(
*linearManipulator, color, axisLength,
AzToolsFramework::ManipulatorLineBoundWidth(AzFramework::InvalidViewportId)));
views.emplace_back(CreateManipulatorViewCone(
*linearManipulator, color, linearManipulator->GetAxis() * (axisLength - coneLength), coneLength, coneRadius));
linearManipulator->SetViews(AZStd::move(views));
};
configureLinearView(m_yLinearManipulator.get(), axis2Color);
configureLinearView(m_zLinearManipulator.get(), axis3Color);
}
void JointsSubComponentModeAngleCone::ConfigurePlanarView(const AZ::Color& planeColor, const AZ::Color& plane2Color)
{
const float planeSize = 0.6f;
AzToolsFramework::ManipulatorViews views;
views.emplace_back(CreateManipulatorViewQuad(*m_yzPlanarManipulator, planeColor, plane2Color, planeSize));
m_yzPlanarManipulator->SetViews(AZStd::move(views));
}
void JointsSubComponentModeAngleCone::DisplayEntityViewport(
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)
{
AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentIdPair.GetEntityId());
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentIdPair, &EditorJointRequests::GetTransformValue,
JointsComponentModeCommon::ParamaterNames::Transform);
AZ::u32 stateBefore = debugDisplay.GetState();
debugDisplay.CullOff();
debugDisplay.PushMatrix(worldTransform);
debugDisplay.PushMatrix(localTransform);
const float xAxisArrowLength = 2.0f;
debugDisplay.SetColor(AZ::Color(1.0f, 0.0f, 0.0f, 1.0f));
debugDisplay.DrawArrow(AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(xAxisArrowLength, 0.0f, 0.0f));
AngleLimitsFloatPair yzSwingAngleLimits;
EditorJointRequestBus::EventResult(
yzSwingAngleLimits, m_entityComponentIdPair, &EditorJointRequests::GetLinearValuePair, m_propertyName);
const AZ::u32 numEllipseSamples = 16;
AZ::Vector3 ellipseSamples[numEllipseSamples];
float coneHeight = Internal::ConeHeight;
// Draw inverted cone if angles are larger than 90 deg.
if (yzSwingAngleLimits.first > 90.0f || yzSwingAngleLimits.second > 90.0f)
{
coneHeight = -Internal::ConeHeight;
}
// Compute points along perimeter of cone base
const float coney = tanf(AZ::DegToRad(yzSwingAngleLimits.first)) * coneHeight;
const float conez = tanf(AZ::DegToRad(yzSwingAngleLimits.second)) * coneHeight;
const float step = AZ::Constants::TwoPi / numEllipseSamples;
for (size_t i = 0; i < numEllipseSamples; ++i)
{
const float angleStep = step * i;
ellipseSamples[i].SetX(coneHeight);
ellipseSamples[i].SetY(conez * sin(angleStep));
ellipseSamples[i].SetZ(coney * cos(angleStep));
}
// draw cone
for (size_t i = 0; i < numEllipseSamples; ++i)
{
size_t nextIndex = i + 1;
if (i == numEllipseSamples - 1)
{
nextIndex = 0;
}
// draw cone sides
debugDisplay.SetColor(AZ::Color(1.0f, 1.0f, 1.0f, 0.2f));
debugDisplay.DrawTri(AZ::Vector3(0.0f, 0.0f, 0.0f), ellipseSamples[i], ellipseSamples[nextIndex]);
// draw parameter of cone base
debugDisplay.SetColor(AZ::Color(0.4f, 0.4f, 0.4f, 0.4f));
debugDisplay.DrawLine(ellipseSamples[i], ellipseSamples[nextIndex]);
}
// draw axis lines at base of cone, and from tip to base.
debugDisplay.SetColor(AZ::Color(0.5f, 0.5f, 0.5f, 0.6f));
debugDisplay.DrawLine(ellipseSamples[0], ellipseSamples[numEllipseSamples / 2]);
debugDisplay.DrawLine(ellipseSamples[numEllipseSamples * 3 / 4], ellipseSamples[numEllipseSamples / 4]);
debugDisplay.DrawLine(AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(coneHeight, 0.0f, 0.0f));
debugDisplay.PopMatrix(); // pop local transform
debugDisplay.PopMatrix(); // pop world transform
debugDisplay.SetState(stateBefore);
// reposition and reorientate manipulators
Refresh(m_entityComponentIdPair);
}
} // namespace PhysX
@@ -0,0 +1,69 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Math/Vector3.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/std/limits.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <Editor/Source/ComponentModes/PhysXSubComponentModeBase.h>
#include <PhysX/EditorJointBus.h>
namespace AzToolsFramework
{
class AngularManipulator;
class LinearManipulator;
class PlanarManipulator;
} // namespace AzToolsFramework
namespace PhysX
{
class JointsSubComponentModeAngleCone final
: public PhysXSubComponentModeBase
, private AzFramework::EntityDebugDisplayEventBus::Handler
{
public:
AZ_CLASS_ALLOCATOR_DECL;
JointsSubComponentModeAngleCone(const AZStd::string& propertyName, float max, float min);
// PhysXSubComponentModeBase ...
void Setup(const AZ::EntityComponentIdPair& idPair) override;
void Refresh(const AZ::EntityComponentIdPair& idPair) override;
void Teardown(const AZ::EntityComponentIdPair& idPair) override;
void ResetValues(const AZ::EntityComponentIdPair& idPair) override;
private:
void ConfigureLinearView(
float axisLength,
const AZ::Color& axis1Color,
const AZ::Color& axis2Color,
const AZ::Color& axis3Color = AZ::Color(0.0f, 0.0f, 1.0f, 0.5f));
void ConfigurePlanarView(
const AZ::Color& planeColor = AZ::Color(0.0f, 1.0f, 0.0f, 0.5f),
const AZ::Color& plane2Color = AZ::Color(0.0f, 0.0f, 1.0f, 0.5f));
// AzFramework::EntityDebugDisplayEventBus
void DisplayEntityViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override;
float m_max = AZStd::numeric_limits<float>::max();
float m_min = -AZStd::numeric_limits<float>::max();
AZ::EntityComponentIdPair m_entityComponentIdPair;
AZ::Vector3 m_resetPostion;
AZ::Vector3 m_resetRotation;
AngleLimitsFloatPair m_resetLimits;
AZStd::string m_propertyName;
AZStd::shared_ptr<AzToolsFramework::AngularManipulator> m_xRotationManipulator;
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> m_yLinearManipulator;
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> m_zLinearManipulator;
AZStd::shared_ptr<AzToolsFramework::PlanarManipulator> m_yzPlanarManipulator;
};
}
@@ -0,0 +1,305 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Editor/Source/ComponentModes/Joints/JointsSubComponentModeAnglePair.h>
#include <AzCore/Component/ComponentBus.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/Manipulators/ManipulatorView.h>
#include <Editor/Source/ComponentModes/Joints/JointsComponentModeCommon.h>
#include <PhysX/EditorJointBus.h>
#include <Source/Utils.h>
namespace PhysX
{
AZ_CLASS_ALLOCATOR_IMPL(JointsSubComponentModeAnglePair, AZ::SystemAllocator, 0);
namespace Internal
{
const float Alpha = 0.6f;
const AZ::Color ColorDefault = AZ::Color(1.0f, 1.0f, 1.0f, Alpha);
const AZ::Color ColorFirst = AZ::Color(1.0f, 0.0f, 0.0f, Alpha);
const AZ::Color ColorSecond = AZ::Color(0.0f, 1.0f, 0.0f, Alpha);
const AZ::Color ColorSweepArc = AZ::Color(1.0f, 1.0f, 1.0f, Alpha);
const float SweepLineDisplaceFactor = 0.5f;
const float SweepLineThickness = 1.0f;
const float SweepLineGranularity = 1.0f;
} // namespace Internal
JointsSubComponentModeAnglePair::JointsSubComponentModeAnglePair(
const AZStd::string& propertyName, const AZ::Vector3& axis, float max, float min)
: m_propertyName(propertyName)
, m_axis(axis)
, m_firstMax(max)
, m_firstMin(min)
, m_secondMax(min)
, m_secondMin(-max)
{
}
void JointsSubComponentModeAnglePair::Setup(const AZ::EntityComponentIdPair& idPair)
{
m_entityComponentIdPair = idPair;
EditorJointRequestBus::EventResult(m_resetValue, idPair, &EditorJointRequests::GetLinearValuePair, m_propertyName);
const AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(idPair.GetEntityId());
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, idPair, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform);
const AZ::Quaternion localRotation = localTransform.GetRotation();
AZ::Vector3 displacement = m_axis;
AZ::Transform displacementTransform = localTransform;
AZ::Vector3 displacementTranslate = localRotation.TransformVector(displacement);
displacementTransform.SetTranslation(localTransform.GetTranslation() + displacementTranslate);
m_firstManipulator = AzToolsFramework::AngularManipulator::MakeShared(worldTransform);
m_firstManipulator->AddEntityComponentIdPair(idPair);
m_firstManipulator->SetAxis(m_axis);
m_firstManipulator->SetLocalTransform(displacementTransform);
displacement = -m_axis;
displacementTranslate = localRotation.TransformVector(displacement);
displacementTransform.SetTranslation(localTransform.GetTranslation() + displacementTranslate);
m_secondManipulator = AzToolsFramework::AngularManipulator::MakeShared(worldTransform);
m_secondManipulator->AddEntityComponentIdPair(idPair);
m_secondManipulator->SetAxis(m_axis);
m_secondManipulator->SetLocalTransform(displacementTransform);
const float manipulatorRadius = 2.0f;
const float manipulatorWidth = 0.05f;
m_firstManipulator->SetView(AzToolsFramework::CreateManipulatorViewCircle(
*m_firstManipulator, Internal::ColorFirst, manipulatorRadius, manipulatorWidth, AzToolsFramework::DrawHalfDottedCircle));
m_secondManipulator->SetView(AzToolsFramework::CreateManipulatorViewCircle(
*m_secondManipulator, Internal::ColorSecond, manipulatorRadius, manipulatorWidth, AzToolsFramework::DrawHalfDottedCircle));
Refresh(idPair);
m_sharedRotationState = AZStd::make_shared<JointsComponentModeCommon::SubComponentModes::AngleModesSharedRotationState>();
auto mouseDownCallback = [this,
idPair](const AzToolsFramework::AngularManipulator::Action& action) mutable -> void
{
const AZ::Quaternion normalizedStart = action.m_start.m_rotation.GetNormalized();
m_sharedRotationState->m_axis = AZ::Vector3(normalizedStart.GetX(), normalizedStart.GetY(), normalizedStart.GetZ());
m_sharedRotationState->m_savedOrientation = AZ::Quaternion::CreateIdentity();
AngleLimitsFloatPair currentValue;
EditorJointRequestBus::EventResult(currentValue, idPair, &EditorJointRequests::GetLinearValuePair, m_propertyName);
m_sharedRotationState->m_valuePair = currentValue;
};
m_firstManipulator->InstallLeftMouseDownCallback(mouseDownCallback);
m_secondManipulator->InstallLeftMouseDownCallback(mouseDownCallback);
m_firstManipulator->InstallMouseMoveCallback(
[this, idPair](const AzToolsFramework::AngularManipulator::Action& action) mutable -> void
{
float angleDelta;
AZ::Quaternion manipulatorOrientation;
const float newValue = MouseMove(m_sharedRotationState, action, true, angleDelta, manipulatorOrientation);
if (newValue > m_firstMax || newValue < m_firstMin)
{
return;
}
m_firstManipulator->SetLocalOrientation(manipulatorOrientation);
const float newFirstValue = AZ::GetClamp(m_sharedRotationState->m_valuePair.first + angleDelta, m_firstMin, m_firstMax);
EditorJointRequestBus::Event(
idPair, &EditorJointRequests::SetLinearValuePair, m_propertyName,
AngleLimitsFloatPair(newFirstValue, m_sharedRotationState->m_valuePair.second));
});
m_secondManipulator->InstallMouseMoveCallback(
[this, idPair](const AzToolsFramework::AngularManipulator::Action& action) mutable -> void
{
float angleDelta;
AZ::Quaternion manipulatorOrientation;
const float newValue = MouseMove(m_sharedRotationState, action, false, angleDelta, manipulatorOrientation);
if (newValue > m_secondMax || newValue < m_secondMin)
{
return; // Not handling values exceeding limits
}
m_secondManipulator->SetLocalOrientation(manipulatorOrientation);
float newSecondValue = AZ::GetClamp(m_sharedRotationState->m_valuePair.second + angleDelta, m_secondMin, m_secondMax);
EditorJointRequestBus::Event(
idPair, &EditorJointRequests::SetLinearValuePair, m_propertyName,
AngleLimitsFloatPair(m_sharedRotationState->m_valuePair.first, newSecondValue));
});
m_firstManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
m_secondManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(idPair.GetEntityId());
}
void JointsSubComponentModeAnglePair::Refresh(const AZ::EntityComponentIdPair& idPair)
{
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, idPair, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform);
const AZ::Quaternion localRotation = localTransform.GetRotation();
AZ::Transform displacementTransform = localTransform;
AZ::Vector3 displacement = m_axis;
AZ::Vector3 displacementTranslate = localRotation.TransformVector(displacement);
displacementTransform.SetTranslation(localTransform.GetTranslation() + displacementTranslate);
m_firstManipulator->SetLocalTransform(displacementTransform);
displacement = -m_axis;
displacementTranslate = localRotation.TransformVector(displacement);
displacementTransform.SetTranslation(localTransform.GetTranslation() + displacementTranslate);
m_secondManipulator->SetLocalTransform(displacementTransform);
}
void JointsSubComponentModeAnglePair::Teardown(const AZ::EntityComponentIdPair& idPair)
{
AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
m_firstManipulator->RemoveEntityComponentIdPair(idPair);
m_firstManipulator->Unregister();
m_secondManipulator->RemoveEntityComponentIdPair(idPair);
m_secondManipulator->Unregister();
}
void JointsSubComponentModeAnglePair::ResetValues(const AZ::EntityComponentIdPair& idPair)
{
EditorJointRequestBus::Event(idPair, &EditorJointRequests::SetLinearValuePair, m_propertyName, m_resetValue);
m_firstManipulator->SetLocalOrientation(AZ::Quaternion::CreateFromAxisAngle(m_axis, m_resetValue.first));
m_secondManipulator->SetLocalOrientation(AZ::Quaternion::CreateFromAxisAngle(m_axis, m_resetValue.first));
}
float JointsSubComponentModeAnglePair::MouseMove(
AZStd::shared_ptr<JointsComponentModeCommon::SubComponentModes::AngleModesSharedRotationState>& sharedRotationState,
const AzToolsFramework::AngularManipulator::Action& action,
bool isFirstValue,
float& angleDelta,
AZ::Quaternion& manipulatorOrientation)
{
sharedRotationState->m_savedOrientation = action.m_current.m_delta.GetInverseFull();
angleDelta = 0.0f;
AZ::Vector3 axis = m_axis;
sharedRotationState->m_savedOrientation.ConvertToAxisAngle(axis, angleDelta);
// Polarity of axis is switched by ConvertToAxisAngle call depending on direction of rotation
if (abs(m_axis.GetX() - 1.0f) < FLT_EPSILON)
{
angleDelta = AZ::RadToDeg(angleDelta) * axis.GetX();
}
else if (abs(m_axis.GetY() - 1.0f) < FLT_EPSILON)
{
angleDelta = AZ::RadToDeg(angleDelta) * axis.GetY();
}
else if (abs(m_axis.GetZ() - 1.0f) < FLT_EPSILON)
{
angleDelta = AZ::RadToDeg(angleDelta) * axis.GetZ();
}
manipulatorOrientation = action.m_start.m_rotation * action.m_current.m_delta;
if (isFirstValue)
{
return sharedRotationState->m_valuePair.first + angleDelta;
}
return sharedRotationState->m_valuePair.second + angleDelta;
}
void JointsSubComponentModeAnglePair::DisplayEntityViewport(
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)
{
AngleLimitsFloatPair currentValue;
EditorJointRequestBus::EventResult(currentValue, m_entityComponentIdPair, &EditorJointRequests::GetLinearValuePair, m_propertyName);
const float size = 2.0f;
AZ::Vector3 axisPoint = m_axis * size * 0.5f;
AZStd::array<AZ::Vector3, 4> points = { -axisPoint, axisPoint, axisPoint, -axisPoint };
if (abs(m_axis.GetX() - 1.0f) < FLT_EPSILON)
{
points[2].SetZ(size);
points[3].SetZ(size);
}
else if (abs(m_axis.GetY() - 1.0f) < FLT_EPSILON)
{
points[2].SetX(size);
points[3].SetX(size);
}
else if (abs(m_axis.GetZ() - 1.0f) < FLT_EPSILON)
{
points[2].SetX(size);
points[3].SetX(size);
}
AZ::u32 stateBefore = debugDisplay.GetState();
debugDisplay.CullOff();
debugDisplay.SetAlpha(Internal::Alpha);
AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentIdPair.GetEntityId());
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentIdPair, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform);
debugDisplay.PushMatrix(worldTransform);
debugDisplay.PushMatrix(localTransform);
debugDisplay.SetColor(Internal::ColorSweepArc);
const AZ::Vector3 zeroVector = AZ::Vector3::CreateZero();
const AZ::Vector3 posPosition = m_axis * Internal::SweepLineDisplaceFactor;
const AZ::Vector3 negPosition = -posPosition;
debugDisplay.DrawArc(
posPosition, Internal::SweepLineThickness, -currentValue.first, currentValue.first, Internal::SweepLineGranularity, -m_axis);
debugDisplay.DrawArc(
zeroVector, Internal::SweepLineThickness, -currentValue.first, currentValue.first, Internal::SweepLineGranularity, -m_axis);
debugDisplay.DrawArc(
negPosition, Internal::SweepLineThickness, -currentValue.first, currentValue.first, Internal::SweepLineGranularity, -m_axis);
debugDisplay.DrawArc(
posPosition, Internal::SweepLineThickness, 0.0f, abs(currentValue.second), Internal::SweepLineGranularity, -m_axis);
debugDisplay.DrawArc(
zeroVector, Internal::SweepLineThickness, 0.0f, abs(currentValue.second), Internal::SweepLineGranularity, -m_axis);
debugDisplay.DrawArc(
negPosition, Internal::SweepLineThickness, 0.0f, abs(currentValue.second), Internal::SweepLineGranularity, -m_axis);
AZ::Quaternion firstRotate = AZ::Quaternion::CreateFromAxisAngle(m_axis, AZ::DegToRad(currentValue.first));
AZ::Transform firstTM = AZ::Transform::CreateFromQuaternion(firstRotate);
debugDisplay.PushMatrix(firstTM);
debugDisplay.SetColor(Internal::ColorFirst);
debugDisplay.DrawQuad(points[0], points[1], points[2], points[3]);
debugDisplay.PopMatrix();
AZ::Quaternion secondRotate = AZ::Quaternion::CreateFromAxisAngle(m_axis, AZ::DegToRad(currentValue.second));
AZ::Transform secondTM = AZ::Transform::CreateFromQuaternion(secondRotate);
debugDisplay.PushMatrix(secondTM);
debugDisplay.SetColor(Internal::ColorSecond);
debugDisplay.DrawQuad(points[0], points[1], points[2], points[3]);
debugDisplay.PopMatrix();
debugDisplay.SetColor(Internal::ColorDefault);
debugDisplay.DrawQuad(points[0], points[1], points[2], points[3]);
debugDisplay.PopMatrix(); // pop local transform
debugDisplay.PopMatrix(); // pop global transform
debugDisplay.SetState(stateBefore);
// reposition and reorientate manipulators
Refresh(m_entityComponentIdPair);
}
} // namespace PhysX
@@ -0,0 +1,70 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Math/Vector3.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/std/limits.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzToolsFramework/Manipulators/AngularManipulator.h>
#include <Editor/Source/ComponentModes/PhysXSubComponentModeBase.h>
#include <PhysX/EditorJointBus.h>
namespace AZ
{
class Vector3;
} // namespace AZ
namespace PhysX
{
namespace AngleComponentModes
{
struct SharedRotationState;
} // namespace AngleComponentModes
class JointsSubComponentModeAnglePair final
: public PhysXSubComponentModeBase
, private AzFramework::EntityDebugDisplayEventBus::Handler
{
public:
AZ_CLASS_ALLOCATOR_DECL;
JointsSubComponentModeAnglePair(const AZStd::string& propertyName, const AZ::Vector3& axis, float max, float min);
// PhysXSubComponentModeBase ...
void Setup(const AZ::EntityComponentIdPair& idPair) override;
void Refresh(const AZ::EntityComponentIdPair& idPair) override;
void Teardown(const AZ::EntityComponentIdPair& idPair) override;
void ResetValues(const AZ::EntityComponentIdPair& idPair) override;
private:
float MouseMove(
AZStd::shared_ptr<JointsComponentModeCommon::SubComponentModes::AngleModesSharedRotationState>& sharedRotationState,
const AzToolsFramework::AngularManipulator::Action& action,
bool isFirstValue,
float& angleDelta,
AZ::Quaternion& manipulatorOrientation);
// AzFramework::EntityDebugDisplayEventBus
void DisplayEntityViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override;
AZ::Vector3 m_axis = AZ::Vector3::CreateAxisX();
float m_firstMax = AZStd::numeric_limits<float>::max();
float m_firstMin = -AZStd::numeric_limits<float>::max();
float m_secondMax = AZStd::numeric_limits<float>::max();
float m_secondMin = -AZStd::numeric_limits<float>::max();
AZStd::shared_ptr<JointsComponentModeCommon::SubComponentModes::AngleModesSharedRotationState> m_sharedRotationState;
AngleLimitsFloatPair m_resetValue;
AZ::EntityComponentIdPair m_entityComponentIdPair;
AZStd::string m_propertyName;
AZStd::shared_ptr<AzToolsFramework::AngularManipulator> m_firstManipulator;
AZStd::shared_ptr<AzToolsFramework::AngularManipulator> m_secondManipulator;
};
}
@@ -0,0 +1,133 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Editor/Source/ComponentModes/Joints/JointsSubComponentModeLinearFloat.h>
#include <AzCore/Component/ComponentBus.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <Editor/Source/ComponentModes/Joints/JointsComponentModeCommon.h>
#include <PhysX/EditorJointBus.h>
#include <Source/Utils.h>
namespace PhysX
{
AZ_CLASS_ALLOCATOR_IMPL(JointsSubComponentModeLinearFloat, AZ::SystemAllocator, 0);
JointsSubComponentModeLinearFloat::JointsSubComponentModeLinearFloat(
const AZStd::string& propertyName, float exponent, float max, float min)
: m_propertyName(propertyName)
, m_exponent(exponent)
, m_inverseExponent(1.0f / exponent)
, m_max(max)
, m_min(min)
{
}
void JointsSubComponentModeLinearFloat::Setup(const AZ::EntityComponentIdPair& idPair)
{
EditorJointRequestBus::EventResult(m_resetValue, idPair, &EditorJointRequests::GetLinearValue, m_propertyName);
const AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(idPair.GetEntityId());
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, idPair, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform);
m_manipulator = AzToolsFramework::LinearManipulator::MakeShared(worldTransform);
m_manipulator->AddEntityComponentIdPair(idPair);
m_manipulator->SetAxis(AZ::Vector3::CreateAxisX());
m_manipulator->SetLocalTransform(localTransform);
Refresh(idPair);
const AZ::Color manipulatorColor(0.3f, 0.3f, 0.3f, 1.0f);
const float manipulatorSize = 0.05f;
AzToolsFramework::ManipulatorViews views;
views.emplace_back(AzToolsFramework::CreateManipulatorViewQuadBillboard(manipulatorColor, manipulatorSize));
m_manipulator->SetViews(AZStd::move(views));
struct SharedState
{
float m_startingValue = 0.0f;
};
auto sharedState = AZStd::make_shared<SharedState>();
m_manipulator->InstallLeftMouseDownCallback(
[this, sharedState, idPair]([[maybe_unused]]const AzToolsFramework::LinearManipulator::Action& action) mutable
{
float currentValue = 0.0f;
EditorJointRequestBus::EventResult(currentValue, idPair, &EditorJointRequests::GetLinearValue, m_propertyName);
sharedState->m_startingValue = currentValue;
});
m_manipulator->InstallMouseMoveCallback(
[this, sharedState, idPair](const AzToolsFramework::LinearManipulator::Action& action)
{
const float axisDisplacement = action.LocalPositionOffset().Dot(action.m_fixed.m_axis);
float newValue = AZ::GetClamp(sharedState->m_startingValue + DisplacementToDeltaValue(axisDisplacement), m_min, m_max);
EditorJointRequestBus::Event(idPair, &EditorJointRequests::SetLinearValue, m_propertyName, newValue);
const AZ::Vector3 localPosition = action.LocalPosition().GetMax(AZ::Vector3(0.01f, 0.0f, 0.0f));
m_manipulator->SetLocalTransform(AZ::Transform::CreateTranslation(localPosition));
});
m_manipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
}
void JointsSubComponentModeLinearFloat::Refresh(const AZ::EntityComponentIdPair& idPair)
{
float currentValue = 0.0f;
EditorJointRequestBus::EventResult(currentValue, idPair, &EditorJointRequests::GetLinearValue, m_propertyName);
m_manipulator->SetLocalTransform(AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX() * ValueToDisplacement(currentValue)));
}
void JointsSubComponentModeLinearFloat::Teardown(const AZ::EntityComponentIdPair& idPair)
{
m_manipulator->RemoveEntityComponentIdPair(idPair);
m_manipulator->Unregister();
m_manipulator.reset();
}
void JointsSubComponentModeLinearFloat::ResetValues(const AZ::EntityComponentIdPair& idPair)
{
EditorJointRequestBus::Event(idPair, &EditorJointRequests::SetLinearValue, m_propertyName, m_resetValue);
m_manipulator->SetLocalTransform(AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX() * ValueToDisplacement(m_resetValue)));
}
float JointsSubComponentModeLinearFloat::DisplacementToDeltaValue(float displacement) const
{
if (displacement > 0.0f)
{
return AZStd::pow(displacement, m_exponent);
}
else if (displacement < 0.0f)
{
return -AZStd::pow(fabsf(displacement), m_exponent);
}
else
{
return 0.0f;
}
}
float JointsSubComponentModeLinearFloat::ValueToDisplacement(float value) const
{
return AZStd::pow(value, m_inverseExponent);
}
} // namespace PhysX
@@ -0,0 +1,56 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Memory/Memory.h>
#include <AzCore/std/limits.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/string/string.h>
#include <Editor/Source/ComponentModes/PhysXSubComponentModeBase.h>
namespace AZ
{
class Vector3;
} // namespace AZ
namespace AzToolsFramework
{
class LinearManipulator;
} // namespace AzToolsFramework
namespace PhysX
{
class JointsSubComponentModeLinearFloat final
: public PhysXSubComponentModeBase
{
public:
AZ_CLASS_ALLOCATOR_DECL;
JointsSubComponentModeLinearFloat(
const AZStd::string& propertyName, float exponent, float max, float min);
// PhysXSubComponentModeBase ...
void Setup(const AZ::EntityComponentIdPair& idPair) override;
void Refresh(const AZ::EntityComponentIdPair& idPair) override;
void Teardown(const AZ::EntityComponentIdPair& idPair) override;
void ResetValues(const AZ::EntityComponentIdPair& idPair) override;
private:
float DisplacementToDeltaValue(float displacement) const;
float ValueToDisplacement(float value) const;
float m_exponent = 1.0f;
float m_inverseExponent = 1.0f;
float m_max = AZStd::numeric_limits<float>::max();
float m_min = -AZStd::numeric_limits<float>::max();
float m_resetValue = 0.0f;
AZStd::string m_propertyName;
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> m_manipulator;
};
}
@@ -0,0 +1,134 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Editor/Source/ComponentModes/Joints/JointsSubComponentModeRotation.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Math/Color.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzFramework/Viewport/CameraState.h>
#include <AzFramework/Viewport/ViewportColors.h>
#include <AzToolsFramework/Manipulators/AngularManipulator.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/Manipulators/ManipulatorView.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
#include <Editor/Source/ComponentModes/Joints/JointsComponentModeCommon.h>
#include <PhysX/EditorColliderComponentRequestBus.h>
#include <PhysX/EditorJointBus.h>
#include <Source/Utils.h>
namespace PhysX
{
AZ_CLASS_ALLOCATOR_IMPL(JointsSubComponentModeRotation, AZ::SystemAllocator, 0);
void JointsSubComponentModeRotation::Setup(const AZ::EntityComponentIdPair& idPair)
{
AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(idPair.GetEntityId());
const AZ::Quaternion worldRotation = worldTransform.GetRotation();
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, idPair, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform);
EditorJointRequestBus::EventResult(
m_resetValue, idPair, &PhysX::EditorJointRequests::GetVector3Value, JointsComponentModeCommon::ParamaterNames::Rotation);
const AZStd::array<AZ::Vector3, 3> axes = { AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ() };
const AZStd::array<AZ::Color, 3> colors = { AZ::Color(1.0f, 0.0f, 0.0f, 1.0f), AZ::Color(0.0f, 1.0f, 0.0f, 1.0f),
AZ::Color(0.0f, 0.0f, 1.0f, 1.0f) };
for (AZ::u32 i = 0; i < 3; ++i)
{
m_manipulators[i] = AzToolsFramework::AngularManipulator::MakeShared(worldTransform);
m_manipulators[i]->AddEntityComponentIdPair(idPair);
m_manipulators[i]->SetAxis(axes[i]);
m_manipulators[i]->SetLocalTransform(localTransform);
const float manipulatorRadius = 2.0f;
m_manipulators[i]->SetView(AzToolsFramework::CreateManipulatorViewCircle(
*m_manipulators[i], colors[i], manipulatorRadius,
AzToolsFramework::ManipulatorCicleBoundWidth(), AzToolsFramework::DrawHalfDottedCircle));
m_manipulators[i]->Register(AzToolsFramework::g_mainManipulatorManagerId);
}
InstallManipulatorMouseCallbacks(idPair);
}
void JointsSubComponentModeRotation::Refresh(const AZ::EntityComponentIdPair& idPair)
{
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, idPair, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform);
for (auto rotationManipulator : m_manipulators)
{
rotationManipulator->SetLocalTransform(localTransform);
}
}
void JointsSubComponentModeRotation::Teardown(const AZ::EntityComponentIdPair& idPair)
{
for (auto rotationManipulator : m_manipulators)
{
rotationManipulator->RemoveEntityComponentIdPair(idPair);
rotationManipulator->Unregister();
}
}
void JointsSubComponentModeRotation::ResetValues(const AZ::EntityComponentIdPair& idPair)
{
EditorJointRequestBus::Event(
idPair, &PhysX::EditorJointRequests::SetVector3Value, JointsComponentModeCommon::ParamaterNames::Rotation, m_resetValue);
const AZ::Quaternion reset = AZ::Quaternion::CreateFromEulerAnglesDegrees(m_resetValue);
for (auto manipulator : m_manipulators)
{
manipulator->SetLocalOrientation(reset);
}
}
void JointsSubComponentModeRotation::InstallManipulatorMouseCallbacks(const AZ::EntityComponentIdPair& idPair)
{
struct SharedState
{
AZ::Transform m_startTM;
};
auto sharedState = AZStd::make_shared<SharedState>();
auto mouseDownRotateXCallback =
[sharedState, idPair]([[maybe_unused]] const AzToolsFramework::AngularManipulator::Action& action) mutable -> void
{
EditorJointRequestBus::EventResult(
sharedState->m_startTM, idPair, &PhysX::EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform);
};
for (AZ::u32 index = 0; index < 3; ++index)
{
m_manipulators[index]->InstallLeftMouseDownCallback(mouseDownRotateXCallback);
m_manipulators[index]->InstallMouseMoveCallback(
[this, index, sharedState, idPair](const AzToolsFramework::AngularManipulator::Action& action) mutable -> void
{
const AZ::Quaternion manipulatorOrientation = action.m_start.m_rotation * action.m_current.m_delta;
AZ::Transform newTransform = AZ::Transform::CreateIdentity();
newTransform = sharedState->m_startTM * AZ::Transform::CreateFromQuaternion(action.m_current.m_delta);
EditorJointRequestBus::Event(
idPair, &PhysX::EditorJointRequests::SetVector3Value, JointsComponentModeCommon::ParamaterNames::Rotation,
newTransform.GetRotation().GetEulerDegrees());
m_manipulators[index]->SetLocalOrientation(manipulatorOrientation);
});
}
}
} // namespace PhysX
@@ -0,0 +1,44 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Math/Vector3.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <Editor/Source/ComponentModes/PhysXSubComponentModeBase.h>
namespace AzToolsFramework
{
class AngularManipulator;
}
namespace PhysX
{
class JointsSubComponentModeRotation final
: public PhysXSubComponentModeBase
{
public:
AZ_CLASS_ALLOCATOR_DECL;
JointsSubComponentModeRotation() = default;
// PhysXSubComponentModeBase ...
void Setup(const AZ::EntityComponentIdPair& idPair) override;
void Refresh(const AZ::EntityComponentIdPair& idPair) override;
void Teardown(const AZ::EntityComponentIdPair& idPair) override;
void ResetValues(const AZ::EntityComponentIdPair& idPair) override;
private:
void InstallManipulatorMouseCallbacks(const AZ::EntityComponentIdPair& idPair);
AZ::Vector3 m_resetValue = AZ::Vector3::CreateZero();
AZStd::array<AZStd::shared_ptr<AzToolsFramework::AngularManipulator>, 3> m_manipulators;
};
}
@@ -1,4 +1,3 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
@@ -6,49 +5,67 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Component/TransformBus.h>
#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
#include <Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnap.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
#include <Editor/EditorJointComponentMode.h>
#include <Editor/EditorSubComponentModeSnap.h>
#include <Editor/Source/ComponentModes/Joints/JointsComponentModeCommon.h>
#include <PhysX/EditorJointBus.h>
#include <Source/Utils.h>
namespace PhysX
{
EditorSubComponentModeSnap::EditorSubComponentModeSnap(
const AZ::EntityComponentIdPair& entityComponentIdPair
, const AZ::Uuid& componentType
, const AZStd::string& name)
: EditorSubComponentModeBase(entityComponentIdPair, componentType, name)
AZ_CLASS_ALLOCATOR_IMPL(JointsSubComponentModeSnap, AZ::SystemAllocator, 0);
void JointsSubComponentModeSnap::Setup(const AZ::EntityComponentIdPair& idPair)
{
m_entityComponentId = idPair;
AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId());
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentId
, &EditorJointRequests::GetTransformValue
, PhysX::EditorJointComponentMode::s_parameterTransform);
localTransform, m_entityComponentId, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform);
m_manipulator = AzToolsFramework::LinearManipulator::MakeShared(worldTransform);
m_manipulator->AddEntityComponentIdPair(m_entityComponentId);
m_manipulator->SetAxis(AZ::Vector3::CreateAxisX());
m_manipulator->SetLocalTransform(localTransform);
Refresh();
Refresh(idPair);
const AZ::Color manipulatorColor(0.3f, 0.3f, 0.3f, 1.0f);
const float manipulatorSize = 0.05f;
AzToolsFramework::ManipulatorViews views;
views.emplace_back(AzToolsFramework::CreateManipulatorViewQuadBillboard(manipulatorColor
, manipulatorSize));
views.emplace_back(AzToolsFramework::CreateManipulatorViewQuadBillboard(manipulatorColor, manipulatorSize));
m_manipulator->SetViews(AZStd::move(views));
m_manipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(m_entityComponentId.GetEntityId());
}
void EditorSubComponentModeSnap::HandleMouseInteraction(
const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteraction)
void JointsSubComponentModeSnap::Refresh(const AZ::EntityComponentIdPair& idPair)
{
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, idPair, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform);
m_manipulator->SetLocalTransform(localTransform);
}
void JointsSubComponentModeSnap::Teardown(const AZ::EntityComponentIdPair& idPair)
{
AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
m_manipulator->RemoveEntityComponentIdPair(idPair);
m_manipulator->Unregister();
m_manipulator.reset();
}
void JointsSubComponentModeSnap::HandleMouseInteraction(const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteraction)
{
if (mouseInteraction.m_mouseEvent == AzToolsFramework::ViewportInteraction::MouseEvent::Move)
{
@@ -63,25 +80,36 @@ namespace PhysX
const AZ::Quaternion worldRotateInv = worldRotate.GetInverseFull();
m_manipulator->SetLocalPosition(worldRotateInv.TransformVector(m_pickedPosition - worldTransform.GetTranslation()));
m_manipulator->SetBoundsDirty();
}
}
}
void EditorSubComponentModeSnap::Refresh()
AZStd::string JointsSubComponentModeSnap::GetPickedEntityName()
{
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentId
, &EditorJointRequests::GetTransformValue
, PhysX::EditorJointComponentMode::s_parameterTransform);
m_manipulator->SetLocalTransform(localTransform);
AZStd::string pickedEntityName;
if (m_pickedEntity.IsValid())
{
AZ::ComponentApplicationBus::BroadcastResult(
pickedEntityName, &AZ::ComponentApplicationRequests::GetEntityName, m_pickedEntity);
}
return pickedEntityName;
}
void EditorSubComponentModeSnap::DisplayEntityViewport(
const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay)
AZ::Vector3 JointsSubComponentModeSnap::GetPosition() const
{
AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId());
AZ::Quaternion worldRotate = worldTransform.GetRotation();
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentId, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform);
return worldTransform.GetTranslation() + worldRotate.TransformVector(localTransform.GetTranslation());
}
void JointsSubComponentModeSnap::DisplayEntityViewport(
const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)
{
AZ::u32 stateBefore = debugDisplay.GetState();
@@ -89,11 +117,9 @@ namespace PhysX
AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId());
AZ::Transform localTransform = AZ::Transform::CreateIdentity();;
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentId
, &EditorJointRequests::GetTransformValue
, PhysX::EditorJointComponentMode::s_parameterTransform);
localTransform, m_entityComponentId, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform);
debugDisplay.PushMatrix(worldTransform);
debugDisplay.PushMatrix(localTransform);
@@ -104,9 +130,7 @@ namespace PhysX
AngleLimitsFloatPair yzSwingAngleLimits;
EditorJointRequestBus::EventResult(
yzSwingAngleLimits, m_entityComponentId
, &EditorJointRequests::GetLinearValuePair
, PhysX::EditorJointComponentMode::s_parameterSwingLimit);
yzSwingAngleLimits, m_entityComponentId, &EditorJointRequests::GetLinearValuePair, JointsComponentModeCommon::ParamaterNames::SwingLimit);
const AZ::u32 numEllipseSamples = 16;
AZStd::array<AZ::Vector3, numEllipseSamples> ellipseSamples;
@@ -154,8 +178,8 @@ namespace PhysX
debugDisplay.DrawLine(ellipseSamples[numEllipseSamples * 3 / 4], ellipseSamples[numEllipseSamples / 4]);
debugDisplay.DrawLine(AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(coneHeight, 0.0f, 0.0f));
debugDisplay.PopMatrix();//pop local transform
debugDisplay.PopMatrix();//pop world transform
debugDisplay.PopMatrix(); // pop local transform
debugDisplay.PopMatrix(); // pop world transform
// draw line from joint to mouse-over entity
if (m_pickedEntity.IsValid())
@@ -173,40 +197,9 @@ namespace PhysX
debugDisplay.DrawWireBox(m_pickedEntityAabb.GetMin(), m_pickedEntityAabb.GetMax());
// draw something, e.g. an icon, to indicate type of snapping
DisplaySpecificSnapType(viewportInfo,
debugDisplay,
position,
directionNorm,
directionLength);
DisplaySpecificSnapType(viewportInfo, debugDisplay, position, directionNorm, directionLength);
}
debugDisplay.SetState(stateBefore);
}
AZStd::string EditorSubComponentModeSnap::GetPickedEntityName()
{
AZStd::string pickedEntityName;
if (m_pickedEntity.IsValid())
{
AZ::ComponentApplicationBus::BroadcastResult(pickedEntityName,
&AZ::ComponentApplicationRequests::GetEntityName,
m_pickedEntity);
}
return pickedEntityName;
}
AZ::Vector3 EditorSubComponentModeSnap::GetPosition() const
{
AZ::Transform worldTransform = PhysX::Utils::GetEntityWorldTransformWithoutScale(m_entityComponentId.GetEntityId());
AZ::Quaternion worldRotate = worldTransform.GetRotation();
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentId
, &EditorJointRequests::GetTransformValue
, PhysX::EditorJointComponentMode::s_parameterTransform);
return worldTransform.GetTranslation() + worldRotate.TransformVector(localTransform.GetTranslation());
}
} // namespace PhysX
@@ -1,4 +1,3 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
@@ -6,12 +5,16 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Memory/Memory.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <Editor/EditorSubComponentModeBase.h>
#include <Editor/EditorViewportEntityPicker.h>
#include <Editor/Source/ComponentModes/PhysXSubComponentModeBase.h>
namespace AzToolsFramework
{
@@ -20,48 +23,43 @@ namespace AzToolsFramework
namespace PhysX
{
/// This sub-component mode uses EditorViewportEntityPicker to get the position of an entity that the mouse is hovering over.
/// Classes inheriting from this class can use the mouse-over entity position to perform custom actions.
class EditorSubComponentModeSnap
: public PhysX::EditorSubComponentModeBase
class JointsSubComponentModeSnap
: public PhysXSubComponentModeBase
, protected AzFramework::EntityDebugDisplayEventBus::Handler
{
public:
EditorSubComponentModeSnap(
const AZ::EntityComponentIdPair& entityComponentIdPair
, const AZ::Uuid& componentType
, const AZStd::string& name);
virtual ~EditorSubComponentModeSnap() = default;
AZ_CLASS_ALLOCATOR_DECL;
// PhysX::EditorSubComponentModeBase
void HandleMouseInteraction(
const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteraction) override;
void Refresh() override;
JointsSubComponentModeSnap() = default;
// PhysXSubComponentModeBase ...
virtual void Setup(const AZ::EntityComponentIdPair& idPair) override;
virtual void Refresh(const AZ::EntityComponentIdPair& idPair) override;
virtual void Teardown(const AZ::EntityComponentIdPair& idPair) override;
void HandleMouseInteraction(const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteraction) override;
protected:
// AzFramework::EntityDebugDisplayEventBus
void DisplayEntityViewport(
const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay) override;
AZStd::string GetPickedEntityName();
AZ::Vector3 GetPosition() const;
/// Override to draw specific snap type display
// AzFramework::EntityDebugDisplayEventBus
void DisplayEntityViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override;
//! Override to draw specific snap type display
virtual void DisplaySpecificSnapType(
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo,
[[maybe_unused]] AzFramework::DebugDisplayRequests& debugDisplay,
[[maybe_unused]] const AZ::Vector3& jointPosition,
[[maybe_unused]] const AZ::Vector3& snapDirection,
[[maybe_unused]] float snapLength) {}
virtual void InitMouseDownCallBack() = 0;
AZStd::string GetPickedEntityName();
AZ::Vector3 GetPosition() const;
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> m_manipulator;
[[maybe_unused]] float snapLength)
{
}
EditorViewportEntityPicker m_picker;
AZ::EntityId m_pickedEntity;
AZ::Aabb m_pickedEntityAabb = AZ::Aabb::CreateNull();
AZ::Vector3 m_pickedPosition;
AZ::EntityComponentIdPair m_entityComponentId;
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> m_manipulator;
};
} // namespace PhysX
}
@@ -0,0 +1,91 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnapPosition.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
#include <Editor/Source/ComponentModes/Joints/JointsComponentModeCommon.h>
#include <PhysX/EditorJointBus.h>
#include <Source/Utils.h>
namespace PhysX
{
AZ_CLASS_ALLOCATOR_IMPL(JointsSubComponentModeSnapPosition, AZ::SystemAllocator, 0);
void JointsSubComponentModeSnapPosition::Setup(const AZ::EntityComponentIdPair& idPair)
{
JointsSubComponentModeSnap::Setup(idPair);
PhysX::EditorJointRequestBus::EventResult(
m_resetPosition, m_entityComponentId, &PhysX::EditorJointRequests::GetVector3Value, JointsComponentModeCommon::ParamaterNames::Position);
PhysX::EditorJointRequestBus::EventResult(
m_resetLeadEntity, m_entityComponentId, &PhysX::EditorJointRequests::GetEntityIdValue,
JointsComponentModeCommon::ParamaterNames::LeadEntity);
m_manipulator->InstallLeftMouseDownCallback(
[this](const AzToolsFramework::LinearManipulator::Action& /*action*/) mutable
{
if (!m_pickedEntity.IsValid())
{
return;
}
const AZ::Vector3 newLocalPosition = PhysX::Utils::ComputeJointLocalTransform(
PhysX::Utils::GetEntityWorldTransformWithScale(m_pickedEntity),
PhysX::Utils::GetEntityWorldTransformWithScale(m_entityComponentId.GetEntityId()))
.GetTranslation();
PhysX::EditorJointRequestBus::Event(
m_entityComponentId, &PhysX::EditorJointRequests::SetVector3Value, JointsComponentModeCommon::ParamaterNames::Position,
newLocalPosition);
const bool selectedEntityIsNotJointEntity = m_pickedEntity != m_entityComponentId.GetEntityId();
AZ_Error(
"EditorSubComponentModeSnapPosition", selectedEntityIsNotJointEntity,
"Joint's lead entity cannot be the same as the entity in which the joint resides. Select lead entity on snap failed.");
if (selectedEntityIsNotJointEntity)
{
PhysX::EditorJointRequestBus::Event(
m_entityComponentId, &PhysX::EditorJointRequests::SetEntityIdValue, JointsComponentModeCommon::ParamaterNames::LeadEntity,
m_pickedEntity);
}
});
}
void JointsSubComponentModeSnapPosition::ResetValues([[maybe_unused]]const AZ::EntityComponentIdPair& idPair)
{
PhysX::EditorJointRequestBus::Event(
m_entityComponentId, &PhysX::EditorJointRequests::SetVector3Value, JointsComponentModeCommon::ParamaterNames::Position, m_resetPosition);
PhysX::EditorJointRequestBus::Event(
m_entityComponentId, &PhysX::EditorJointRequests::SetEntityIdValue, JointsComponentModeCommon::ParamaterNames::LeadEntity, m_resetLeadEntity);
}
void JointsSubComponentModeSnapPosition::DisplaySpecificSnapType(
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay,
const AZ::Vector3& jointPosition,
const AZ::Vector3& snapDirection,
float snapLength)
{
const float arrowLength = 1.0f;
const float iconGap = 1.0f;
const AZ::Vector3 iconPosition = jointPosition + (snapDirection * (snapLength + arrowLength + iconGap));
debugDisplay.SetColor(AZ::Colors::Red);
debugDisplay.DrawArrow(iconPosition, iconPosition + AZ::Vector3(arrowLength, 0.0f, 0.0f));
debugDisplay.SetColor(AZ::Colors::Green);
debugDisplay.DrawArrow(iconPosition, iconPosition + AZ::Vector3(0.2f, arrowLength, 0.2f));
debugDisplay.SetColor(AZ::Colors::Blue);
debugDisplay.DrawArrow(iconPosition, iconPosition + AZ::Vector3(0.0f, 0.0f, arrowLength));
}
} // namespace PhysX
@@ -0,0 +1,42 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Math/Vector3.h>
#include <AzCore/Memory/Memory.h>
#include <Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnap.h>
namespace PhysX
{
class JointsSubComponentModeSnapPosition final
: public JointsSubComponentModeSnap
{
public:
AZ_CLASS_ALLOCATOR_DECL;
JointsSubComponentModeSnapPosition() = default;
// JointsSubComponentModeSnap ...
void Setup(const AZ::EntityComponentIdPair& idPair) override;
void ResetValues(const AZ::EntityComponentIdPair& idPair) override;
protected:
// JointsSubComponentModeSnap ...
void DisplaySpecificSnapType(
const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay,
const AZ::Vector3& jointPosition,
const AZ::Vector3& snapDirection,
float snapLength) override;
private:
AZ::Vector3 m_resetPosition;
AZ::EntityId m_resetLeadEntity;
};
}
@@ -0,0 +1,117 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Editor/Source/ComponentModes/Joints/JointsSubComponentModeSnapRotation.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
#include <Editor/Source/ComponentModes/Joints/JointsComponentModeCommon.h>
#include <PhysX/EditorJointBus.h>
#include <Source/Utils.h>
namespace PhysX
{
AZ_CLASS_ALLOCATOR_IMPL(JointsSubComponentModeSnapRotation, AZ::SystemAllocator, 0);
void JointsSubComponentModeSnapRotation::Setup(const AZ::EntityComponentIdPair& idPair)
{
JointsSubComponentModeSnap::Setup(idPair);
PhysX::EditorJointRequestBus::EventResult(
m_resetRotation, m_entityComponentId, &PhysX::EditorJointRequests::GetVector3Value, JointsComponentModeCommon::ParamaterNames::Rotation);
m_manipulator->InstallLeftMouseDownCallback(
[this]([[maybe_unused]] const AzToolsFramework::LinearManipulator::Action& action) mutable
{
if (!m_pickedEntity.IsValid())
{
return;
}
AZ::EntityId leadEntityId;
PhysX::EditorJointRequestBus::EventResult(
leadEntityId, m_entityComponentId, &PhysX::EditorJointRequests::GetEntityIdValue,
JointsComponentModeCommon::ParamaterNames::LeadEntity);
if (leadEntityId.IsValid() && m_pickedEntity == leadEntityId)
{
AZ_Warning(
"EditorsubComponentModeSnapRotation", false,
"The entity %s is the lead of the joint. Please snap rotation (or orientation) of joint to another entity that is "
"not the lead entity.",
GetPickedEntityName().c_str());
return;
}
AZ::Transform worldTransform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(worldTransform, m_entityComponentId.GetEntityId(), &AZ::TransformInterface::GetWorldTM);
worldTransform.ExtractUniformScale();
AZ::Transform localTransform = AZ::Transform::CreateIdentity();
EditorJointRequestBus::EventResult(
localTransform, m_entityComponentId, &EditorJointRequests::GetTransformValue, JointsComponentModeCommon::ParamaterNames::Transform);
AZ::Transform pickedEntityTransform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(pickedEntityTransform, m_pickedEntity, &AZ::TransformInterface::GetWorldTM);
const AZ::Transform worldTransformInv = worldTransform.GetInverse();
const AZ::Vector3 pickedLocalPosition =
worldTransformInv.TransformVector(pickedEntityTransform.GetTranslation()) - localTransform.GetTranslation();
if (AZStd::abs(pickedLocalPosition.GetLength()) < FLT_EPSILON)
{
AZ_Warning(
"EditorsubComponentModeSnapRotation", false,
"The entity %s is too close to the joint position. Please snap rotation to an entity that is not at the position "
"of the joint.",
GetPickedEntityName().c_str());
return;
}
const AZ::Vector3 targetDirection = pickedLocalPosition.GetNormalized();
const AZ::Vector3 sourceDirection = AZ::Vector3::CreateAxisX();
const AZ::Quaternion newLocalRotation = AZ::Quaternion::CreateShortestArc(sourceDirection, targetDirection);
PhysX::EditorJointRequestBus::Event(
m_entityComponentId, &PhysX::EditorJointRequests::SetVector3Value,
JointsComponentModeCommon::ParamaterNames::Rotation // using rotation parameter name to set the local rotation value
,
newLocalRotation.GetEulerDegrees());
});
}
void JointsSubComponentModeSnapRotation::ResetValues([[maybe_unused]] const AZ::EntityComponentIdPair& idPair)
{
PhysX::EditorJointRequestBus::Event(
m_entityComponentId, &PhysX::EditorJointRequests::SetVector3Value, JointsComponentModeCommon::ParamaterNames::Rotation, m_resetRotation);
}
void JointsSubComponentModeSnapRotation::DisplaySpecificSnapType(
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo,
AzFramework::DebugDisplayRequests& debugDisplay,
const AZ::Vector3& jointPosition,
const AZ::Vector3& snapDirection,
float snapLength)
{
const float circleRadius = 0.5f;
const float iconGap = 1.0f;
const AZ::Vector3 iconPosition = jointPosition + (snapDirection * (snapLength + circleRadius * 2.0f + iconGap));
debugDisplay.SetColor(AZ::Colors::Red);
debugDisplay.DrawCircle(iconPosition, circleRadius, 0);
debugDisplay.SetColor(AZ::Colors::Green);
debugDisplay.DrawCircle(iconPosition, circleRadius, 1);
debugDisplay.SetColor(AZ::Colors::Blue);
debugDisplay.DrawCircle(iconPosition, circleRadius, 2);
}
} // namespace PhysX

Some files were not shown because too many files have changed in this diff Show More