Merge pull request #365 from aws-lumberyard-dev/MultiplayerComponents

Multiplayer components
This commit is contained in:
kberg-amzn
2021-04-27 18:59:41 -07:00
committed by GitHub
95 changed files with 26262 additions and 13550 deletions
@@ -197,6 +197,7 @@ namespace AZ
AzFramework::InputDeviceKeyboard::Key::EditSpace, // ImGuiKey_Space
AzFramework::InputDeviceKeyboard::Key::EditEnter, // ImGuiKey_Enter
AzFramework::InputDeviceKeyboard::Key::Escape, // ImGuiKey_Escape
AzFramework::InputDeviceKeyboard::Key::NumPadEnter, // ImGuiKey_KeyPadEnter
AzFramework::InputDeviceKeyboard::Key::AlphanumericA, // ImGuiKey_A
AzFramework::InputDeviceKeyboard::Key::AlphanumericC, // ImGuiKey_C
AzFramework::InputDeviceKeyboard::Key::AlphanumericV, // ImGuiKey_V
@@ -95,14 +95,14 @@ namespace AZ
{
#if defined(IMGUI_ENABLED)
InitializeViewportSizeIfNeeded();
ImGui::ImGuiManagerListenerBus::Broadcast(&ImGui::IImGuiManagerListener::Render);
ImGui::ImGuiManagerBus::Broadcast(&ImGui::IImGuiManager::Render);
#endif
}
void ImguiAtomSystemComponent::OnViewportSizeChanged(AzFramework::WindowSize size)
{
#if defined(IMGUI_ENABLED)
ImGui::ImGuiManagerListenerBus::Broadcast([this, size](ImGui::ImGuiManagerListenerBus::Events* imgui)
ImGui::ImGuiManagerBus::Broadcast([this, size](ImGui::ImGuiManagerBus::Events* imgui)
{
imgui->OverrideRenderWindowSize(size.m_width, size.m_height);
// ImGuiManagerListenerBus may not have been connected when this system component is activated
+1 -1
View File
@@ -26,7 +26,7 @@ ly_add_target(
PRIVATE
Source
INTERFACE
../External/ImGui/v1.70
../External/ImGui/v1.82
PUBLIC
Include
COMPILE_DEFINITIONS
+4 -4
View File
@@ -45,7 +45,7 @@ ImGuiViewportWidget::ImGuiViewportWidget(QWidget* parent)
ImGuiViewportWidget::~ImGuiViewportWidget()
{
DestroyRenderContext();
ImGuiManagerListenerBus::Broadcast(&IImGuiManagerListener::SetEditorWindowState,
ImGuiManagerBus::Broadcast(&IImGuiManager::SetEditorWindowState,
DisplayState::Hidden);
}
@@ -68,7 +68,7 @@ bool ImGuiViewportWidget::CreateRenderContext()
editor->GetEnv()->pRenderer->CreateContext(window);
RestorePreviousContext();
ImGuiManagerListenerBus::Broadcast(&IImGuiManagerListener::SetEditorWindowState,
ImGuiManagerBus::Broadcast(&IImGuiManager::SetEditorWindowState,
DisplayState::Visible);
m_creatingRenderContext = false;
@@ -154,8 +154,8 @@ void ImGuiViewportWidget::Render()
ColorF stateMessageColor(Col_Gray);
AZStd::string stateMessage = "No State";
DisplayState visibilityState = DisplayState::Hidden;
ImGuiManagerListenerBus::BroadcastResult(visibilityState,
&IImGuiManagerListener::GetEditorWindowState);
ImGuiManagerBus::BroadcastResult(visibilityState,
&IImGuiManager::GetEditorWindowState);
switch (visibilityState)
{
case ImGui::DisplayState::Hidden:
+14 -6
View File
@@ -68,13 +68,12 @@ namespace ImGui
typedef AZ::EBus<IImGuiUpdateListener> ImGuiUpdateListenerBus;
// Bus for sending events and getting state from the ImGui manager
class IImGuiManagerListener : public AZ::EBusTraits
class IImGuiManager
{
public:
static const char* GetUniqueName() { return "IImGuiManagerListener"; }
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
using Bus = AZ::EBus<IImGuiManagerListener>;
AZ_RTTI(IImGuiManager, "{F5A0F08B-F2DA-43B7-8CD2-C6FC71E1A712}");
static const char* GetUniqueName() { return "IImGuiManager"; }
virtual DisplayState GetEditorWindowState() const = 0;
virtual void SetEditorWindowState(DisplayState state) = 0;
@@ -94,7 +93,16 @@ namespace ImGui
virtual void RestoreRenderWindowSizeToDefault() = 0;
virtual void Render() = 0;
};
typedef AZ::EBus<IImGuiManagerListener> ImGuiManagerListenerBus;
class IImGuiManagerRequests
: public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
using Bus = AZ::EBus<IImGuiManager>;
};
using ImGuiManagerBus = AZ::EBus<IImGuiManager, IImGuiManagerRequests>;
// Bus for getting notifications from the IMGUI Entity Outliner
class IImGuiEntityOutlinerNotifcations : public AZ::EBusTraits
+12 -8
View File
@@ -143,7 +143,7 @@ namespace
void ImGuiManager::Initialize()
{
// Register for Buses
ImGuiManagerListenerBus::Handler::BusConnect();
ImGuiManagerBus::Handler::BusConnect();
// Register for Input Notifications
InputChannelEventListener::Connect();
@@ -236,10 +236,14 @@ void ImGuiManager::Initialize()
// Future work here could include responding to the mouse being connected and disconnected at run-time, but this is fine for now.
const AzFramework::InputDevice* mouseDevice = AzFramework::InputDeviceRequests::FindInputDevice(AzFramework::InputDeviceMouse::Id);
m_hardwardeMouseConnected = mouseDevice && mouseDevice->IsConnected();
AZ::Interface<ImGui::IImGuiManager>::Register(this);
}
void ImGuiManager::Shutdown()
{
AZ::Interface<ImGui::IImGuiManager>::Unregister(this);
if (!gEnv)
{
AZ_Warning("ImGuiManager", false, "%s %s", __func__, "gEnv Invalid -- Skipping ImGui Shutdown.");
@@ -253,7 +257,7 @@ void ImGuiManager::Shutdown()
#endif
// Unregister from Buses
ImGuiManagerListenerBus::Handler::BusDisconnect();
ImGuiManagerBus::Handler::BusDisconnect();
InputChannelEventListener::Disconnect();
InputTextEventListener::Disconnect();
AzFramework::WindowNotificationBus::Handler::BusDisconnect();
@@ -332,7 +336,7 @@ void ImGuiManager::Render()
}
// If no item and no window is focused, we should artificially add focus to the Main Menu Bar, to save 1 step when navigating with a controller.
if (!ImGui::IsAnyItemFocused() && !ImGui::IsAnyWindowFocused())
if (!ImGui::IsAnyItemFocused() && !ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow))
{
ImGuiWindow* mainMenuWin = ImGui::FindWindowByName("##MainMenuBar");
if (mainMenuWin)
@@ -829,27 +833,27 @@ void OnEnableCameraMonitorCBFunc(ICVar* pArgs)
void OnShowImGuiCBFunc(ICVar* pArgs)
{
ImGui::ImGuiManagerListenerBus::Broadcast(&ImGui::IImGuiManagerListener::SetClientMenuBarState, pArgs->GetIVal() != 0 ? ImGui::DisplayState::Visible : ImGui::DisplayState::Hidden);
ImGui::ImGuiManagerBus::Broadcast(&ImGui::IImGuiManager::SetClientMenuBarState, pArgs->GetIVal() != 0 ? ImGui::DisplayState::Visible : ImGui::DisplayState::Hidden);
}
void OnDiscreteInputModeCBFunc(ICVar* pArgs)
{
ImGui::ImGuiManagerListenerBus::Broadcast(&ImGui::IImGuiManagerListener::SetEnableDiscreteInputMode, pArgs->GetIVal() != 0 );
ImGui::ImGuiManagerBus::Broadcast(&ImGui::IImGuiManager::SetEnableDiscreteInputMode, pArgs->GetIVal() != 0 );
}
void OnEnableControllerCBFunc(ICVar* pArgs)
{
ImGui::ImGuiManagerListenerBus::Broadcast(&ImGui::IImGuiManagerListener::EnableControllerSupportMode, ImGuiControllerModeFlags::Contextual, (pArgs->GetIVal() != 0));
ImGui::ImGuiManagerBus::Broadcast(&ImGui::IImGuiManager::EnableControllerSupportMode, ImGuiControllerModeFlags::Contextual, (pArgs->GetIVal() != 0));
}
void OnEnableControllerMouseCBFunc(ICVar* pArgs)
{
ImGui::ImGuiManagerListenerBus::Broadcast(&ImGui::IImGuiManagerListener::EnableControllerSupportMode, ImGuiControllerModeFlags::Mouse, (pArgs->GetIVal() != 0));
ImGui::ImGuiManagerBus::Broadcast(&ImGui::IImGuiManager::EnableControllerSupportMode, ImGuiControllerModeFlags::Mouse, (pArgs->GetIVal() != 0));
}
void OnControllerMouseSensitivityCBFunc(ICVar* pArgs)
{
ImGui::ImGuiManagerListenerBus::Broadcast(&ImGui::IImGuiManagerListener::SetControllerMouseSensitivity, pArgs->GetFVal());
ImGui::ImGuiManagerBus::Broadcast(&ImGui::IImGuiManager::SetControllerMouseSensitivity, pArgs->GetFVal());
}
+3 -3
View File
@@ -33,7 +33,7 @@ namespace ImGui
class ImGuiManager
: public AzFramework::InputChannelEventListener
, public AzFramework::InputTextEventListener
, public ImGuiManagerListenerBus::Handler
, public ImGuiManagerBus::Handler
, public AzFramework::WindowNotificationBus::Handler
{
public:
@@ -45,7 +45,7 @@ namespace ImGui
protected:
void RenderImGuiBuffers(const ImVec2& scaleRects);
// -- ImGuiManagerListenerBus Interface -------------------------------------------------------------------
// -- ImGuiManagerBus Interface -------------------------------------------------------------------
DisplayState GetEditorWindowState() const override { return m_editorWindowState; }
void SetEditorWindowState(DisplayState state) override { m_editorWindowState = state; }
DisplayState GetClientMenuBarState() const override { return m_clientMenuBarState; }
@@ -63,7 +63,7 @@ namespace ImGui
void OverrideRenderWindowSize(uint32_t width, uint32_t height) override;
void RestoreRenderWindowSizeToDefault() override;
void Render() override;
// -- ImGuiManagerListenerBus Interface -------------------------------------------------------------------
// -- ImGuiManagerBus Interface -------------------------------------------------------------------
// -- AzFramework::InputChannelEventListener and AzFramework::InputTextEventListener Interface ------------
bool OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel) override;
@@ -362,7 +362,7 @@ namespace ImGui
ImGui::NextColumn();
// A Small Legend and Hints section for help using this thing
ImGui::BeginChild("MouseHoverLegendChild", ImVec2(250.0f, 30.0f), true);
if (ImGui::IsMouseHoveringWindow())
if (ImGui::IsWindowHovered())
{
ImGui::BeginTooltip();
ImGui::TextColored(ImGui::Colors::s_NiceLabelColor, "Legend:");
@@ -389,7 +389,7 @@ namespace ImGui
ImGui::EndTooltip();
}
ImGui::TextColored(ImGui::IsMouseHoveringWindow() ? ImGui::Colors::s_NiceLabelColor : ImGui::Colors::s_PlainLabelColor, "Mouse Over For Legend and Tips");
ImGui::TextColored(ImGui::IsWindowHovered() ? ImGui::Colors::s_NiceLabelColor : ImGui::Colors::s_PlainLabelColor, "Mouse Over For Legend and Tips");
ImGui::EndChild(); // MouseHover Child
ImGui::NextColumn();
@@ -115,7 +115,7 @@ namespace ImGui
ImGui::TextColored(ImGui::Colors::s_NiceLabelColor, "Previous Cam %d: %s %s", i, camInfo.m_camId.ToString().c_str(), camInfo.m_camName.c_str());
ImGui::TextColored(ImGui::Colors::s_NiceLabelColor, " Active Cam frames/time: %d / %.02f", camInfo.m_activeFrames, camInfo.m_activeTime);
if (ImGui::IsMouseHoveringWindow())
if (ImGui::IsWindowHovered())
{
ImGui::BeginTooltip();
ImGui::BeginChild(AZStd::string::format("cameraInfoTooltip%d", i).c_str(), ImVec2(500.0f, 140.0f), true);
@@ -100,7 +100,7 @@ namespace ImGui
{
// Get Discrete Input state now, we will use it both inside the ImGui SubMenu, and along the main task bar ( when it is on )
bool discreteInputEnabled = false;
ImGuiManagerListenerBus::BroadcastResult(discreteInputEnabled, &IImGuiManagerListener::GetEnableDiscreteInputMode);
ImGuiManagerBus::BroadcastResult(discreteInputEnabled, &IImGuiManager::GetEnableDiscreteInputMode);
// Input Mode Display
{
@@ -116,7 +116,7 @@ namespace ImGui
{
// Discrete Input - Control ImGui and Game independently.
ImGui::DisplayState state;
ImGui::ImGuiManagerListenerBus::BroadcastResult(state, &ImGui::IImGuiManagerListener::GetClientMenuBarState);
ImGui::ImGuiManagerBus::BroadcastResult(state, &ImGui::IImGuiManager::GetClientMenuBarState);
if (state == DisplayState::Visible)
{
inputTitle.append("ImGui");
@@ -414,40 +414,40 @@ namespace ImGui
// Controller Support - Contextual
{
bool controllerEnabled = false;
ImGuiManagerListenerBus::BroadcastResult(controllerEnabled, &IImGuiManagerListener::IsControllerSupportModeEnabled, ImGuiControllerModeFlags::Contextual);
ImGuiManagerBus::BroadcastResult(controllerEnabled, &IImGuiManager::IsControllerSupportModeEnabled, ImGuiControllerModeFlags::Contextual);
bool controllerEnabledCheckbox = controllerEnabled;
ImGui::Checkbox(AZStd::string::format("Controller Support (Contextual) %s (Click Checkbox to Toggle)", controllerEnabledCheckbox ? "On" : "Off").c_str(), &controllerEnabledCheckbox);
if (controllerEnabledCheckbox != controllerEnabled)
{
ImGuiManagerListenerBus::Broadcast(&IImGuiManagerListener::EnableControllerSupportMode, ImGuiControllerModeFlags::Contextual, controllerEnabledCheckbox);
ImGuiManagerBus::Broadcast(&IImGuiManager::EnableControllerSupportMode, ImGuiControllerModeFlags::Contextual, controllerEnabledCheckbox);
}
}
// Controller Support - Mouse
{
bool controllerMouseEnabled = false;
ImGuiManagerListenerBus::BroadcastResult(controllerMouseEnabled, &IImGuiManagerListener::IsControllerSupportModeEnabled, ImGuiControllerModeFlags::Mouse);
ImGuiManagerBus::BroadcastResult(controllerMouseEnabled, &IImGuiManager::IsControllerSupportModeEnabled, ImGuiControllerModeFlags::Mouse);
bool controllerMouseEnabledCheckbox = controllerMouseEnabled;
ImGui::Checkbox(AZStd::string::format("Controller Support (Mouse) %s (Click Checkbox to Toggle)", controllerMouseEnabledCheckbox ? "On" : "Off").c_str(), &controllerMouseEnabledCheckbox);
if (controllerMouseEnabledCheckbox != controllerMouseEnabled)
{
ImGuiManagerListenerBus::Broadcast(&IImGuiManagerListener::EnableControllerSupportMode, ImGuiControllerModeFlags::Mouse, controllerMouseEnabledCheckbox);
ImGuiManagerBus::Broadcast(&IImGuiManager::EnableControllerSupportMode, ImGuiControllerModeFlags::Mouse, controllerMouseEnabledCheckbox);
}
// Only draw Controller Mouse Sensitivity slider if the mouse is enabled
if (controllerMouseEnabled)
{
float controllerMouseSensitivity = 1.0f;
ImGuiManagerListenerBus::BroadcastResult(controllerMouseSensitivity, &IImGuiManagerListener::GetControllerMouseSensitivity);
ImGuiManagerBus::BroadcastResult(controllerMouseSensitivity, &IImGuiManager::GetControllerMouseSensitivity);
float controllerMouseSensitivitySlider = controllerMouseSensitivity;
ImGui::DragFloat("Controller Mouse Sensitivity", &controllerMouseSensitivitySlider, 0.1f, 0.1f, 50.0f);
if (controllerMouseSensitivitySlider != controllerMouseSensitivity)
{
ImGuiManagerListenerBus::Broadcast(&IImGuiManagerListener::SetControllerMouseSensitivity, controllerMouseSensitivitySlider);
ImGuiManagerBus::Broadcast(&IImGuiManager::SetControllerMouseSensitivity, controllerMouseSensitivitySlider);
}
}
}
@@ -458,7 +458,7 @@ namespace ImGui
ImGui::Checkbox(AZStd::string::format("Discrete Input %s (Click Checkbox to Toggle)", discreteInputEnabledCheckbox ? "On" : "Off").c_str(), &discreteInputEnabledCheckbox);
if (discreteInputEnabledCheckbox != discreteInputEnabled)
{
ImGuiManagerListenerBus::Broadcast(&IImGuiManagerListener::SetEnableDiscreteInputMode, discreteInputEnabledCheckbox);
ImGuiManagerBus::Broadcast(&IImGuiManager::SetEnableDiscreteInputMode, discreteInputEnabledCheckbox);
}
}
@@ -484,7 +484,7 @@ namespace ImGui
ImGui::TextColored(ImGui::Colors::s_NiceLabelColor, "ImGui Resolution Mode:");
ImGuiResolutionMode resMode = ImGuiResolutionMode::MatchRenderResolution;
ImGuiManagerListenerBus::BroadcastResult(resMode, &IImGuiManagerListener::GetResolutionMode);
ImGuiManagerBus::BroadcastResult(resMode, &IImGuiManager::GetResolutionMode);
int resModeRadioBtn = static_cast<int>(resMode);
ImGui::RadioButton("Force Resolution", &resModeRadioBtn, static_cast<int>(ImGuiResolutionMode::LockToResolution));
@@ -496,12 +496,12 @@ namespace ImGui
ImGuiResolutionMode resModeRadioBtnResult = static_cast<ImGuiResolutionMode>(resModeRadioBtn);
if (resModeRadioBtnResult != resMode)
{
ImGuiManagerListenerBus::Broadcast(&IImGuiManagerListener::SetResolutionMode, resModeRadioBtnResult);
ImGuiManagerBus::Broadcast(&IImGuiManager::SetResolutionMode, resModeRadioBtnResult);
}
// Resolutions
ImVec2 imGuiRes;
ImGuiManagerListenerBus::BroadcastResult(imGuiRes, &IImGuiManagerListener::GetImGuiRenderResolution);
ImGuiManagerBus::BroadcastResult(imGuiRes, &IImGuiManager::GetImGuiRenderResolution);
ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Current ImGui Resolution: ");
ImGui::SameLine();
@@ -518,7 +518,7 @@ namespace ImGui
if (ImGui::Button(AZStd::string::format("%d x %d", s_renderResolutionWidths[j], renderHeight).c_str(), ImVec2(400, 0)))
{
ImVec2 newRenderRes(static_cast<float>(s_renderResolutionWidths[j]), static_cast<float>(renderHeight));
ImGuiManagerListenerBus::Broadcast(&IImGuiManagerListener::SetImGuiRenderResolution, newRenderRes);
ImGuiManagerBus::Broadcast(&IImGuiManager::SetImGuiRenderResolution, newRenderRes);
}
}
}
@@ -577,10 +577,10 @@ namespace ImGui
void ImGuiLYCommonMenu::OnImGuiUpdate_DrawControllerLegend()
{
bool contextualControllerEnabled = false;
ImGuiManagerListenerBus::BroadcastResult(contextualControllerEnabled, &IImGuiManagerListener::IsControllerSupportModeEnabled, ImGuiControllerModeFlags::Contextual);
ImGuiManagerBus::BroadcastResult(contextualControllerEnabled, &IImGuiManager::IsControllerSupportModeEnabled, ImGuiControllerModeFlags::Contextual);
bool controllerMouseEnabled = false;
ImGuiManagerListenerBus::BroadcastResult(controllerMouseEnabled, &IImGuiManagerListener::IsControllerSupportModeEnabled, ImGuiControllerModeFlags::Mouse);
ImGuiManagerBus::BroadcastResult(controllerMouseEnabled, &IImGuiManager::IsControllerSupportModeEnabled, ImGuiControllerModeFlags::Mouse);
ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Contextual Controller Input Legend. Currently Enabled:");
ImGui::SameLine();
@@ -701,10 +701,10 @@ namespace ImGui
AZ::TickBus::Handler::BusConnect();
// Get the current ImGui Display state to restore it later.
ImGuiManagerListenerBus::BroadcastResult(m_telemetryCapturePreCaptureState, &IImGuiManagerListener::GetClientMenuBarState);
ImGuiManagerBus::BroadcastResult(m_telemetryCapturePreCaptureState, &IImGuiManager::GetClientMenuBarState);
// Turn off the ImGui Manager
ImGuiManagerListenerBus::Broadcast(&IImGuiManagerListener::SetClientMenuBarState, DisplayState::Hidden);
ImGuiManagerBus::Broadcast(&IImGuiManager::SetClientMenuBarState, DisplayState::Hidden);
}
void ImGuiLYCommonMenu::StopTelemetryCapture()
@@ -714,7 +714,7 @@ namespace ImGui
// Restore ImGui State
// Turn off the ImGui Manager
ImGuiManagerListenerBus::Broadcast(&IImGuiManagerListener::SetClientMenuBarState, m_telemetryCapturePreCaptureState);
ImGuiManagerBus::Broadcast(&IImGuiManager::SetClientMenuBarState, m_telemetryCapturePreCaptureState);
// Reset timer and disconnect tick bus
m_telemetryCaptureTimeRemaining = 0.0f;
@@ -96,7 +96,7 @@ namespace ImGui
}
// Toggle collapsing when double clicking this "window"
if (ImGui::IsMouseDoubleClicked(0) && ImGui::IsMouseHoveringWindow())
if (ImGui::IsMouseDoubleClicked(0) && ImGui::IsWindowHovered())
{
m_collapsed = !m_collapsed;
}
+9 -8
View File
@@ -10,12 +10,13 @@
#
set(FILES
../External/ImGui/v1.70/imgui/imconfig.h
../External/ImGui/v1.70/imgui/imgui.cpp
../External/ImGui/v1.70/imgui/imgui.h
../External/ImGui/v1.70/imgui/imgui_draw.cpp
../External/ImGui/v1.70/imgui/imgui_internal.h
../External/ImGui/v1.70/imgui/imgui_user.h
../External/ImGui/v1.70/imgui/imgui_user.inl
../External/ImGui/v1.70/imgui/imgui_widgets.cpp
../External/ImGui/v1.82/imgui/imconfig.h
../External/ImGui/v1.82/imgui/imgui.cpp
../External/ImGui/v1.82/imgui/imgui.h
../External/ImGui/v1.82/imgui/imgui_draw.cpp
../External/ImGui/v1.82/imgui/imgui_internal.h
../External/ImGui/v1.82/imgui/imgui_tables.cpp
../External/ImGui/v1.82/imgui/imgui_user.h
../External/ImGui/v1.82/imgui/imgui_user.inl
../External/ImGui/v1.82/imgui/imgui_widgets.cpp
)
-20
View File
@@ -1,20 +0,0 @@
# editorconfig.org
# top-most EditorConfig file
root = true
# Default settings:
# Use 4 spaces as indentation
[*]
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
[imstb_*]
indent_size = 3
trim_trailing_whitespace = false
[Makefile]
indent_style = tab
indent_size = 4
-34
View File
@@ -1,34 +0,0 @@
language: cpp
sudo: required
dist: trusty
os:
- linux
- osx
compiler:
- gcc
- clang
before_install:
- if [ $TRAVIS_OS_NAME == linux ]; then
sudo apt-get update -qq;
sudo apt-get install -y --no-install-recommends libxrandr-dev libxi-dev libxxf86vm-dev libsdl2-dev;
wget https://github.com/glfw/glfw/releases/download/3.2.1/glfw-3.2.1.zip;
unzip glfw-3.2.1.zip && cd glfw-3.2.1;
cmake -DBUILD_SHARED_LIBS=true -DGLFW_BUILD_EXAMPLES=false -DGLFW_BUILD_TESTS=false -DGLFW_BUILD_DOCS=false .;
sudo make -j $CPU_NUM install && cd ..;
fi
- if [ $TRAVIS_OS_NAME == osx ]; then
brew update;
brew install glfw3;
brew install sdl2;
fi
script:
- make -C examples/example_glfw_opengl2
- make -C examples/example_glfw_opengl3
- make -C examples/example_sdl_opengl3
- if [ $TRAVIS_OS_NAME == osx ]; then
xcodebuild -project examples/example_apple_metal/example_apple_metal.xcodeproj -target example_apple_metal_macos;
fi
-81
View File
@@ -1,81 +0,0 @@
// Modifications copyright Amazon.com, Inc. or its affiliates.
//-----------------------------------------------------------------------------
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
//-----------------------------------------------------------------------------
// A) You may edit imconfig.h (and not overwrite it when updating imgui, or maintain a patch/branch with your modifications to imconfig.h)
// B) or add configuration directives in your own file and compile with #define IMGUI_USER_CONFIG "myfilename.h"
// If you do so you need to make sure that configuration settings are defined consistently _everywhere_ dear imgui is used, which include
// the imgui*.cpp files but also _any_ of your code that uses imgui. This is because some compile-time options have an affect on data structures.
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
//-----------------------------------------------------------------------------
#pragma once
// Include Platform Def to get mutliplatform AZ_DLL_IMPORT and AZ_DLL_EXPORT to use below
#include <AzCore/PlatformDef.h>
//---- Define assertion handler. Defaults to calling assert().
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows.
#ifdef IMGUI_API_IMPORT
#define IMGUI_API AZ_DLL_IMPORT
#else
#define IMGUI_API AZ_DLL_EXPORT
#endif // IMGUI_API_IMPORT
//---- Don't define obsolete functions/enums names. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//---- Don't implement demo windows functionality (ShowDemoWindow()/ShowStyleEditor()/ShowUserGuide() methods will be empty)
//---- It is very strongly recommended to NOT disable the demo windows during development. Please read the comments in imgui_demo.cpp.
//#define IMGUI_DISABLE_DEMO_WINDOWS
//---- Don't implement some functions to reduce linkage requirements.
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc.
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow.
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function.
//#define IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself if you don't want to link with vsnprintf.
//#define IMGUI_DISABLE_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 wrapper so you can implement them yourself. Declare your prototypes in imconfig.h.
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
//---- Include imgui_user.h at the end of imgui.h as a convenience
//#define IMGUI_INCLUDE_IMGUI_USER_H
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
//#define IMGUI_USE_BGRA_PACKED_COLOR
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
// By default the embedded implementations are declared static and not available outside of imgui cpp files.
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
/*
#define IM_VEC2_CLASS_EXTRA \
ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \
operator MyVec2() const { return MyVec2(x,y); }
#define IM_VEC4_CLASS_EXTRA \
ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \
operator MyVec4() const { return MyVec4(x,y,z,w); }
*/
//---- Use 32-bit vertex indices (default is 16-bit) to allow meshes with more than 64K vertices. Render function needs to support it.
//#define ImDrawIdx unsigned int
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
/*
namespace ImGui
{
void MyFunction(const char* name, const MyMatrix44& v);
}
*/
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,321 +0,0 @@
dear imgui, v1.70
(Font Readme)
---------------------------------------
The code in imgui.cpp embeds a copy of 'ProggyClean.ttf' (by Tristan Grimmer),
a 13 pixels high, pixel-perfect font used by default.
We embed it font in source code so you can use Dear ImGui without any file system access.
You may also load external .TTF/.OTF files.
The files in this folder are suggested fonts, provided as a convenience.
Fonts are rasterized in a single texture at the time of calling either of io.Fonts->GetTexDataAsAlpha8()/GetTexDataAsRGBA32()/Build().
Also read dear imgui FAQ in imgui.cpp!
If you have other loading/merging/adding fonts, you can post on the Dear ImGui "Getting Started" forum:
https://discourse.dearimgui.org/c/getting-started
---------------------------------------
INDEX:
---------------------------------------
- Readme First / FAQ
- Using Icons
- Fonts Loading Instructions
- FreeType rasterizer, Small font sizes
- Building Custom Glyph Ranges
- Embedding Fonts in Source Code
- Credits/Licences for fonts included in this folder
- Fonts Links
---------------------------------------
README FIRST / FAQ
---------------------------------------
- You can use the style editor ImGui::ShowStyleEditor() in the "Fonts" section to browse your fonts
and understand what's going on if you have an issue.
- Make sure your font ranges data are persistent (available during the call to GetTexDataAsAlpha8()/GetTexDataAsRGBA32()/Build().
- Use C++11 u8"my text" syntax to encode literal strings as UTF-8. e.g.:
u8"hello"
u8"こんにちは" // this will be encoded as UTF-8
- If you want to include a backslash \ character in your string literal, you need to double them e.g. "folder\\filename".
- Please use the Discourse forum (https://discourse.dearimgui.org) and not the Github issue tracker for basic font loading questions.
---------------------------------------
USING ICONS
---------------------------------------
Using an icon font (such as FontAwesome: http://fontawesome.io or OpenFontIcons. https://github.com/traverseda/OpenFontIcons)
is an easy and practical way to use icons in your Dear ImGui application.
A common pattern is to merge the icon font within your main font, so you can embed icons directly from your strings without
having to change fonts back and forth.
To refer to the icon UTF-8 codepoints from your C++ code, you may use those headers files created by Juliette Foucaut:
https://github.com/juliettef/IconFontCppHeaders
The C++11 version of those files uses the u8"" utf-8 encoding syntax + \u
#define ICON_FA_SEARCH u8"\uf002"
The pre-C++11 version has the values directly encoded as utf-8:
#define ICON_FA_SEARCH "\xEF\x80\x82"
Example Setup:
// Merge icons into default tool font
#include "IconsFontAwesome.h"
ImGuiIO& io = ImGui::GetIO();
io.Fonts->AddFontDefault();
ImFontConfig config;
config.MergeMode = true;
config.GlyphMinAdvanceX = 13.0f; // Use if you want to make the icon monospaced
static const ImWchar icon_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };
io.Fonts->AddFontFromFileTTF("fonts/fontawesome-webfont.ttf", 13.0f, &config, icon_ranges);
Example Usage:
// Usage, e.g.
ImGui::Text("%s among %d items", ICON_FA_SEARCH, count);
ImGui::Button(ICON_FA_SEARCH " Search");
// C string _literals_ can be concatenated at compilation time, e.g. "hello" " world"
// ICON_FA_SEARCH is defined as a string literal so this is the same as "A" "B" becoming "AB"
See Links below for other icons fonts and related tools.
---------------------------------------
FONTS LOADING INSTRUCTIONS
---------------------------------------
Load default font:
ImGuiIO& io = ImGui::GetIO();
io.Fonts->AddFontDefault();
Load .TTF/.OTF file with:
ImGuiIO& io = ImGui::GetIO();
ImFont* font1 = io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels);
ImFont* font2 = io.Fonts->AddFontFromFileTTF("anotherfont.otf", size_pixels);
// Select font at runtime
ImGui::Text("Hello"); // use the default font (which is the first loaded font)
ImGui::PushFont(font2);
ImGui::Text("Hello with another font");
ImGui::PopFont();
For advanced options create a ImFontConfig structure and pass it to the AddFont function (it will be copied internally):
ImFontConfig config;
config.OversampleH = 2;
config.OversampleV = 1;
config.GlyphExtraSpacing.x = 1.0f;
ImFont* font = io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels, &config);
Read about oversampling here:
https://github.com/nothings/stb/blob/master/tests/oversample
If you have very large number of glyphs or multiple fonts, the texture may become too big for your graphics API.
The typical result of failing to upload a texture is if every glyphs appears as white rectangles.
In particular, using a large range such as GetGlyphRangesChineseSimplifiedCommon() is not recommended unless you
set OversampleH/OversampleV to 1 and use a small font size.
Mind the fact that some graphics drivers have texture size limitation.
If you are building a PC application, mind the fact that your users may use hardware with lower limitations than yours.
Some solutions:
- 1) Reduce glyphs ranges by calculating them from source localization data.
You can use ImFontGlyphRangesBuilder for this purpose, this will be the biggest win!
- 2) You may reduce oversampling, e.g. config.OversampleH = config.OversampleV = 1, this will largely reduce your texture size.
- 3) Set io.Fonts.TexDesiredWidth to specify a texture width to minimize texture height (see comment in ImFontAtlas::Build function).
- 4) Set io.Fonts.Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight; to disable rounding the texture height to the next power of two.
Combine two fonts into one:
// Load a first font
ImFont* font = io.Fonts->AddFontDefault();
// Add character ranges and merge into the previous font
// The ranges array is not copied by the AddFont* functions and is used lazily
// so ensure it is available at the time of building or calling GetTexDataAsRGBA32().
static const ImWchar icons_ranges[] = { 0xf000, 0xf3ff, 0 }; // Will not be copied by AddFont* so keep in scope.
ImFontConfig config;
config.MergeMode = true;
io.Fonts->AddFontFromFileTTF("DroidSans.ttf", 18.0f, &config, io.Fonts->GetGlyphRangesJapanese());
io.Fonts->AddFontFromFileTTF("fontawesome-webfont.ttf", 18.0f, &config, icons_ranges);
io.Fonts->Build();
Add a fourth parameter to bake specific font ranges only:
// Basic Latin, Extended Latin
io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels, NULL, io.Fonts->GetGlyphRangesDefault());
// Default + Selection of 2500 Ideographs used by Simplified Chinese
io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels, NULL, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
// Default + Hiragana, Katakana, Half-Width, Selection of 1946 Ideographs
io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels, NULL, io.Fonts->GetGlyphRangesJapanese());
See "BUILDING CUSTOM GLYPH RANGES" section to create your own ranges.
Offset font vertically by altering the io.Font->DisplayOffset value:
ImFont* font = io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels);
font->DisplayOffset.y = 1; // Render 1 pixel down
---------------------------------------
FREETYPE RASTERIZER, SMALL FONT SIZES
---------------------------------------
Dear ImGui uses imstb_truetype.h to rasterize fonts (with optional oversampling).
This technique and its implementation are not ideal for fonts rendered at _small sizes_, which may appear a
little blurry or hard to read.
There is an implementation of the ImFontAtlas builder using FreeType that you can use in the misc/freetype/ folder.
FreeType supports auto-hinting which tends to improve the readability of small fonts.
Note that this code currently creates textures that are unoptimally too large (could be fixed with some work).
Also note that correct sRGB space blending will have an important effect on your font rendering quality.
---------------------------------------
BUILDING CUSTOM GLYPH RANGES
---------------------------------------
You can use the ImFontGlyphRangesBuilder helper to create glyph ranges based on text input.
For example: for a game where your script is known, if you can feed your entire script to it and only build the characters the game needs.
ImVector<ImWchar> ranges;
ImFontGlyphRangesBuilder builder;
builder.AddText("Hello world"); // Add a string (here "Hello world" contains 7 unique characters)
builder.AddChar(0x7262); // Add a specific character
builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges
builder.BuildRanges(&ranges); // Build the final result (ordered ranges with all the unique characters submitted)
io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, ranges.Data);
io.Fonts->Build(); // Build the atlas while 'ranges' is still in scope and not deleted.
---------------------------------------
EMBEDDING FONTS IN SOURCE CODE
---------------------------------------
Compile and use 'binary_to_compressed_c.cpp' to create a compressed C style array that you can embed in source code.
See the documentation in binary_to_compressed_c.cpp for instruction on how to use the tool.
You may find a precompiled version binary_to_compressed_c.exe for Windows instead of demo binaries package (see README).
The tool can optionally output Base85 encoding to reduce the size of _source code_ but the read-only arrays in the
actual binary will be about 20% bigger.
Then load the font with:
ImFont* font = io.Fonts->AddFontFromMemoryCompressedTTF(compressed_data, compressed_data_size, size_pixels, ...);
or:
ImFont* font = io.Fonts->AddFontFromMemoryCompressedBase85TTF(compressed_data_base85, size_pixels, ...);
---------------------------------------
CREDITS/LICENSES FOR FONTS INCLUDED IN THIS FOLDER
---------------------------------------
Roboto-Medium.ttf
Apache License 2.0
by Christian Robertson
https://fonts.google.com/specimen/Roboto
Cousine-Regular.ttf
by Steve Matteson
Digitized data copyright (c) 2010 Google Corporation.
Licensed under the SIL Open Font License, Version 1.1
https://fonts.google.com/specimen/Cousine
DroidSans.ttf
Copyright (c) Steve Matteson
Apache License, version 2.0
https://www.fontsquirrel.com/fonts/droid-sans
ProggyClean.ttf
Copyright (c) 2004, 2005 Tristan Grimmer
MIT License
recommended loading setting in ImGui: Size = 13.0, DisplayOffset.Y = +1
http://www.proggyfonts.net/
ProggyTiny.ttf
Copyright (c) 2004, 2005 Tristan Grimmer
MIT License
recommended loading setting in ImGui: Size = 10.0, DisplayOffset.Y = +1
http://www.proggyfonts.net/
Karla-Regular.ttf
Copyright (c) 2012, Jonathan Pinhorn
SIL OPEN FONT LICENSE Version 1.1
---------------------------------------
FONTS LINKS
---------------------------------------
ICON FONTS
C/C++ header for icon fonts (#define with code points to use in source code string literals)
https://github.com/juliettef/IconFontCppHeaders
FontAwesome
https://fortawesome.github.io/Font-Awesome
OpenFontIcons
https://github.com/traverseda/OpenFontIcons
Google Icon Fonts
https://design.google.com/icons/
Kenney Icon Font (Game Controller Icons)
https://github.com/nicodinh/kenney-icon-font
IcoMoon - Custom Icon font builder
https://icomoon.io/app
REGULAR FONTS
Google Noto Fonts (worldwide languages)
https://www.google.com/get/noto/
Open Sans Fonts
https://fonts.google.com/specimen/Open+Sans
(Japanese) M+ fonts by Coji Morishita are free
http://mplus-fonts.sourceforge.jp/mplus-outline-fonts/index-en.html
MONOSPACE FONTS
(Pixel Perfect) Proggy Fonts, by Tristan Grimmer
http://www.proggyfonts.net or http://upperbounds.net
(Pixel Perfect) Sweet16, Sweet16 Mono, by Martin Sedlak (Latin + Supplemental + Extended A)
https://github.com/kmar/Sweet16Font
Also include .inl file to use directly in dear imgui.
Google Noto Mono Fonts
https://www.google.com/get/noto/
Typefaces for source code beautification
https://github.com/chrissimpkins/codeface
Programmation fonts
http://s9w.github.io/font_compare/
Inconsolata
http://www.levien.com/type/myfonts/inconsolata.html
Adobe Source Code Pro: Monospaced font family for user interface and coding environments
https://github.com/adobe-fonts/source-code-pro
Monospace/Fixed Width Programmer's Fonts
http://www.lowing.org/fonts/
Or use Arial Unicode or other Unicode fonts provided with Windows for full characters coverage (not sure of their licensing).
@@ -1,131 +0,0 @@
# imgui_freetype
Build font atlases using FreeType instead of stb_truetype (the default imgui's font rasterizer).
<br>by @vuhdo, @mikesart, @ocornut.
### Usage
1. Get latest FreeType binaries or build yourself (under Windows you may use vcpkg with `vcpkg install freetype`).
2. Add imgui_freetype.h/cpp alongside your imgui sources.
3. Include imgui_freetype.h after imgui.h.
4. Call `ImGuiFreeType::BuildFontAtlas()` *BEFORE* calling `ImFontAtlas::GetTexDataAsRGBA32()` or `ImFontAtlas::Build()` (so normal Build() won't be called):
```cpp
// See ImGuiFreeType::RasterizationFlags
unsigned int flags = ImGuiFreeType::NoHinting;
ImGuiFreeType::BuildFontAtlas(io.Fonts, flags);
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
```
### Gamma Correct Blending
FreeType assumes blending in linear space rather than gamma space.
See FreeType note for [FT_Render_Glyph](https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph).
For correct results you need to be using sRGB and convert to linear space in the pixel shader output.
The default imgui styles will be impacted by this change (alpha values will need tweaking).
### Test code Usage
```cpp
#include "misc/freetype/imgui_freetype.h"
#include "misc/freetype/imgui_freetype.cpp"
// Load various small fonts
ImGuiIO& io = ImGui::GetIO();
io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 13.0f);
io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 13.0f);
io.Fonts->AddFontDefault();
FreeTypeTest freetype_test;
// Main Loop
while (true)
{
if (freetype_test.UpdateRebuild())
{
// REUPLOAD FONT TEXTURE TO GPU
ImGui_ImplXXX_DestroyDeviceObjects();
ImGui_ImplXXX_CreateDeviceObjects();
}
ImGui::NewFrame();
freetype_test.ShowFreetypeOptionsWindow();
...
}
```
### Test code
```cpp
#include "misc/freetype/imgui_freetype.h"
#include "misc/freetype/imgui_freetype.cpp"
struct FreeTypeTest
{
enum FontBuildMode
{
FontBuildMode_FreeType,
FontBuildMode_Stb
};
FontBuildMode BuildMode;
bool WantRebuild;
float FontsMultiply;
int FontsPadding;
unsigned int FontsFlags;
FreeTypeTest()
{
BuildMode = FontBuildMode_FreeType;
WantRebuild = true;
FontsMultiply = 1.0f;
FontsPadding = 1;
FontsFlags = 0;
}
// Call _BEFORE_ NewFrame()
bool UpdateRebuild()
{
if (!WantRebuild)
return false;
ImGuiIO& io = ImGui::GetIO();
io.Fonts->TexGlyphPadding = FontsPadding;
for (int n = 0; n < io.Fonts->ConfigData.Size; n++)
{
ImFontConfig* font_config = (ImFontConfig*)&io.Fonts->ConfigData[n];
font_config->RasterizerMultiply = FontsMultiply;
font_config->RasterizerFlags = (BuildMode == FontBuildMode_FreeType) ? FontsFlags : 0x00;
}
if (BuildMode == FontBuildMode_FreeType)
ImGuiFreeType::BuildFontAtlas(io.Fonts, FontsFlags);
else if (BuildMode == FontBuildMode_Stb)
io.Fonts->Build();
WantRebuild = false;
return true;
}
// Call to draw interface
void ShowFreetypeOptionsWindow()
{
ImGui::Begin("FreeType Options");
ImGui::ShowFontSelector("Fonts");
WantRebuild |= ImGui::RadioButton("FreeType", (int*)&BuildMode, FontBuildMode_FreeType);
ImGui::SameLine();
WantRebuild |= ImGui::RadioButton("Stb (Default)", (int*)&BuildMode, FontBuildMode_Stb);
WantRebuild |= ImGui::DragFloat("Multiply", &FontsMultiply, 0.001f, 0.0f, 2.0f);
WantRebuild |= ImGui::DragInt("Padding", &FontsPadding, 0.1f, 0, 16);
if (BuildMode == FontBuildMode_FreeType)
{
WantRebuild |= ImGui::CheckboxFlags("NoHinting", &FontsFlags, ImGuiFreeType::NoHinting);
WantRebuild |= ImGui::CheckboxFlags("NoAutoHint", &FontsFlags, ImGuiFreeType::NoAutoHint);
WantRebuild |= ImGui::CheckboxFlags("ForceAutoHint", &FontsFlags, ImGuiFreeType::ForceAutoHint);
WantRebuild |= ImGui::CheckboxFlags("LightHinting", &FontsFlags, ImGuiFreeType::LightHinting);
WantRebuild |= ImGui::CheckboxFlags("MonoHinting", &FontsFlags, ImGuiFreeType::MonoHinting);
WantRebuild |= ImGui::CheckboxFlags("Bold", &FontsFlags, ImGuiFreeType::Bold);
WantRebuild |= ImGui::CheckboxFlags("Oblique", &FontsFlags, ImGuiFreeType::Oblique);
}
ImGui::End();
}
};
```
### Known issues
- `cfg.OversampleH`, `OversampleV` are ignored (but perhaps not so necessary with this rasterizer).
@@ -1,35 +0,0 @@
// Wrapper to use FreeType (instead of stb_truetype) for Dear ImGui
// Get latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype
// Original code by @Vuhdo (Aleksei Skriabin), maintained by @ocornut
#pragma once
#include "imgui.h" // IMGUI_API, ImFontAtlas
namespace ImGuiFreeType
{
// Hinting greatly impacts visuals (and glyph sizes).
// When disabled, FreeType generates blurrier glyphs, more or less matches the stb's output.
// The Default hinting mode usually looks good, but may distort glyphs in an unusual way.
// The Light hinting mode generates fuzzier glyphs but better matches Microsoft's rasterizer.
// You can set those flags on a per font basis in ImFontConfig::RasterizerFlags.
// Use the 'extra_flags' parameter of BuildFontAtlas() to force a flag on all your fonts.
enum RasterizerFlags
{
// By default, hinting is enabled and the font's native hinter is preferred over the auto-hinter.
NoHinting = 1 << 0, // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes.
NoAutoHint = 1 << 1, // Disable auto-hinter.
ForceAutoHint = 1 << 2, // Indicates that the auto-hinter is preferred over the font's native hinter.
LightHinting = 1 << 3, // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text.
MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output.
Bold = 1 << 5, // Styling: Should we artificially embolden the font?
Oblique = 1 << 6 // Styling: Should we slant the font, emulating italic style?
};
IMGUI_API bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int extra_flags = 0);
// By default ImGuiFreeType will use IM_ALLOC()/IM_FREE().
// However, as FreeType does lots of allocations we provide a way for the user to redirect it to a separate memory heap if desired:
IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data = NULL);
}
@@ -1,4 +0,0 @@
Natvis file to describe dear imgui types in the Visual Studio debugger.
With this, types like ImVector<> will be displayed nicely in the debugger.
You can include this file a Visual Studio project file, or install it in Visual Studio folder.
+24
View File
@@ -0,0 +1,24 @@
# See http://editorconfig.org to read about the EditorConfig format.
# - In theory automatically supported by VS2017+ and most common IDE or text editors.
# - In practice VS2019 stills gets trailing whitespaces wrong :(
# - Suggest install to trim whitespaces: https://marketplace.visualstudio.com/items?itemName=MadsKristensen.TrailingWhitespaceVisualizer
# - Alternative for older VS2010 to VS2015: https://marketplace.visualstudio.com/items?itemName=EditorConfigTeam.EditorConfig
# top-most EditorConfig file
root = true
# Default settings:
# Use 4 spaces as indentation
[*]
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
[imstb_*]
indent_size = 3
trim_trailing_whitespace = false
[Makefile]
indent_style = tab
indent_size = 4
@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2014-2019 Omar Cornut
Copyright (c) 2014-2021 Omar Cornut
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+128
View File
@@ -0,0 +1,128 @@
//-----------------------------------------------------------------------------
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
//-----------------------------------------------------------------------------
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it)
// B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template.
//-----------------------------------------------------------------------------
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
//-----------------------------------------------------------------------------
#pragma once
// Include Platform Def to get mutliplatform AZ_DLL_IMPORT and AZ_DLL_EXPORT to use below
#include <AzCore/PlatformDef.h>
//---- Define assertion handler. Defaults to calling assert().
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
//#define IMGUI_API __declspec( dllexport )
//#define IMGUI_API __declspec( dllimport )
#ifdef IMGUI_API_IMPORT
# define IMGUI_API AZ_DLL_IMPORT
#else
# define IMGUI_API AZ_DLL_EXPORT
#endif // IMGUI_API_IMPORT
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//---- Disable all of Dear ImGui or don't implement standard windows.
// It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp.
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended.
//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger window: ShowMetricsWindow() will be empty.
//---- Don't implement some functions to reduce linkage requirements.
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow. (imm32.lib/.a)
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
//---- Include imgui_user.h at the end of imgui.h as a convenience
//#define IMGUI_INCLUDE_IMGUI_USER_H
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
//#define IMGUI_USE_BGRA_PACKED_COLOR
//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
//#define IMGUI_USE_WCHAR32
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
//---- Use stb_printf's faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
// Requires 'stb_sprintf.h' to be available in the include path. Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by STB sprintf.
// #define IMGUI_USE_STB_SPRINTF
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
// On Windows you may use vcpkg with 'vcpkg install freetype' + 'vcpkg integrate install'.
//#define IMGUI_ENABLE_FREETYPE
//---- Use stb_truetype to build and rasterize the font atlas (default)
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
//#define IMGUI_ENABLE_STB_TRUETYPE
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
/*
#define IM_VEC2_CLASS_EXTRA \
ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \
operator MyVec2() const { return MyVec2(x,y); }
#define IM_VEC4_CLASS_EXTRA \
ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \
operator MyVec4() const { return MyVec4(x,y,z,w); }
*/
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
//#define ImDrawIdx unsigned int
//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly)
//struct ImDrawList;
//struct ImDrawCmd;
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
//#define ImDrawCallback MyImDrawCallback
//---- Debug Tools: Macro to break in Debugger
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
//#define IM_DEBUG_BREAK IM_ASSERT(0)
//#define IM_DEBUG_BREAK __debugbreak()
//---- Debug Tools: Have the Item Picker break in the ItemAdd() function instead of ItemHoverable(),
// (which comes earlier in the code, will catch a few extra items, allow picking items other than Hovered one.)
// This adds a small runtime cost which is why it is not enabled by default.
//#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
//---- Debug Tools: Enable slower asserts
//#define IMGUI_DEBUG_PARANOID
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
/*
namespace ImGui
{
void MyFunction(const char* name, const MyMatrix44& v);
}
*/
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,10 +1,10 @@
// [DEAR IMGUI]
// This is a slightly modified version of stb_rect_pack.h 0.99.
// [DEAR IMGUI]
// This is a slightly modified version of stb_rect_pack.h 1.00.
// Those changes would need to be pushed into nothings/stb:
// - Added STBRP__CDECL
// Grep for [DEAR IMGUI] to find the changes.
// stb_rect_pack.h - v0.99 - public domain - rectangle packing
// stb_rect_pack.h - v1.00 - public domain - rectangle packing
// Sean Barrett 2014
//
// Useful for e.g. packing rectangular textures into an atlas.
@@ -37,9 +37,11 @@
//
// Bugfixes / warning fixes
// Jeremy Jaussaud
// Fabian Giesen
//
// Version history:
//
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
// 0.99 (2019-02-07) warning fixes
// 0.11 (2017-03-03) return packing success/fail result
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
@@ -357,6 +359,13 @@ static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int widt
width -= width % c->align;
STBRP_ASSERT(width % c->align == 0);
// if it can't possibly fit, bail immediately
if (width > c->width || height > c->height) {
fr.prev_link = NULL;
fr.x = fr.y = 0;
return fr;
}
node = c->active_head;
prev = &c->active_head;
while (node->x + width <= c->width) {
@@ -420,7 +429,7 @@ static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int widt
}
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
if (y + height < c->height) {
if (y + height <= c->height) {
if (y <= best_y) {
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
best_x = xpos;
@@ -1,4 +1,4 @@
// [DEAR IMGUI]
// [DEAR IMGUI]
// This is a slightly modified version of stb_textedit.h 1.13.
// Those changes would need to be pushed into nothings/stb:
// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
@@ -148,6 +148,8 @@
// STB_TEXTEDIT_K_RIGHT keyboard input to move cursor right
// STB_TEXTEDIT_K_UP keyboard input to move cursor up
// STB_TEXTEDIT_K_DOWN keyboard input to move cursor down
// STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page
// STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page
// STB_TEXTEDIT_K_LINESTART keyboard input to move cursor to start of line // e.g. HOME
// STB_TEXTEDIT_K_LINEEND keyboard input to move cursor to end of line // e.g. END
// STB_TEXTEDIT_K_TEXTSTART keyboard input to move cursor to start of text // e.g. ctrl-HOME
@@ -170,14 +172,10 @@
// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text
// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text
//
// Todo:
// STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page
// STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page
//
// Keyboard input must be encoded as a single integer value; e.g. a character code
// and some bitflags that represent shift states. to simplify the interface, SHIFT must
// be a bitflag, so we can test the shifted state of cursor movements to allow selection,
// i.e. (STB_TEXTED_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow.
// i.e. (STB_TEXTEDIT_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow.
//
// You can encode other things, such as CONTROL or ALT, in additional bits, and
// then test for their presence in e.g. STB_TEXTEDIT_K_WORDLEFT. For example,
@@ -337,6 +335,10 @@ typedef struct
// each textfield keeps its own insert mode state. to keep an app-wide
// insert mode, copy this value in/out of the app state
int row_count_per_page;
// page size in number of row.
// this value MUST be set to >0 for pageup or pagedown in multilines documents.
/////////////////////
//
// private data
@@ -855,12 +857,16 @@ retry:
break;
case STB_TEXTEDIT_K_DOWN:
case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT: {
case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT:
case STB_TEXTEDIT_K_PGDOWN:
case STB_TEXTEDIT_K_PGDOWN | STB_TEXTEDIT_K_SHIFT: {
StbFindState find;
StbTexteditRow row;
int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0;
int i, j, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0;
int is_page = (key & ~STB_TEXTEDIT_K_SHIFT) == STB_TEXTEDIT_K_PGDOWN;
int row_count = is_page ? state->row_count_per_page : 1;
if (state->single_line) {
if (!is_page && state->single_line) {
// on windows, up&down in single-line behave like left&right
key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT);
goto retry;
@@ -869,17 +875,25 @@ retry:
if (sel)
stb_textedit_prep_selection_at_cursor(state);
else if (STB_TEXT_HAS_SELECTION(state))
stb_textedit_move_to_last(str,state);
stb_textedit_move_to_last(str, state);
// compute current position of cursor point
stb_textedit_clamp(str, state);
stb_textedit_find_charpos(&find, str, state->cursor, state->single_line);
// now find character position down a row
if (find.length) {
float goal_x = state->has_preferred_x ? state->preferred_x : find.x;
float x;
for (j = 0; j < row_count; ++j) {
float x, goal_x = state->has_preferred_x ? state->preferred_x : find.x;
int start = find.first_char + find.length;
if (find.length == 0)
break;
// [DEAR IMGUI]
// going down while being on the last line shouldn't bring us to that line end
if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE)
break;
// now find character position down a row
state->cursor = start;
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
x = row.x0;
@@ -901,17 +915,25 @@ retry:
if (sel)
state->select_end = state->cursor;
// go to next line
find.first_char = find.first_char + find.length;
find.length = row.num_chars;
}
break;
}
case STB_TEXTEDIT_K_UP:
case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: {
case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT:
case STB_TEXTEDIT_K_PGUP:
case STB_TEXTEDIT_K_PGUP | STB_TEXTEDIT_K_SHIFT: {
StbFindState find;
StbTexteditRow row;
int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0;
int i, j, prev_scan, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0;
int is_page = (key & ~STB_TEXTEDIT_K_SHIFT) == STB_TEXTEDIT_K_PGUP;
int row_count = is_page ? state->row_count_per_page : 1;
if (state->single_line) {
if (!is_page && state->single_line) {
// on windows, up&down become left&right
key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT);
goto retry;
@@ -926,11 +948,14 @@ retry:
stb_textedit_clamp(str, state);
stb_textedit_find_charpos(&find, str, state->cursor, state->single_line);
// can only go up if there's a previous row
if (find.prev_first != find.first_char) {
for (j = 0; j < row_count; ++j) {
float x, goal_x = state->has_preferred_x ? state->preferred_x : find.x;
// can only go up if there's a previous row
if (find.prev_first == find.first_char)
break;
// now find character position up a row
float goal_x = state->has_preferred_x ? state->preferred_x : find.x;
float x;
state->cursor = find.prev_first;
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
x = row.x0;
@@ -952,6 +977,14 @@ retry:
if (sel)
state->select_end = state->cursor;
// go to previous line
// (we need to scan previous line the hard way. maybe we could expose this as a new API function?)
prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0;
while (prev_scan > 0 && STB_TEXTEDIT_GETCHAR(str, prev_scan - 1) != STB_TEXTEDIT_NEWLINE)
--prev_scan;
find.first_char = find.prev_first;
find.prev_first = prev_scan;
}
break;
}
@@ -1075,10 +1108,6 @@ retry:
state->has_preferred_x = 0;
break;
}
// @TODO:
// STB_TEXTEDIT_K_PGUP - move cursor up a page
// STB_TEXTEDIT_K_PGDOWN - move cursor down a page
}
}
@@ -1134,7 +1163,7 @@ static void stb_textedit_discard_redo(StbUndoState *state)
state->undo_rec[i].char_storage += n;
}
// now move all the redo records towards the end of the buffer; the first one is at 'redo_point'
// {DEAR IMGUI]
// [DEAR IMGUI]
size_t move_size = (size_t)((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));
const char* buf_begin = (char*)state->undo_rec; (void)buf_begin;
const char* buf_end = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end;
@@ -1350,6 +1379,7 @@ static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_lin
state->initialized = 1;
state->single_line = (unsigned char) is_single_line;
state->insert_mode = 0;
state->row_count_per_page = 0;
}
// API initialize
@@ -1,4 +1,4 @@
// [DEAR IMGUI]
// [DEAR IMGUI]
// This is a slightly modified version of stb_truetype.h 1.20.
// Mostly fixing for compiler and static analyzer warnings.
// Grep for [DEAR IMGUI] to find the changes.
@@ -2538,11 +2538,11 @@ static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, i
// There are no other cases.
STBTT_assert(0);
break;
};
} // [DEAR IMGUI] removed ;
}
}
break;
};
} // [DEAR IMGUI] removed ;
default:
// TODO: Implement other stuff.
@@ -4132,7 +4132,7 @@ STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect
STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges)
{
stbtt_fontinfo info;
int i,j,n, return_value = 1;
int i,j,n, return_value; // [DEAR IMGUI] removed = 1
//stbrp_context *context = (stbrp_context *) spc->pack_info;
stbrp_rect *rects;
@@ -4302,7 +4302,7 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex
int winding = 0;
orig[0] = x;
//orig[1] = y; // [DEAR IMGUI] commmented double assignment
//orig[1] = y; // [DEAR IMGUI] commented double assignment
// make sure y never passes through a vertex of the shape
y_frac = (float) STBTT_fmod(y, 1.0f);
@@ -4374,32 +4374,32 @@ static float stbtt__cuberoot( float x )
// x^3 + c*x^2 + b*x + a = 0
static int stbtt__solve_cubic(float a, float b, float c, float* r)
{
float s = -a / 3;
float p = b - a*a / 3;
float q = a * (2*a*a - 9*b) / 27 + c;
float s = -a / 3;
float p = b - a*a / 3;
float q = a * (2*a*a - 9*b) / 27 + c;
float p3 = p*p*p;
float d = q*q + 4*p3 / 27;
if (d >= 0) {
float z = (float) STBTT_sqrt(d);
float u = (-q + z) / 2;
float v = (-q - z) / 2;
u = stbtt__cuberoot(u);
v = stbtt__cuberoot(v);
r[0] = s + u + v;
return 1;
} else {
float u = (float) STBTT_sqrt(-p/3);
float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative
float m = (float) STBTT_cos(v);
float d = q*q + 4*p3 / 27;
if (d >= 0) {
float z = (float) STBTT_sqrt(d);
float u = (-q + z) / 2;
float v = (-q - z) / 2;
u = stbtt__cuberoot(u);
v = stbtt__cuberoot(v);
r[0] = s + u + v;
return 1;
} else {
float u = (float) STBTT_sqrt(-p/3);
float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative
float m = (float) STBTT_cos(v);
float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f;
r[0] = s + u * 2 * m;
r[1] = s - u * (m + n);
r[2] = s - u * (m - n);
r[0] = s + u * 2 * m;
r[1] = s - u * (m + n);
r[2] = s - u * (m - n);
//STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe?
//STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f);
//STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f);
return 3;
return 3;
}
}
@@ -3,6 +3,10 @@ misc/cpp/
InputText() wrappers for C++ standard library (STL) type: std::string.
This is also an example of how you may wrap your own similar types.
misc/debuggers/
Helper files for popular debuggers.
With the .natvis file, types like ImVector<> will be displayed nicely in Visual Studio debugger.
misc/fonts/
Fonts loading/merging instructions (e.g. How to handle glyph ranges, how to merge icons fonts).
Command line tool "binary_to_compressed_c" to create compressed arrays to embed data in source code.
@@ -12,7 +16,8 @@ misc/freetype/
Font atlas builder/rasterizer using FreeType instead of stb_truetype.
Benefit from better FreeType rasterization, in particular for small fonts.
misc/natvis/
Natvis file to describe dear imgui types in the Visual Studio debugger.
With this, types like ImVector<> will be displayed nicely in the debugger.
You can include this file a Visual Studio project file, or install it in Visual Studio folder.
misc/single_file/
Single-file header stub.
We use this to validate compiling all *.cpp files in a same compilation unit.
Users of that technique (also called "Unity builds") can generally provide this themselves,
so we don't really recommend you use this in your projects.
@@ -5,6 +5,6 @@ imgui_stdlib.h + imgui_stdlib.cpp
imgui_scoped.h
[Experimental, not currently in main repository]
Additional header file with some RAII-style wrappers for common ImGui functions.
Additional header file with some RAII-style wrappers for common Dear ImGui functions.
Try by merging: https://github.com/ocornut/imgui/pull/2197
Discuss at: https://github.com/ocornut/imgui/issues/2096
@@ -1,5 +1,4 @@
// imgui_stdlib.cpp
// Wrappers for C++ standard library (STL) types (std::string, etc.)
// dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.)
// This is also an example of how you may wrap your own similar types.
// Compatibility:
@@ -1,5 +1,4 @@
// imgui_stdlib.h
// Wrappers for C++ standard library (STL) types (std::string, etc.)
// dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.)
// This is also an example of how you may wrap your own similar types.
// Compatibility:
@@ -0,0 +1,16 @@
HELPER FILES FOR POPULAR DEBUGGERS
imgui.gdb
GDB: disable stepping into trivial functions.
(read comments inside file for details)
imgui.natstepfilter
Visual Studio Debugger: disable stepping into trivial functions.
(read comments inside file for details)
imgui.natvis
Visual Studio Debugger: describe Dear ImGui types for better display.
With this, types like ImVector<> will be displayed nicely in the debugger.
(read comments inside file for details)
@@ -0,0 +1,12 @@
# GDB configuration to aid debugging experience
# To enable these customizations edit $HOME/.gdbinit (or ./.gdbinit if local gdbinit is enabled) and add:
# add-auto-load-safe-path /path/to/imgui.gdb
# source /path/to/imgui.gdb
#
# More Information at:
# * https://sourceware.org/gdb/current/onlinedocs/gdb/gdbinit-man.html
# * https://sourceware.org/gdb/current/onlinedocs/gdb/Init-File-in-the-Current-Directory.html#Init-File-in-the-Current-Directory
# Disable stepping into trivial functions
skip -rfunction Im(Vec2|Vec4|Strv|Vector|Span)::.+
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
.natstepfilter file for Visual Studio debugger.
Purpose: instruct debugger to skip some functions when using StepInto (F11)
To enable:
* copy in %USERPROFILE%\Documents\Visual Studio XXXX\Visualizers (current user)
* or copy in %VsInstallDirectory%\Common7\Packages\Debugger\Visualizers (all users)
If you have multiple VS version installed, the version that matters is the one you are using the IDE/debugger of (not the compiling toolset).
This is supported since Visual Studio 2012.
Unfortunately, unlike .natvis files, it isn't yet possible to include this file in your project :(
You may upvote this: https://developercommunity.visualstudio.com/t/allow-natstepfilter-and-natjmc-to-be-included-as-p/561718
More information at: https://docs.microsoft.com/en-us/visualstudio/debugger/just-my-code?view=vs-2019#BKMK_C___Just_My_Code
-->
<StepFilter xmlns="http://schemas.microsoft.com/vstudio/debugger/natstepfilter/2010">
<!-- Disable stepping into trivial functions -->
<Function>
<Name>(ImVec2|ImVec4|ImStrv)::.+</Name>
<Action>NoStepInto</Action>
</Function>
<Function>
<Name>(ImVector|ImSpan).*::operator.+</Name>
<Action>NoStepInto</Action>
</Function>
</StepFilter>
@@ -1,6 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
.natvis file for Visual Studio debugger.
Purpose: provide nicer views on data types used by Dear ImGui.
<!-- natvis file for Visual Studio debugger (you can include this in a project file, or install in visual studio folder) -->
To enable:
* include file in your VS project (most recommended: not intrusive and always kept up to date!)
* or copy in %USERPROFILE%\Documents\Visual Studio XXXX\Visualizers (current user)
* or copy in %VsInstallDirectory%\Common7\Packages\Debugger\Visualizers (all users)
More information at: https://docs.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects?view=vs-2019
-->
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
@@ -14,6 +23,16 @@
</Expand>
</Type>
<Type Name="ImSpan&lt;*&gt;">
<DisplayString>{{Size={DataEnd-Data} }}</DisplayString>
<Expand>
<ArrayItems>
<Size>DataEnd-Data</Size>
<ValuePointer>Data</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="ImVec2">
<DisplayString>{{x={x,g} y={y,g}}}</DisplayString>
</Type>
@@ -35,5 +54,5 @@
<Type Name="ImGuiWindow">
<DisplayString>{{Name {Name,s} Active {(Active||WasActive)?1:0,d} Child {(Flags &amp; 0x01000000)?1:0,d} Popup {(Flags &amp; 0x04000000)?1:0,d} Hidden {(Hidden)?1:0,d}}</DisplayString>
</Type>
</AutoVisualizer>
</AutoVisualizer>
@@ -1,15 +1,17 @@
// ImGui - binary_to_compressed_c.cpp
// dear imgui
// (binary_to_compressed_c.cpp)
// Helper tool to turn a file into a C array, if you want to embed font data in your source code.
// The data is first compressed with stb_compress() to reduce source code size,
// then encoded in Base85 to fit in a string so we can fit roughly 4 bytes of compressed data into 5 bytes of source code (suggested by @mmalex)
// (If we used 32-bits constants it would require take 11 bytes of source code to encode 4 bytes, and be endianness dependent)
// (If we used 32-bit constants it would require take 11 bytes of source code to encode 4 bytes, and be endianness dependent)
// Note that even with compression, the output array is likely to be bigger than the binary file..
// Load compressed TTF fonts with ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF()
// Build with, e.g:
// # cl.exe binary_to_compressed_c.cpp
// # gcc binary_to_compressed_c.cpp
// # g++ binary_to_compressed_c.cpp
// # clang++ binary_to_compressed_c.cpp
// You can also find a precompiled Windows binary in the binary/demo package available from https://github.com/ocornut/imgui
// Usage:
@@ -27,7 +29,7 @@
// stb_compress* from stb.h - declaration
typedef unsigned int stb_uint;
typedef unsigned char stb_uchar;
stb_uint stb_compress(stb_uchar *out,stb_uchar *in,stb_uint len);
stb_uint stb_compress(stb_uchar* out, stb_uchar* in, stb_uint len);
static bool binary_to_compressed_c(const char* filename, const char* symbol, bool use_base85_encoding, bool use_compression);
@@ -48,18 +50,21 @@ int main(int argc, char** argv)
else if (strcmp(argv[argn], "-nocompress") == 0) { use_compression = false; argn++; }
else
{
printf("Unknown argument: '%s'\n", argv[argn]);
fprintf(stderr, "Unknown argument: '%s'\n", argv[argn]);
return 1;
}
}
return binary_to_compressed_c(argv[argn], argv[argn+1], use_base85_encoding, use_compression) ? 0 : 1;
bool ret = binary_to_compressed_c(argv[argn], argv[argn + 1], use_base85_encoding, use_compression);
if (!ret)
fprintf(stderr, "Error opening or reading file: '%s'\n", argv[argn]);
return ret ? 0 : 1;
}
char Encode85Byte(unsigned int x)
{
x = (x % 85) + 35;
return (x>='\\') ? x+1 : x;
return (x >= '\\') ? x + 1 : x;
}
bool binary_to_compressed_c(const char* filename, const char* symbol, bool use_base85_encoding, bool use_compression)
@@ -69,7 +74,7 @@ bool binary_to_compressed_c(const char* filename, const char* symbol, bool use_b
if (!f) return false;
int data_sz;
if (fseek(f, 0, SEEK_END) || (data_sz = (int)ftell(f)) == -1 || fseek(f, 0, SEEK_SET)) { fclose(f); return false; }
char* data = new char[data_sz+4];
char* data = new char[data_sz + 4];
if (fread(data, 1, data_sz, f) != (size_t)data_sz) { fclose(f); delete[] data; return false; }
memset((void*)(((char*)data) + data_sz), 0, 4);
fclose(f);
@@ -79,16 +84,16 @@ bool binary_to_compressed_c(const char* filename, const char* symbol, bool use_b
char* compressed = use_compression ? new char[maxlen] : data;
int compressed_sz = use_compression ? stb_compress((stb_uchar*)compressed, (stb_uchar*)data, data_sz) : data_sz;
if (use_compression)
memset(compressed + compressed_sz, 0, maxlen - compressed_sz);
memset(compressed + compressed_sz, 0, maxlen - compressed_sz);
// Output as Base85 encoded
FILE* out = stdout;
fprintf(out, "// File: '%s' (%d bytes)\n", filename, (int)data_sz);
fprintf(out, "// Exported using binary_to_compressed_c.cpp\n");
const char* compressed_str = use_compression ? "compressed_" : "";
const char* compressed_str = use_compression ? "compressed_" : "";
if (use_base85_encoding)
{
fprintf(out, "static const char %s_%sdata_base85[%d+1] =\n \"", symbol, compressed_str, (int)((compressed_sz+3)/4)*5);
fprintf(out, "static const char %s_%sdata_base85[%d+1] =\n \"", symbol, compressed_str, (int)((compressed_sz + 3) / 4)*5);
char prev_c = 0;
for (int src_i = 0; src_i < compressed_sz; src_i += 4)
{
@@ -100,7 +105,7 @@ bool binary_to_compressed_c(const char* filename, const char* symbol, bool use_b
fprintf(out, (c == '?' && prev_c == '?') ? "\\%c" : "%c", c);
prev_c = c;
}
if ((src_i % 112) == 112-4)
if ((src_i % 112) == 112 - 4)
fprintf(out, "\"\n \"");
}
fprintf(out, "\";\n\n");
@@ -108,7 +113,7 @@ bool binary_to_compressed_c(const char* filename, const char* symbol, bool use_b
else
{
fprintf(out, "static const unsigned int %s_%ssize = %d;\n", symbol, compressed_str, (int)compressed_sz);
fprintf(out, "static const unsigned int %s_%sdata[%d/4] =\n{", symbol, compressed_str, (int)((compressed_sz+3)/4)*4);
fprintf(out, "static const unsigned int %s_%sdata[%d/4] =\n{", symbol, compressed_str, (int)((compressed_sz + 3) / 4)*4);
int column = 0;
for (int i = 0; i < compressed_sz; i += 4)
{
@@ -124,7 +129,7 @@ bool binary_to_compressed_c(const char* filename, const char* symbol, bool use_b
// Cleanup
delete[] data;
if (use_compression)
delete[] compressed;
delete[] compressed;
return true;
}
@@ -0,0 +1,30 @@
# imgui_freetype
Build font atlases using FreeType instead of stb_truetype (which is the default font rasterizer).
<br>by @vuhdo, @mikesart, @ocornut.
### Usage
1. Get latest FreeType binaries or build yourself (under Windows you may use vcpkg with `vcpkg install freetype`, `vcpkg integrate install`).
2. Add imgui_freetype.h/cpp alongside your project files.
3. Add `#define IMGUI_ENABLE_FREETYPE` in your [imconfig.h](https://github.com/ocornut/imgui/blob/master/imconfig.h) file
### About Gamma Correct Blending
FreeType assumes blending in linear space rather than gamma space.
See FreeType note for [FT_Render_Glyph](https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph).
For correct results you need to be using sRGB and convert to linear space in the pixel shader output.
The default Dear ImGui styles will be impacted by this change (alpha values will need tweaking).
### Testbed for toying with settings (for developers)
See https://gist.github.com/ocornut/b3a9ecf13502fd818799a452969649ad
### Known issues
- Oversampling settins are ignored but also not so much necessary with the higher quality rendering.
### Comparaison
Small, thin anti-aliased fonts are typically benefiting a lots from Freetype's hinting:
![comparing_font_rasterizers](https://user-images.githubusercontent.com/8225057/107550178-fef87f00-6bd0-11eb-8d09-e2edb2f0ccfc.gif)
@@ -1,23 +1,33 @@
// Wrapper to use FreeType (instead of stb_truetype) for Dear ImGui
// dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder)
// (code)
// Get latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype
// Original code by @vuhdo (Aleksei Skriabin). Improvements by @mikesart. Maintained and v0.60+ by @ocornut.
// Original code by @vuhdo (Aleksei Skriabin). Improvements by @mikesart. Maintained since 2019 by @ocornut.
// Changelog:
// - v0.50: (2017/08/16) imported from https://github.com/Vuhdo/imgui_freetype into http://www.github.com/ocornut/imgui_club, updated for latest changes in ImFontAtlas, minor tweaks.
// - v0.51: (2017/08/26) cleanup, optimizations, support for ImFontConfig::RasterizerFlags, ImFontConfig::RasterizerMultiply.
// - v0.52: (2017/09/26) fixes for imgui internal changes.
// - v0.53: (2017/10/22) minor inconsequential change to match change in master (removed an unnecessary statement).
// - v0.54: (2018/01/22) fix for addition of ImFontAtlas::TexUvscale member.
// - v0.55: (2018/02/04) moved to main imgui repository (away from http://www.github.com/ocornut/imgui_club)
// - v0.56: (2018/06/08) added support for ImFontConfig::GlyphMinAdvanceX, GlyphMaxAdvanceX.
// - v0.60: (2019/01/10) re-factored to match big update in STB builder. fixed texture height waste. fixed redundant glyphs when merging. support for glyph padding.
// - v0.61: (2019/01/15) added support for imgui allocators + added FreeType only override function SetAllocatorFunctions().
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs.
// 2021/03/02: set 'atlas->TexPixelsUseColors = true' to help some backends with deciding of a prefered texture format.
// 2021/01/28: added support for color-layered glyphs via ImGuiFreeTypeBuilderFlags_LoadColor (require Freetype 2.10+).
// 2021/01/26: simplified integration by using '#define IMGUI_ENABLE_FREETYPE'.
// renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. removed ImGuiFreeType::BuildFontAtlas().
// 2020/06/04: fix for rare case where FT_Get_Char_Index() succeed but FT_Load_Glyph() fails.
// 2019/02/09: added RasterizerFlags::Monochrome flag to disable font anti-aliasing (combine with ::MonoHinting for best results!)
// 2019/01/15: added support for imgui allocators + added FreeType only override function SetAllocatorFunctions().
// 2019/01/10: re-factored to match big update in STB builder. fixed texture height waste. fixed redundant glyphs when merging. support for glyph padding.
// 2018/06/08: added support for ImFontConfig::GlyphMinAdvanceX, GlyphMaxAdvanceX.
// 2018/02/04: moved to main imgui repository (away from http://www.github.com/ocornut/imgui_club)
// 2018/01/22: fix for addition of ImFontAtlas::TexUvscale member.
// 2017/10/22: minor inconsequential change to match change in master (removed an unnecessary statement).
// 2017/09/26: fixes for imgui internal changes.
// 2017/08/26: cleanup, optimizations, support for ImFontConfig::RasterizerFlags, ImFontConfig::RasterizerMultiply.
// 2017/08/16: imported from https://github.com/Vuhdo/imgui_freetype into http://www.github.com/ocornut/imgui_club, updated for latest changes in ImFontAtlas, minor tweaks.
// Gamma Correct Blending:
// FreeType assumes blending in linear space rather than gamma space.
// See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph
// For correct results you need to be using sRGB and convert to linear space in the pixel shader output.
// The default imgui styles will be impacted by this change (alpha values will need tweaking).
// About Gamma Correct Blending:
// - FreeType assumes blending in linear space rather than gamma space.
// - See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph
// - For correct results you need to be using sRGB and convert to linear space in the pixel shader output.
// - The default dear imgui styles will be impacted by this change (alpha values will need tweaking).
// FIXME: cfg.OversampleH, OversampleV are not supported (but perhaps not so necessary with this rasterizer).
@@ -35,9 +45,27 @@
#endif
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
#endif
//-------------------------------------------------------------------------
// Data
//-------------------------------------------------------------------------
// Default memory allocators
static void* ImGuiFreeTypeDefaultAllocFunc(size_t size, void* user_data) { IM_UNUSED(user_data); return IM_ALLOC(size); }
static void ImGuiFreeTypeDefaultFreeFunc(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_FREE(ptr); }
// Current memory allocators
static void* (*GImGuiFreeTypeAllocFunc)(size_t size, void* user_data) = ImGuiFreeTypeDefaultAllocFunc;
static void (*GImGuiFreeTypeFreeFunc)(void* ptr, void* user_data) = ImGuiFreeTypeDefaultFreeFunc;
static void* GImGuiFreeTypeAllocatorUserData = NULL;
//-------------------------------------------------------------------------
// Code
//-------------------------------------------------------------------------
namespace
{
// Glyph metrics:
@@ -71,7 +99,7 @@ namespace
// | |
// |------------- advanceX ----------->|
/// A structure that describe a glyph.
// A structure that describe a glyph.
struct GlyphInfo
{
int Width; // Glyph's width in pixels.
@@ -79,6 +107,7 @@ namespace
FT_Int OffsetX; // The distance from the origin ("pen position") to the left of the glyph.
FT_Int OffsetY; // The distance from the origin to the top of the glyph. This is usually a value < 0.
float AdvanceX; // The distance from the origin to the origin of the next glyph. This is usually a value > 0.
bool IsColored; // The glyph is colored
};
// Font parameters and metrics.
@@ -101,7 +130,7 @@ namespace
void SetPixelHeight(int pixel_height); // Change font pixel size. All following calls to RasterizeGlyph() will use this size
const FT_Glyph_Metrics* LoadGlyph(uint32_t in_codepoint);
const FT_Bitmap* RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info);
void BlitGlyph(const FT_Bitmap* ft_bitmap, uint8_t* dst, uint32_t dst_pitch, unsigned char* multiply_table = NULL);
void BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table = NULL);
~FreeTypeFont() { CloseFont(); }
// [Internals]
@@ -109,12 +138,13 @@ namespace
FT_Face Face;
unsigned int UserFlags; // = ImFontConfig::RasterizerFlags
FT_Int32 LoadFlags;
FT_Render_Mode RenderMode;
};
// From SDL_ttf: Handy routines for converting from fixed point
#define FT_CEIL(X) (((X + 63) & -64) / 64)
bool FreeTypeFont::InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_user_flags)
bool FreeTypeFont::InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_font_builder_flags)
{
FT_Error error = FT_New_Memory_Face(ft_library, (uint8_t*)cfg.FontData, (uint32_t)cfg.FontDataSize, (uint32_t)cfg.FontNo, &Face);
if (error != 0)
@@ -123,25 +153,37 @@ namespace
if (error != 0)
return false;
memset(&Info, 0, sizeof(Info));
SetPixelHeight((uint32_t)cfg.SizePixels);
// Convert to FreeType flags (NB: Bold and Oblique are processed separately)
UserFlags = cfg.RasterizerFlags | extra_user_flags;
LoadFlags = FT_LOAD_NO_BITMAP;
if (UserFlags & ImGuiFreeType::NoHinting)
UserFlags = cfg.FontBuilderFlags | extra_font_builder_flags;
LoadFlags = 0;
if ((UserFlags & ImGuiFreeTypeBuilderFlags_Bitmap) == 0)
LoadFlags |= FT_LOAD_NO_BITMAP;
if (UserFlags & ImGuiFreeTypeBuilderFlags_NoHinting)
LoadFlags |= FT_LOAD_NO_HINTING;
if (UserFlags & ImGuiFreeType::NoAutoHint)
if (UserFlags & ImGuiFreeTypeBuilderFlags_NoAutoHint)
LoadFlags |= FT_LOAD_NO_AUTOHINT;
if (UserFlags & ImGuiFreeType::ForceAutoHint)
if (UserFlags & ImGuiFreeTypeBuilderFlags_ForceAutoHint)
LoadFlags |= FT_LOAD_FORCE_AUTOHINT;
if (UserFlags & ImGuiFreeType::LightHinting)
if (UserFlags & ImGuiFreeTypeBuilderFlags_LightHinting)
LoadFlags |= FT_LOAD_TARGET_LIGHT;
else if (UserFlags & ImGuiFreeType::MonoHinting)
else if (UserFlags & ImGuiFreeTypeBuilderFlags_MonoHinting)
LoadFlags |= FT_LOAD_TARGET_MONO;
else
LoadFlags |= FT_LOAD_TARGET_NORMAL;
if (UserFlags & ImGuiFreeTypeBuilderFlags_Monochrome)
RenderMode = FT_RENDER_MODE_MONO;
else
RenderMode = FT_RENDER_MODE_NORMAL;
if (UserFlags & ImGuiFreeTypeBuilderFlags_LoadColor)
LoadFlags |= FT_LOAD_COLOR;
memset(&Info, 0, sizeof(Info));
SetPixelHeight((uint32_t)cfg.SizePixels);
return true;
}
@@ -160,7 +202,7 @@ namespace
// is a maximum height of an any given glyph, i.e. it's the sum of font's ascender and descender. Seems strange to me.
// NB: FT_Set_Pixel_Sizes() doesn't seem to get us the same result.
FT_Size_RequestRec req;
req.type = FT_SIZE_REQUEST_TYPE_REAL_DIM;
req.type = (UserFlags & ImGuiFreeTypeBuilderFlags_Bitmap) ? FT_SIZE_REQUEST_TYPE_NOMINAL : FT_SIZE_REQUEST_TYPE_REAL_DIM;
req.width = 0;
req.height = (uint32_t)pixel_height * 64;
req.horiResolution = 0;
@@ -188,12 +230,12 @@ namespace
// Need an outline for this to work
FT_GlyphSlot slot = Face->glyph;
IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE);
IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP);
// Apply convenience transform (this is not picking from real "Bold"/"Italic" fonts! Merely applying FreeType helper transform. Oblique == Slanting)
if (UserFlags & ImGuiFreeType::Bold)
if (UserFlags & ImGuiFreeTypeBuilderFlags_Bold)
FT_GlyphSlot_Embolden(slot);
if (UserFlags & ImGuiFreeType::Oblique)
if (UserFlags & ImGuiFreeTypeBuilderFlags_Oblique)
{
FT_GlyphSlot_Oblique(slot);
//FT_BBox bbox;
@@ -208,7 +250,7 @@ namespace
const FT_Bitmap* FreeTypeFont::RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info)
{
FT_GlyphSlot slot = Face->glyph;
FT_Error error = FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL);
FT_Error error = FT_Render_Glyph(slot, RenderMode);
if (error != 0)
return NULL;
@@ -218,11 +260,12 @@ namespace
out_glyph_info->OffsetX = Face->glyph->bitmap_left;
out_glyph_info->OffsetY = -Face->glyph->bitmap_top;
out_glyph_info->AdvanceX = (float)FT_CEIL(slot->advance.x);
out_glyph_info->IsColored = (ft_bitmap->pixel_mode == FT_PIXEL_MODE_BGRA);
return ft_bitmap;
}
void FreeTypeFont::BlitGlyph(const FT_Bitmap* ft_bitmap, uint8_t* dst, uint32_t dst_pitch, unsigned char* multiply_table)
void FreeTypeFont::BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table)
{
IM_ASSERT(ft_bitmap != NULL);
const uint32_t w = ft_bitmap->width;
@@ -230,32 +273,94 @@ namespace
const uint8_t* src = ft_bitmap->buffer;
const uint32_t src_pitch = ft_bitmap->pitch;
if (multiply_table == NULL)
switch (ft_bitmap->pixel_mode)
{
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
memcpy(dst, src, w);
}
else
{
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
for (uint32_t x = 0; x < w; x++)
dst[x] = multiply_table[src[x]];
case FT_PIXEL_MODE_GRAY: // Grayscale image, 1 byte per pixel.
{
if (multiply_table == NULL)
{
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
for (uint32_t x = 0; x < w; x++)
dst[x] = IM_COL32(255, 255, 255, src[x]);
}
else
{
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
for (uint32_t x = 0; x < w; x++)
dst[x] = IM_COL32(255, 255, 255, multiply_table[src[x]]);
}
break;
}
case FT_PIXEL_MODE_MONO: // Monochrome image, 1 bit per pixel. The bits in each byte are ordered from MSB to LSB.
{
uint8_t color0 = multiply_table ? multiply_table[0] : 0;
uint8_t color1 = multiply_table ? multiply_table[255] : 255;
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
{
uint8_t bits = 0;
const uint8_t* bits_ptr = src;
for (uint32_t x = 0; x < w; x++, bits <<= 1)
{
if ((x & 7) == 0)
bits = *bits_ptr++;
dst[x] = IM_COL32(255, 255, 255, (bits & 0x80) ? color1 : color0);
}
}
break;
}
case FT_PIXEL_MODE_BGRA:
{
// FIXME: Converting pre-multiplied alpha to straight. Doesn't smell good.
#define DE_MULTIPLY(color, alpha) (ImU32)(255.0f * (float)color / (float)alpha + 0.5f)
if (multiply_table == NULL)
{
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
for (uint32_t x = 0; x < w; x++)
{
uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3];
dst[x] = IM_COL32(DE_MULTIPLY(r, a), DE_MULTIPLY(g, a), DE_MULTIPLY(b, a), a);
}
}
else
{
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
{
for (uint32_t x = 0; x < w; x++)
{
uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3];
dst[x] = IM_COL32(multiply_table[DE_MULTIPLY(r, a)], multiply_table[DE_MULTIPLY(g, a)], multiply_table[DE_MULTIPLY(b, a)], multiply_table[a]);
}
}
}
#undef DE_MULTIPLY
break;
}
default:
IM_ASSERT(0 && "FreeTypeFont::BlitGlyph(): Unknown bitmap pixel mode!");
}
}
}
#ifndef STB_RECT_PACK_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds)
#define STBRP_ASSERT(x) IM_ASSERT(x)
#ifndef STB_RECT_PACK_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds)
#ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
#define STBRP_ASSERT(x) do { IM_ASSERT(x); } while (0)
#define STBRP_STATIC
#define STB_RECT_PACK_IMPLEMENTATION
#endif
#ifdef IMGUI_STB_RECT_PACK_FILENAME
#include IMGUI_STB_RECT_PACK_FILENAME
#else
#include "imstb_rectpack.h"
#endif
#endif
struct ImFontBuildSrcGlyphFT
{
GlyphInfo Info;
uint32_t Codepoint;
unsigned char* BitmapData; // Point within one of the dst_tmp_bitmap_buffers[] array
unsigned int* BitmapData; // Point within one of the dst_tmp_bitmap_buffers[] array
ImFontBuildSrcGlyphFT() { memset(this, 0, sizeof(*this)); }
};
struct ImFontBuildSrcDataFT
@@ -266,7 +371,7 @@ struct ImFontBuildSrcDataFT
int DstIndex; // Index into atlas->Fonts[] and dst_tmp_array[]
int GlyphsHighest; // Highest requested codepoint
int GlyphsCount; // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font)
ImBoolVector GlyphsSet; // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB)
ImBitVector GlyphsSet; // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB)
ImVector<ImFontBuildSrcGlyphFT> GlyphsList;
};
@@ -276,14 +381,14 @@ struct ImFontBuildDstDataFT
int SrcCount; // Number of source fonts targeting this destination font.
int GlyphsHighest;
int GlyphsCount;
ImBoolVector GlyphsSet; // This is used to resolve collision when multiple sources are merged into a same destination font.
ImBitVector GlyphsSet; // This is used to resolve collision when multiple sources are merged into a same destination font.
};
bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, unsigned int extra_flags)
bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, unsigned int extra_flags)
{
IM_ASSERT(atlas->ConfigData.Size > 0);
ImFontAtlasBuildRegisterDefaultCustomRects(atlas);
ImFontAtlasBuildInit(atlas);
// Clear atlas
atlas->TexID = (ImTextureID)NULL;
@@ -293,12 +398,13 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns
atlas->ClearTexData();
// Temporary storage for building
bool src_load_color = false;
ImVector<ImFontBuildSrcDataFT> src_tmp_array;
ImVector<ImFontBuildDstDataFT> dst_tmp_array;
src_tmp_array.resize(atlas->ConfigData.Size);
dst_tmp_array.resize(atlas->Fonts.Size);
memset(src_tmp_array.Data, 0, (size_t)src_tmp_array.size_in_bytes());
memset(dst_tmp_array.Data, 0, (size_t)dst_tmp_array.size_in_bytes());
memset((void*)src_tmp_array.Data, 0, (size_t)src_tmp_array.size_in_bytes());
memset((void*)dst_tmp_array.Data, 0, (size_t)dst_tmp_array.size_in_bytes());
// 1. Initialize font loading structure, check font data validity
for (int src_i = 0; src_i < atlas->ConfigData.Size; src_i++)
@@ -322,6 +428,7 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns
return false;
// Measure highest codepoints
src_load_color |= (cfg.FontBuilderFlags & ImGuiFreeTypeBuilderFlags_LoadColor) != 0;
ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
src_tmp.SrcRanges = cfg.GlyphRanges ? cfg.GlyphRanges : atlas->GetGlyphRangesDefault();
for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
@@ -336,14 +443,14 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns
{
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
src_tmp.GlyphsSet.Resize(src_tmp.GlyphsHighest + 1);
src_tmp.GlyphsSet.Create(src_tmp.GlyphsHighest + 1);
if (dst_tmp.GlyphsSet.Storage.empty())
dst_tmp.GlyphsSet.Resize(dst_tmp.GlyphsHighest + 1);
dst_tmp.GlyphsSet.Create(dst_tmp.GlyphsHighest + 1);
for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
for (int codepoint = src_range[0]; codepoint <= src_range[1]; codepoint++)
for (int codepoint = src_range[0]; codepoint <= (int)src_range[1]; codepoint++)
{
if (dst_tmp.GlyphsSet.GetBit(codepoint)) // Don't overwrite existing glyphs. We could make this an option (e.g. MergeOverwrite)
if (dst_tmp.GlyphsSet.TestBit(codepoint)) // Don't overwrite existing glyphs. We could make this an option (e.g. MergeOverwrite)
continue;
uint32_t glyph_index = FT_Get_Char_Index(src_tmp.Font.Face, codepoint); // It is actually in the font? (FIXME-OPT: We are not storing the glyph_index..)
if (glyph_index == 0)
@@ -352,8 +459,8 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns
// Add to avail set/counters
src_tmp.GlyphsCount++;
dst_tmp.GlyphsCount++;
src_tmp.GlyphsSet.SetBit(codepoint, true);
dst_tmp.GlyphsSet.SetBit(codepoint, true);
src_tmp.GlyphsSet.SetBit(codepoint);
dst_tmp.GlyphsSet.SetBit(codepoint);
total_glyphs_count++;
}
}
@@ -364,16 +471,15 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
src_tmp.GlyphsList.reserve(src_tmp.GlyphsCount);
IM_ASSERT(sizeof(src_tmp.GlyphsSet.Storage.Data[0]) == sizeof(int));
const int* it_begin = src_tmp.GlyphsSet.Storage.begin();
const int* it_end = src_tmp.GlyphsSet.Storage.end();
for (const int* it = it_begin; it < it_end; it++)
if (int entries_32 = *it)
for (int bit_n = 0; bit_n < 32; bit_n++)
if (entries_32 & (1 << bit_n))
IM_ASSERT(sizeof(src_tmp.GlyphsSet.Storage.Data[0]) == sizeof(ImU32));
const ImU32* it_begin = src_tmp.GlyphsSet.Storage.begin();
const ImU32* it_end = src_tmp.GlyphsSet.Storage.end();
for (const ImU32* it = it_begin; it < it_end; it++)
if (ImU32 entries_32 = *it)
for (ImU32 bit_n = 0; bit_n < 32; bit_n++)
if (entries_32 & ((ImU32)1 << bit_n))
{
ImFontBuildSrcGlyphFT src_glyph;
memset(&src_glyph, 0, sizeof(src_glyph));
src_glyph.Codepoint = (ImWchar)(((it - it_begin) << 5) + bit_n);
//src_glyph.GlyphIndex = 0; // FIXME-OPT: We had this info in the previous step and lost it..
src_tmp.GlyphsList.push_back(src_glyph);
@@ -427,7 +533,6 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns
ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i];
const FT_Glyph_Metrics* metrics = src_tmp.Font.LoadGlyph(src_glyph.Codepoint);
IM_ASSERT(metrics != NULL);
if (metrics == NULL)
continue;
@@ -436,7 +541,7 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns
IM_ASSERT(ft_bitmap);
// Allocate new temporary chunk if needed
const int bitmap_size_in_bytes = src_glyph.Info.Width * src_glyph.Info.Height;
const int bitmap_size_in_bytes = src_glyph.Info.Width * src_glyph.Info.Height * 4;
if (buf_bitmap_current_used_bytes + bitmap_size_in_bytes > BITMAP_BUFFERS_CHUNK_SIZE)
{
buf_bitmap_current_used_bytes = 0;
@@ -444,9 +549,9 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns
}
// Blit rasterized pixels to our temporary buffer and keep a pointer to it.
src_glyph.BitmapData = buf_bitmap_buffers.back() + buf_bitmap_current_used_bytes;
src_glyph.BitmapData = (unsigned int*)(buf_bitmap_buffers.back() + buf_bitmap_current_used_bytes);
buf_bitmap_current_used_bytes += bitmap_size_in_bytes;
src_tmp.Font.BlitGlyph(ft_bitmap, src_glyph.BitmapData, src_glyph.Info.Width * 1, multiply_enabled ? multiply_table : NULL);
src_tmp.Font.BlitGlyph(ft_bitmap, src_glyph.BitmapData, src_glyph.Info.Width, multiply_enabled ? multiply_table : NULL);
src_tmp.Rects[glyph_i].w = (stbrp_coord)(src_glyph.Info.Width + padding);
src_tmp.Rects[glyph_i].h = (stbrp_coord)(src_glyph.Info.Height + padding);
@@ -462,7 +567,7 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns
if (atlas->TexDesiredWidth > 0)
atlas->TexWidth = atlas->TexDesiredWidth;
else
atlas->TexWidth = (surface_sqrt >= 4096*0.7f) ? 4096 : (surface_sqrt >= 2048*0.7f) ? 2048 : (surface_sqrt >= 1024*0.7f) ? 1024 : 512;
atlas->TexWidth = (surface_sqrt >= 4096 * 0.7f) ? 4096 : (surface_sqrt >= 2048 * 0.7f) ? 2048 : (surface_sqrt >= 1024 * 0.7f) ? 1024 : 512;
// 5. Start packing
// Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
@@ -493,25 +598,37 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns
// 7. Allocate texture
atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight);
atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight);
atlas->TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight);
memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight);
if (src_load_color)
{
atlas->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight * 4);
memset(atlas->TexPixelsRGBA32, 0, atlas->TexWidth * atlas->TexHeight * 4);
}
else
{
atlas->TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight);
memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight);
}
// 8. Copy rasterized font characters back into the main texture
// 9. Setup ImFont and glyphs for runtime
bool tex_use_colors = false;
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
{
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
if (src_tmp.GlyphsCount == 0)
continue;
// When merging fonts with MergeMode=true:
// - We can have multiple input fonts writing into a same destination font.
// - dst_font->ConfigData is != from cfg which is our source configuration.
ImFontConfig& cfg = atlas->ConfigData[src_i];
ImFont* dst_font = cfg.DstFont; // We can have multiple input fonts writing into a same destination font (when using MergeMode=true)
ImFont* dst_font = cfg.DstFont;
const float ascent = src_tmp.Font.Info.Ascender;
const float descent = src_tmp.Font.Info.Descender;
ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
const float font_off_x = cfg.GlyphOffset.x;
const float font_off_y = cfg.GlyphOffset.y + (float)(int)(dst_font->Ascent + 0.5f);
const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent);
const int padding = atlas->TexGlyphPadding;
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
@@ -519,6 +636,8 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns
ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i];
stbrp_rect& pack_rect = src_tmp.Rects[glyph_i];
IM_ASSERT(pack_rect.was_packed);
if (pack_rect.w == 0 && pack_rect.h == 0)
continue;
GlyphInfo& info = src_glyph.Info;
IM_ASSERT(info.Width + padding <= pack_rect.w);
@@ -529,19 +648,24 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns
// Blit from temporary buffer to final texture
size_t blit_src_stride = (size_t)src_glyph.Info.Width;
size_t blit_dst_stride = (size_t)atlas->TexWidth;
unsigned char* blit_src = src_glyph.BitmapData;
unsigned char* blit_dst = atlas->TexPixelsAlpha8 + (ty * blit_dst_stride) + tx;
for (int y = info.Height; y > 0; y--, blit_dst += blit_dst_stride, blit_src += blit_src_stride)
memcpy(blit_dst, blit_src, blit_src_stride);
float char_advance_x_org = info.AdvanceX;
float char_advance_x_mod = ImClamp(char_advance_x_org, cfg.GlyphMinAdvanceX, cfg.GlyphMaxAdvanceX);
float char_off_x = font_off_x;
if (char_advance_x_org != char_advance_x_mod)
char_off_x += cfg.PixelSnapH ? (float)(int)((char_advance_x_mod - char_advance_x_org) * 0.5f) : (char_advance_x_mod - char_advance_x_org) * 0.5f;
unsigned int* blit_src = src_glyph.BitmapData;
if (atlas->TexPixelsAlpha8 != NULL)
{
unsigned char* blit_dst = atlas->TexPixelsAlpha8 + (ty * blit_dst_stride) + tx;
for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride)
for (int x = 0; x < info.Width; x++)
blit_dst[x] = (unsigned char)((blit_src[x] >> IM_COL32_A_SHIFT) & 0xFF);
}
else
{
unsigned int* blit_dst = atlas->TexPixelsRGBA32 + (ty * blit_dst_stride) + tx;
for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride)
for (int x = 0; x < info.Width; x++)
blit_dst[x] = blit_src[x];
}
// Register glyph
float x0 = info.OffsetX + char_off_x;
float x0 = info.OffsetX + font_off_x;
float y0 = info.OffsetY + font_off_y;
float x1 = x0 + info.Width;
float y1 = y0 + info.Height;
@@ -549,11 +673,17 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns
float v0 = (ty) / (float)atlas->TexHeight;
float u1 = (tx + info.Width) / (float)atlas->TexWidth;
float v1 = (ty + info.Height) / (float)atlas->TexHeight;
dst_font->AddGlyph((ImWchar)src_glyph.Codepoint, x0, y0, x1, y1, u0, v0, u1, v1, char_advance_x_mod);
dst_font->AddGlyph(&cfg, (ImWchar)src_glyph.Codepoint, x0, y0, x1, y1, u0, v0, u1, v1, info.AdvanceX);
ImFontGlyph* dst_glyph = &dst_font->Glyphs.back();
IM_ASSERT(dst_glyph->Codepoint == src_glyph.Codepoint);
if (src_glyph.Info.IsColored)
dst_glyph->Colored = tex_use_colors = true;
}
src_tmp.Rects = NULL;
}
atlas->TexPixelsUseColors = tex_use_colors;
// Cleanup
for (int buf_i = 0; buf_i < buf_bitmap_buffers.Size; buf_i++)
@@ -566,53 +696,45 @@ bool ImFontAtlasBuildWithFreeType(FT_Library ft_library, ImFontAtlas* atlas, uns
return true;
}
// Default memory allocators
static void* ImFreeTypeDefaultAllocFunc(size_t size, void* user_data) { IM_UNUSED(user_data); return IM_ALLOC(size); }
static void ImFreeTypeDefaultFreeFunc(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_FREE(ptr); }
// Current memory allocators
static void* (*GImFreeTypeAllocFunc)(size_t size, void* user_data) = ImFreeTypeDefaultAllocFunc;
static void (*GImFreeTypeFreeFunc)(void* ptr, void* user_data) = ImFreeTypeDefaultFreeFunc;
static void* GImFreeTypeAllocatorUserData = NULL;
// FreeType memory allocation callbacks
static void* FreeType_Alloc(FT_Memory /*memory*/, long size)
{
return GImFreeTypeAllocFunc((size_t)size, GImFreeTypeAllocatorUserData);
return GImGuiFreeTypeAllocFunc((size_t)size, GImGuiFreeTypeAllocatorUserData);
}
static void FreeType_Free(FT_Memory /*memory*/, void* block)
{
GImFreeTypeFreeFunc(block, GImFreeTypeAllocatorUserData);
GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
}
static void* FreeType_Realloc(FT_Memory /*memory*/, long cur_size, long new_size, void* block)
{
// Implement realloc() as we don't ask user to provide it.
if (block == NULL)
return GImFreeTypeAllocFunc((size_t)new_size, GImFreeTypeAllocatorUserData);
return GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData);
if (new_size == 0)
{
GImFreeTypeFreeFunc(block, GImFreeTypeAllocatorUserData);
GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
return NULL;
}
if (new_size > cur_size)
{
void* new_block = GImFreeTypeAllocFunc((size_t)new_size, GImFreeTypeAllocatorUserData);
void* new_block = GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData);
memcpy(new_block, block, (size_t)cur_size);
GImFreeTypeFreeFunc(block, GImFreeTypeAllocatorUserData);
GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
return new_block;
}
return block;
}
bool ImGuiFreeType::BuildFontAtlas(ImFontAtlas* atlas, unsigned int extra_flags)
static bool ImFontAtlasBuildWithFreeType(ImFontAtlas* atlas)
{
// FreeType memory management: https://www.freetype.org/freetype2/docs/design/design-4.html
FT_MemoryRec_ memory_rec = { 0 };
FT_MemoryRec_ memory_rec = {};
memory_rec.user = NULL;
memory_rec.alloc = &FreeType_Alloc;
memory_rec.free = &FreeType_Free;
memory_rec.realloc = &FreeType_Realloc;
@@ -626,15 +748,22 @@ bool ImGuiFreeType::BuildFontAtlas(ImFontAtlas* atlas, unsigned int extra_flags)
// If you don't call FT_Add_Default_Modules() the rest of code may work, but FreeType won't use our custom allocator.
FT_Add_Default_Modules(ft_library);
bool ret = ImFontAtlasBuildWithFreeType(ft_library, atlas, extra_flags);
bool ret = ImFontAtlasBuildWithFreeTypeEx(ft_library, atlas, atlas->FontBuilderFlags);
FT_Done_Library(ft_library);
return ret;
}
const ImFontBuilderIO* ImGuiFreeType::GetBuilderForFreeType()
{
static ImFontBuilderIO io;
io.FontBuilder_Build = ImFontAtlasBuildWithFreeType;
return &io;
}
void ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
{
GImFreeTypeAllocFunc = alloc_func;
GImFreeTypeFreeFunc = free_func;
GImFreeTypeAllocatorUserData = user_data;
GImGuiFreeTypeAllocFunc = alloc_func;
GImGuiFreeTypeFreeFunc = free_func;
GImGuiFreeTypeAllocatorUserData = user_data;
}
@@ -0,0 +1,50 @@
// dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder)
// (headers)
#pragma once
#include "imgui.h" // IMGUI_API
// Forward declarations
struct ImFontAtlas;
struct ImFontBuilderIO;
// Hinting greatly impacts visuals (and glyph sizes).
// - By default, hinting is enabled and the font's native hinter is preferred over the auto-hinter.
// - When disabled, FreeType generates blurrier glyphs, more or less matches the stb_truetype.h
// - The Default hinting mode usually looks good, but may distort glyphs in an unusual way.
// - The Light hinting mode generates fuzzier glyphs but better matches Microsoft's rasterizer.
// You can set those flags globaly in ImFontAtlas::FontBuilderFlags
// You can set those flags on a per font basis in ImFontConfig::FontBuilderFlags
enum ImGuiFreeTypeBuilderFlags
{
ImGuiFreeTypeBuilderFlags_NoHinting = 1 << 0, // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes.
ImGuiFreeTypeBuilderFlags_NoAutoHint = 1 << 1, // Disable auto-hinter.
ImGuiFreeTypeBuilderFlags_ForceAutoHint = 1 << 2, // Indicates that the auto-hinter is preferred over the font's native hinter.
ImGuiFreeTypeBuilderFlags_LightHinting = 1 << 3, // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text.
ImGuiFreeTypeBuilderFlags_MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output.
ImGuiFreeTypeBuilderFlags_Bold = 1 << 5, // Styling: Should we artificially embolden the font?
ImGuiFreeTypeBuilderFlags_Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style?
ImGuiFreeTypeBuilderFlags_Monochrome = 1 << 7, // Disable anti-aliasing. Combine this with MonoHinting for best results!
ImGuiFreeTypeBuilderFlags_LoadColor = 1 << 8, // Enable FreeType color-layered glyphs
ImGuiFreeTypeBuilderFlags_Bitmap = 1 << 9 // Enable FreeType bitmap glyphs
};
namespace ImGuiFreeType
{
// This is automatically assigned when using '#define IMGUI_ENABLE_FREETYPE'.
// If you need to dynamically select between multiple builders:
// - you can manually assign this builder with 'atlas->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()'
// - prefer deep-copying this into your own ImFontBuilderIO instance if you use hot-reloading that messes up static data.
IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType();
// Override allocators. By default ImGuiFreeType will use IM_ALLOC()/IM_FREE()
// However, as FreeType does lots of allocations we provide a way for the user to redirect it to a separate memory heap if desired.
IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data = NULL);
// Obsolete names (will be removed soon)
// Prefer using '#define IMGUI_ENABLE_FREETYPE'
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
static inline bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int flags = 0) { atlas->FontBuilderIO = GetBuilderForFreeType(); atlas->FontBuilderFlags = flags; return atlas->Build(); }
#endif
}
@@ -0,0 +1,18 @@
// dear imgui: single-file wrapper include
// We use this to validate compiling all *.cpp files in a same compilation unit.
// Users of that technique (also called "Unity builds") can generally provide this themselves,
// so we don't really recommend you use this in your projects.
// Do this:
// #define IMGUI_IMPLEMENTATION
// Before you include this file in *one* C++ file to create the implementation.
// Using this in your project will leak the contents of imgui_internal.h and ImVec2 operators in this compilation unit.
#include "../../imgui.h"
#ifdef IMGUI_IMPLEMENTATION
#include "../../imgui.cpp"
#include "../../imgui_demo.cpp"
#include "../../imgui_draw.cpp"
#include "../../imgui_tables.cpp"
#include "../../imgui_widgets.cpp"
#endif
@@ -100,49 +100,10 @@ namespace LmbrCentral
void DecalComponent::Activate()
{
AZ::Transform transform = AZ::Transform::CreateIdentity();
EBUS_EVENT_ID_RESULT(transform, GetEntityId(), AZ::TransformBus, GetWorldTM);
SDecalProperties decalProperties = m_configuration.GetDecalProperties(transform);
m_decalRenderNode = static_cast<IDecalRenderNode*>(gEnv->p3DEngine->CreateRenderNode(eERType_Decal));
if (m_decalRenderNode)
{
m_decalRenderNode->SetRndFlags(m_decalRenderNode->GetRndFlags() | ERF_COMPONENT_ENTITY);
m_decalRenderNode->SetDecalProperties(decalProperties);
m_decalRenderNode->SetMinSpec(static_cast<int>(decalProperties.m_minSpec));
m_decalRenderNode->SetMatrix(AZTransformToLYTransform(transform));
m_decalRenderNode->SetViewDistanceMultiplier(m_configuration.m_viewDistanceMultiplier);
const int configSpec = gEnv->pSystem->GetConfigSpec(true);
if (!m_configuration.m_visible || static_cast<AZ::u32>(configSpec) < static_cast<AZ::u32>(m_configuration.m_minSpec))
{
Hide();
}
}
m_materialBusHandler->Activate(m_decalRenderNode, m_entity->GetId());
DecalComponentRequestBus::Handler::BusConnect(GetEntityId());
RenderNodeRequestBus::Handler::BusConnect(GetEntityId());
AZ::TransformNotificationBus::Handler::BusConnect(GetEntityId());
MaterialOwnerRequestBus::Handler::BusConnect(GetEntityId());
}
void DecalComponent::Deactivate()
{
DecalComponentRequestBus::Handler::BusDisconnect();
RenderNodeRequestBus::Handler::BusDisconnect();
AZ::TransformNotificationBus::Handler::BusDisconnect();
MaterialOwnerRequestBus::Handler::BusDisconnect();
m_materialBusHandler->Deactivate();
if (m_decalRenderNode)
{
gEnv->p3DEngine->DeleteRenderNode(m_decalRenderNode);
m_decalRenderNode = nullptr;
}
}
void DecalComponent::OnTransformChanged([[maybe_unused]] const AZ::Transform& local, const AZ::Transform& world)
@@ -190,50 +190,10 @@ namespace LmbrCentral
void EditorDecalComponent::Activate()
{
Base::Activate();
AZ::EntityId entityId = GetEntityId();
IEditor* editor = nullptr;
EBUS_EVENT_RESULT(editor, AzToolsFramework::EditorRequests::Bus, GetEditor);
m_configuration.m_editorEntityId = entityId;
m_decalRenderNode = static_cast<IDecalRenderNode*>(editor->Get3DEngine()->CreateRenderNode(eERType_Decal));
RefreshDecal();
MaterialOwnerRequestBus::Handler::BusConnect(entityId);
AZ::TransformNotificationBus::Handler::BusConnect(entityId);
DecalComponentEditorRequests::Bus::Handler::BusConnect(entityId);
RenderNodeRequestBus::Handler::BusConnect(entityId);
AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(entityId);
AzToolsFramework::EditorVisibilityNotificationBus::Handler::BusConnect(entityId);
AzToolsFramework::EditorEvents::Bus::Handler::BusConnect();
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(entityId);
AzFramework::BoundsRequestBus::Handler::BusConnect(entityId);
}
void EditorDecalComponent::Deactivate()
{
MaterialOwnerRequestBus::Handler::BusDisconnect();
DecalComponentEditorRequests::Bus::Handler::BusDisconnect();
RenderNodeRequestBus::Handler::BusDisconnect();
AZ::TransformNotificationBus::Handler::BusDisconnect();
AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
AzToolsFramework::EditorVisibilityNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect();
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect();
AzFramework::BoundsRequestBus::Handler::BusDisconnect();
m_configuration.m_editorEntityId.SetInvalid();
if (m_decalRenderNode)
{
IEditor* editor = nullptr;
EBUS_EVENT_RESULT(editor, AzToolsFramework::EditorRequests::Bus, GetEditor);
editor->Get3DEngine()->DeleteRenderNode(m_decalRenderNode);
m_decalRenderNode = nullptr;
}
Base::Deactivate();
}
+23 -15
View File
@@ -15,6 +15,7 @@
#include <AzCore/RTTI/RTTI.h>
#include <AzNetworking/ConnectionLayer/IConnection.h>
#include <AzNetworking/DataStructures/ByteBuffer.h>
#include <Include/MultiplayerStats.h>
namespace AzNetworking
{
@@ -23,21 +24,6 @@ namespace AzNetworking
namespace Multiplayer
{
struct MultiplayerStats
{
uint64_t m_entityCount = 0;
uint64_t m_clientConnectionCount = 0;
uint64_t m_serverConnectionCount = 0;
uint64_t m_propertyUpdatesSent = 0;
uint64_t m_propertyUpdatesSentBytes = 0;
uint64_t m_propertyUpdatesRecv = 0;
uint64_t m_propertyUpdatesRecvBytes = 0;
uint64_t m_rpcsSent = 0;
uint64_t m_rpcsSentBytes = 0;
uint64_t m_rpcsRecv = 0;
uint64_t m_rpcsRecvBytes = 0;
};
//! Collection of types of Multiplayer Connections
enum class MultiplayerAgentType
{
@@ -88,6 +74,28 @@ namespace Multiplayer
//! @param handler The SessionShutdownEvent handler to add
virtual void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) = 0;
//! Returns the gem name associated with the provided component index.
//! @param netComponentId the componentId to return the gem name of
//! @return the name of the gem that contains the requested component
virtual const char* GetComponentGemName(NetComponentId netComponentId) const = 0;
//! Returns the component name associated with the provided component index.
//! @param netComponentId the componentId to return the component name of
//! @return the name of the component
virtual const char* GetComponentName(NetComponentId netComponentId) const = 0;
//! Returns the property name associated with the provided component index and property index.
//! @param netComponentId the component index to return the property name of
//! @param propertyIndex the index of the network property to return the property name of
//! @return the name of the network property
virtual const char* GetComponentPropertyName(NetComponentId netComponentId, PropertyIndex propertyIndex) const = 0;
//! Returns the Rpc name associated with the provided component index and rpc index.
//! @param netComponentId the componentId to return the property name of
//! @param rpcIndex the index of the rpc to return the rpc name of
//! @return the name of the requested rpc
virtual const char* GetComponentRpcName(NetComponentId netComponentId, RpcIndex rpcIndex) const = 0;
//! Retrieve the stats object bound to this multiplayer instance.
//! @return the stats object bound to this multiplayer instance
MultiplayerStats& GetStats() { return m_stats; }
@@ -0,0 +1,164 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Include/MultiplayerStats.h>
namespace Multiplayer
{
void MultiplayerStats::ReserveComponentStats(NetComponentId netComponentId, uint16_t propertyCount, uint16_t rpcCount)
{
const uint16_t netComponentIndex = aznumeric_cast<uint16_t>(netComponentId);
if (m_componentStats.size() <= netComponentIndex)
{
m_componentStats.resize(netComponentIndex + 1);
}
m_componentStats[netComponentIndex].m_propertyUpdatesSent.resize(propertyCount);
m_componentStats[netComponentIndex].m_propertyUpdatesRecv.resize(propertyCount);
m_componentStats[netComponentIndex].m_rpcsSent.resize(rpcCount);
m_componentStats[netComponentIndex].m_rpcsRecv.resize(rpcCount);
}
void MultiplayerStats::RecordPropertySent(NetComponentId netComponentId, PropertyIndex propertyId, uint32_t totalBytes)
{
const uint16_t netComponentIndex = aznumeric_cast<uint16_t>(netComponentId);
const uint16_t propertyIndex = aznumeric_cast<uint16_t>(propertyId);
m_componentStats[netComponentIndex].m_propertyUpdatesSent[propertyIndex].m_totalCalls++;
m_componentStats[netComponentIndex].m_propertyUpdatesSent[propertyIndex].m_totalBytes += totalBytes;
m_componentStats[netComponentIndex].m_propertyUpdatesSent[propertyIndex].m_callHistory[m_recordMetricIndex]++;
m_componentStats[netComponentIndex].m_propertyUpdatesSent[propertyIndex].m_byteHistory[m_recordMetricIndex] += totalBytes;
}
void MultiplayerStats::RecordPropertyReceived(NetComponentId netComponentId, PropertyIndex propertyId, uint32_t totalBytes)
{
const uint16_t netComponentIndex = aznumeric_cast<uint16_t>(netComponentId);
const uint16_t propertyIndex = aznumeric_cast<uint16_t>(propertyId);
m_componentStats[netComponentIndex].m_propertyUpdatesRecv[propertyIndex].m_totalCalls++;
m_componentStats[netComponentIndex].m_propertyUpdatesRecv[propertyIndex].m_totalBytes += totalBytes;
m_componentStats[netComponentIndex].m_propertyUpdatesRecv[propertyIndex].m_callHistory[m_recordMetricIndex]++;
m_componentStats[netComponentIndex].m_propertyUpdatesRecv[propertyIndex].m_byteHistory[m_recordMetricIndex] += totalBytes;
}
void MultiplayerStats::RecordRpcSent(NetComponentId netComponentId, RpcIndex rpcId, uint32_t totalBytes)
{
const uint16_t netComponentIndex = aznumeric_cast<uint16_t>(netComponentId);
const uint16_t rpcIndex = aznumeric_cast<uint16_t>(rpcId);
m_componentStats[netComponentIndex].m_rpcsSent[rpcIndex].m_totalCalls++;
m_componentStats[netComponentIndex].m_rpcsSent[rpcIndex].m_totalBytes += totalBytes;
m_componentStats[netComponentIndex].m_rpcsSent[rpcIndex].m_callHistory[m_recordMetricIndex]++;
m_componentStats[netComponentIndex].m_rpcsSent[rpcIndex].m_byteHistory[m_recordMetricIndex] += totalBytes;
}
void MultiplayerStats::RecordRpcReceived(NetComponentId netComponentId, RpcIndex rpcId, uint32_t totalBytes)
{
const uint16_t netComponentIndex = aznumeric_cast<uint16_t>(netComponentId);
const uint16_t rpcIndex = aznumeric_cast<uint16_t>(rpcId);
m_componentStats[netComponentIndex].m_rpcsRecv[rpcIndex].m_totalCalls++;
m_componentStats[netComponentIndex].m_rpcsRecv[rpcIndex].m_totalBytes += totalBytes;
m_componentStats[netComponentIndex].m_rpcsRecv[rpcIndex].m_callHistory[m_recordMetricIndex]++;
m_componentStats[netComponentIndex].m_rpcsRecv[rpcIndex].m_byteHistory[m_recordMetricIndex] += totalBytes;
}
void MultiplayerStats::TickStats(AZ::TimeMs metricFrameTimeMs)
{
m_totalHistoryTimeMs = metricFrameTimeMs * static_cast<AZ::TimeMs>(RingbufferSamples);
m_recordMetricIndex = ++m_recordMetricIndex % RingbufferSamples;
}
static void CombineMetrics(MultiplayerStats::Metric& outArg1, const MultiplayerStats::Metric& arg2)
{
outArg1.m_totalCalls += arg2.m_totalCalls;
outArg1.m_totalBytes += arg2.m_totalBytes;
for (uint32_t index = 0; index < MultiplayerStats::RingbufferSamples; ++index)
{
outArg1.m_callHistory[index] += arg2.m_callHistory[index];
outArg1.m_byteHistory[index] += arg2.m_byteHistory[index];
}
}
static MultiplayerStats::Metric SumMetricVector(const AZStd::vector<MultiplayerStats::Metric>& metricVector)
{
MultiplayerStats::Metric result;
for (AZStd::size_t index = 0; index < metricVector.size(); ++index)
{
CombineMetrics(result, metricVector[index]);
}
return result;
}
MultiplayerStats::Metric MultiplayerStats::CalculateComponentPropertyUpdateSentMetrics(NetComponentId netComponentId) const
{
const uint16_t netComponentIndex = aznumeric_cast<uint16_t>(netComponentId);
return SumMetricVector(m_componentStats[netComponentIndex].m_propertyUpdatesSent);
}
MultiplayerStats::Metric MultiplayerStats::CalculateComponentPropertyUpdateRecvMetrics(NetComponentId netComponentId) const
{
const uint16_t netComponentIndex = aznumeric_cast<uint16_t>(netComponentId);
return SumMetricVector(m_componentStats[netComponentIndex].m_propertyUpdatesRecv);
}
MultiplayerStats::Metric MultiplayerStats::CalculateComponentRpcsSentMetrics(NetComponentId netComponentId) const
{
const uint16_t netComponentIndex = aznumeric_cast<uint16_t>(netComponentId);
return SumMetricVector(m_componentStats[netComponentIndex].m_rpcsSent);
}
MultiplayerStats::Metric MultiplayerStats::CalculateComponentRpcsRecvMetrics(NetComponentId netComponentId) const
{
const uint16_t netComponentIndex = aznumeric_cast<uint16_t>(netComponentId);
return SumMetricVector(m_componentStats[netComponentIndex].m_rpcsRecv);
}
MultiplayerStats::Metric MultiplayerStats::CalculateTotalPropertyUpdateSentMetrics() const
{
Metric result;
for (AZStd::size_t index = 0; index < m_componentStats.size(); ++index)
{
const NetComponentId netComponentId = aznumeric_cast<NetComponentId>(index);
CombineMetrics(result, CalculateComponentPropertyUpdateSentMetrics(netComponentId));
}
return result;
}
MultiplayerStats::Metric MultiplayerStats::CalculateTotalPropertyUpdateRecvMetrics() const
{
Metric result;
for (AZStd::size_t index = 0; index < m_componentStats.size(); ++index)
{
const NetComponentId netComponentId = aznumeric_cast<NetComponentId>(index);
CombineMetrics(result, CalculateComponentPropertyUpdateRecvMetrics(netComponentId));
}
return result;
}
MultiplayerStats::Metric MultiplayerStats::CalculateTotalRpcsSentMetrics() const
{
Metric result;
for (AZStd::size_t index = 0; index < m_componentStats.size(); ++index)
{
const NetComponentId netComponentId = aznumeric_cast<NetComponentId>(index);
CombineMetrics(result, CalculateComponentRpcsSentMetrics(netComponentId));
}
return result;
}
MultiplayerStats::Metric MultiplayerStats::CalculateTotalRpcsRecvMetrics() const
{
Metric result;
for (AZStd::size_t index = 0; index < m_componentStats.size(); ++index)
{
const NetComponentId netComponentId = aznumeric_cast<NetComponentId>(index);
CombineMetrics(result, CalculateComponentRpcsRecvMetrics(netComponentId));
}
return result;
}
}
@@ -0,0 +1,71 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Time/ITime.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/array.h>
#include <Include/MultiplayerTypes.h>
namespace AzNetworking
{
class INetworkInterface;
}
namespace Multiplayer
{
struct MultiplayerStats
{
uint64_t m_entityCount = 0;
uint64_t m_clientConnectionCount = 0;
uint64_t m_serverConnectionCount = 0;
uint64_t m_recordMetricIndex = 0;
AZ::TimeMs m_totalHistoryTimeMs = AZ::TimeMs{ 0 };
static const uint32_t RingbufferSamples = 32;
using MetricRingbuffer = AZStd::array<uint64_t, RingbufferSamples>;
struct Metric
{
uint64_t m_totalCalls = 0;
uint64_t m_totalBytes = 0;
MetricRingbuffer m_callHistory;
MetricRingbuffer m_byteHistory;
};
struct ComponentStats
{
AZStd::vector<Metric> m_propertyUpdatesSent;
AZStd::vector<Metric> m_propertyUpdatesRecv;
AZStd::vector<Metric> m_rpcsSent;
AZStd::vector<Metric> m_rpcsRecv;
};
AZStd::vector<ComponentStats> m_componentStats;
void ReserveComponentStats(NetComponentId netComponentId, uint16_t propertyCount, uint16_t rpcCount);
void RecordPropertySent(NetComponentId netComponentId, PropertyIndex propertyId, uint32_t totalBytes);
void RecordPropertyReceived(NetComponentId netComponentId, PropertyIndex propertyId, uint32_t totalBytes);
void RecordRpcSent(NetComponentId netComponentId, RpcIndex rpcId, uint32_t totalBytes);
void RecordRpcReceived(NetComponentId netComponentId, RpcIndex rpcId, uint32_t totalBytes);
void TickStats(AZ::TimeMs metricFrameTimeMs);
Metric CalculateComponentPropertyUpdateSentMetrics(NetComponentId netComponentId) const;
Metric CalculateComponentPropertyUpdateRecvMetrics(NetComponentId netComponentId) const;
Metric CalculateComponentRpcsSentMetrics(NetComponentId netComponentId) const;
Metric CalculateComponentRpcsRecvMetrics(NetComponentId netComponentId) const;
Metric CalculateTotalPropertyUpdateSentMetrics() const;
Metric CalculateTotalPropertyUpdateRecvMetrics() const;
Metric CalculateTotalRpcsSentMetrics() const;
Metric CalculateTotalRpcsRecvMetrics() const;
};
}
@@ -0,0 +1,118 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/Event.h>
#include <AzCore/Name/Name.h>
#include <AzCore/RTTI/TypeSafeIntegral.h>
#include <AzCore/std/string/fixed_string.h>
#include <AzNetworking/Serialization/ISerializer.h>
#include <AzNetworking/ConnectionLayer/ConnectionEnums.h>
namespace Multiplayer
{
//! The default number of rewindable samples for us to store.
static constexpr uint32_t RewindHistorySize = 128;
AZ_TYPE_SAFE_INTEGRAL(HostId, uint32_t);
static constexpr HostId InvalidHostId = static_cast<HostId>(-1);
AZ_TYPE_SAFE_INTEGRAL(NetEntityId, uint32_t);
static constexpr NetEntityId InvalidNetEntityId = static_cast<NetEntityId>(-1);
AZ_TYPE_SAFE_INTEGRAL(NetComponentId, uint16_t);
static constexpr NetComponentId InvalidNetComponentId = static_cast<NetComponentId>(-1);
AZ_TYPE_SAFE_INTEGRAL(PropertyIndex, uint16_t);
AZ_TYPE_SAFE_INTEGRAL(RpcIndex, uint16_t);
using LongNetworkString = AZ::CVarFixedString;
using ReliabilityType = AzNetworking::ReliabilityType;
class NetworkEntityRpcMessage;
using RpcSendEvent = AZ::Event<NetworkEntityRpcMessage&>;
// Note that we explicitly set storage classes so that sizeof() is accurate for serialized size
enum class RpcDeliveryType : uint8_t
{
None,
AuthorityToClient, // Invoked from Authority, handled on Client
AuthorityToAutonomous, // Invoked from Authority, handled on Autonomous
AutonomousToAuthority, // Invoked from Autonomous, handled on Authority
ServerToAuthority // Invoked from Server, handled on Authority
};
enum class NetEntityRole : uint8_t
{
InvalidRole, // No role
Client, // A simulated proxy on a client
Autonomous, // An autonomous proxy on a client (can execute local prediction)
Server, // A simulated proxy on a server
Authority // An authoritative proxy on a server (full authority)
};
enum class ComponentSerializationType : uint8_t
{
Properties,
Correction
};
enum class EntityIsMigrating : uint8_t
{
False,
True
};
// This is just a placeholder
// The level/prefab cooking will devise the actual solution for identifying a dynamically spawnable entity within a prefab
struct PrefabEntityId
{
AZ_TYPE_INFO(PrefabEntityId, "{EFD37465-CCAC-4E87-A825-41B4010A2C75}");
static constexpr uint32_t AllIndices = AZStd::numeric_limits<uint32_t>::max();
AZ::Name m_prefabName;
uint32_t m_entityOffset = AllIndices;
PrefabEntityId() = default;
explicit PrefabEntityId(AZ::Name name, uint32_t entityOffset = AllIndices)
: m_prefabName(name)
, m_entityOffset(entityOffset)
{
}
bool operator==(const PrefabEntityId& rhs) const
{
return m_prefabName == rhs.m_prefabName && m_entityOffset == rhs.m_entityOffset;
}
bool operator!=(const PrefabEntityId& rhs) const
{
return !(*this == rhs);
}
bool Serialize(AzNetworking::ISerializer& serializer)
{
serializer.Serialize(m_prefabName, "prefabName");
serializer.Serialize(m_entityOffset, "entityOffset");
return serializer.IsValid();
}
};
}
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::HostId);
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::NetEntityId);
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::NetComponentId);
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::PropertyIndex);
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::RpcIndex);
@@ -1,7 +1,7 @@
#pragma once
#include <AzCore/std/containers/list.h>
#include <Source/MultiplayerTypes.h>
#include <Include/MultiplayerTypes.h>
namespace AZ
{
@@ -12,15 +12,8 @@ namespace AZ
{% set Namespace = dataFiles[0].attrib['Namespace'] %}
namespace {{ Namespace }}
{
enum class ComponentTypes
{
{% for Component in dataFiles %}
{% set ComponentName = Component.attrib['Name'] %}
{{ ComponentName }},
{% endfor %}
Count
};
static_assert(ComponentTypes::Count < static_cast<ComponentTypes>(Multiplayer::InvalidNetComponentId), "ComponentId overflow");
//! Registers all multiplayer components contained within this gem with the MultiplayerComponentRegistry.
void RegisterMultiplayerComponents();
//! For reflecting multiplayer components into the serialize, edit, and behaviour contexts.
void CreateComponentDescriptors(AZStd::list<AZ::ComponentDescriptor*>& descriptors);
@@ -1,4 +1,6 @@
#include <AzCore/Component/Component.h>
#include <Source/Components/MultiplayerComponentRegistry.h>
#include <Source/NetworkEntity/INetworkEntityManager.h>
{% for Component in dataFiles %}
{% set ComponentDerived = Component.attrib['OverrideComponent']|booleanTrue %}
{% set ControllerDerived = Component.attrib['OverrideController']|booleanTrue %}
@@ -10,8 +12,38 @@
{% endfor %}
{% set Namespace = dataFiles[0].attrib['Namespace'] %}
{% for Component in dataFiles %}
{% if Component.attrib['Namespace'] != Namespace %}
#error "mismatched component namespaces detected in declared multiplayer components, expected {{ Namespace }} but found {{ Component.attrib['Namespace'] }}"
{% endif %}
{% endfor %}
namespace {{ Namespace }}
{
void RegisterMultiplayerComponents()
{
Multiplayer::MultiplayerComponentRegistry* multiplayerComponentRegistry = GetMultiplayerComponentRegistry();
Multiplayer::MultiplayerStats& stats = AZ::Interface<Multiplayer::IMultiplayer>::Get()->GetStats();
{% for Component in dataFiles %}
{% set ComponentName = Component.attrib['Name'] %}
{% set ComponentBaseName = ComponentName %}
{% if Component.attrib['OverrideComponent']|booleanTrue %}
{% set ComponentBaseName = ComponentName + "Base" %}
{% endif %}
{% set NetworkInputCount = Component.findall('NetworkInput') | len %}
{% set NetworkPropertyCount = Component.findall('NetworkProperty') | len %}
{% set RpcCount = Component.findall('RemoteProcedure') | len %}
{
Multiplayer::MultiplayerComponentRegistry::ComponentData componentData;
componentData.m_gemName = AZ::Name("{{ Namespace }}");
componentData.m_componentName = AZ::Name("{{ Component.attrib['Name'] }}");
componentData.m_componentPropertyNameLookupFunction = {{ ComponentBaseName }}::GetNetworkPropertyName;
componentData.m_componentRpcNameLookupFunction = {{ ComponentBaseName }}::GetRpcName;
{{ ComponentBaseName }}::s_netComponentId = multiplayerComponentRegistry->RegisterMultiplayerComponent(componentData);
stats.ReserveComponentStats({{ ComponentBaseName }}::s_netComponentId, static_cast<uint16_t>({{ NetworkPropertyCount }}), static_cast<uint16_t>({{ RpcCount }}));
}
{% endfor %}
}
void CreateComponentDescriptors(AZStd::list<AZ::ComponentDescriptor*>& descriptors)
{
descriptors.insert(descriptors.end(), {
@@ -227,7 +227,7 @@ AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }
#include <Source/Components/MultiplayerController.h>
#include <Source/NetworkInput/IMultiplayerComponentInput.h>
#include <Source/NetworkTime/RewindableObject.h>
#include <Source/MultiplayerTypes.h>
#include <Include/MultiplayerTypes.h>
{% call(Include) AutoComponentMacros.ParseIncludes(Component) %}
#include <{{ Include.attrib['File'] }}>
{% endcall %}
@@ -251,9 +251,6 @@ namespace {{ Component.attrib['Namespace'] }}
class {{ ComponentName }};
class {{ ControllerName }};
//! Returns a human readable name for the provided remoteProcedureId.
const char* GetRemoteProcedureName(uint16_t remoteProcedureId);
{% set RecordName = ComponentName + "Record" %}
//! @class {{RecordName }}
//! @brief A record of the changed bits in the NetworkProperties for component {{ ComponentName }}.
@@ -329,7 +326,6 @@ namespace {{ Component.attrib['Namespace'] }}
: public Multiplayer::IMultiplayerComponentInput
{
public:
static const Multiplayer::NetComponentId s_componentId = static_cast<Multiplayer::NetComponentId>({{ Component.attrib['Namespace'] }}::ComponentTypes::{{ Component.attrib['Name'] }});
Multiplayer::NetComponentId GetComponentId() const override;
INetworkInput& operator=(const INetworkInput& rhs) override;
bool Serialize(AzNetworking::ISerializer& serializer);
@@ -412,8 +408,6 @@ namespace {{ Component.attrib['Namespace'] }}
AZ_MULTIPLAYER_COMPONENT({{ Component.attrib['Namespace'] }}::{{ ComponentBaseName }}, s_{{ LowerFirst(ComponentName) }}ConcreteUuid, Multiplayer::MultiplayerComponent);
{% endif %}
static const Multiplayer::NetComponentId s_componentId = static_cast<Multiplayer::NetComponentId>({{ Component.attrib['Namespace'] }}::ComponentTypes::{{ Component.attrib['Name'] }});
static void Reflect(AZ::ReflectContext* context);
static void ReflectToEditContext(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
@@ -489,6 +483,10 @@ namespace {{ Component.attrib['Namespace'] }}
bool SerializeAutonomousToAuthorityProperties({{ RecordName }}& replicationRecord, AzNetworking::ISerializer& serializer);
void NotifyChangesAutonomousToAuthorityProperties(const {{ RecordName }}& replicationRecord) const;
//! Debug name helpers
static const char* GetNetworkPropertyName(PropertyIndex propertyIndex);
static const char* GetRpcName(RpcIndex rpcIndex);
AZStd::unique_ptr<{{ RecordName }}> m_currentRecord;
AZStd::unique_ptr<{{ ControllerName }}> m_controller;
@@ -518,6 +516,9 @@ namespace {{ Component.attrib['Namespace'] }}
{% call(Type, Name) AutoComponentMacros.ParseComponentServiceTypeAndName(Component) %}
{{ Type }}* {{ Name }} = nullptr;
{% endcall %}
static NetComponentId s_netComponentId;
friend void RegisterMultiplayerComponents();
};
}
{% endfor %}
@@ -285,15 +285,15 @@ void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(const {{ Prop
{{ AutoComponentMacros.ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }}
void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramDefines) }})
{
constexpr uint8_t rpcId = static_cast<uint8_t>({{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ UpperFirst(Property.attrib['Name']) }});
constexpr Multiplayer::NetComponentId componentId = static_cast<Multiplayer::NetComponentId>({{ Component.attrib['Namespace'] }}::ComponentTypes::{{ Component.attrib['Name'] }});
constexpr RpcIndex rpcId = static_cast<RpcIndex>({{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ UpperFirst(Property.attrib['Name']) }});
{% if Property.attrib['IsReliable']|booleanTrue %}
constexpr AzNetworking::ReliabilityType isReliable = Multiplayer::ReliabilityType::Reliable;
{% else %}
constexpr AzNetworking::ReliabilityType isReliable = Multiplayer::ReliabilityType::Unreliable;
{% endif %}
Multiplayer::NetworkEntityRpcMessage rpcMessage(Multiplayer::RpcDeliveryType::{{ InvokeFrom }}To{{ HandleOn }}, GetNetEntityId(), componentId, rpcId, isReliable);
const Multiplayer::NetComponentId netComponentId = GetParent().GetNetComponentId();
Multiplayer::NetworkEntityRpcMessage rpcMessage(Multiplayer::RpcDeliveryType::{{ InvokeFrom }}To{{ HandleOn }}, GetNetEntityId(), netComponentId, rpcId, isReliable);
{% if paramNames|count > 0 %}
{{ UpperFirst(Component.attrib['Name']) }}Internal::{{ UpperFirst(Property.attrib['Name']) }}RpcStruct rpcStruct({{ ', '.join(paramNames) }});
{% else %}
@@ -509,6 +509,7 @@ bool {{ ClassName }}::Serialize{{ AutoComponentMacros.GetNetPropertiesSetName(Re
m_{{ LowerFirst(Property.attrib['Name']) }},
"{{ Property.attrib['Name'] }}",
GetNetComponentId(),
static_cast<PropertyIndex>({{ UpperFirst(Component.attrib['Name']) }}Internal::NetworkProperties::{{ UpperFirst(Property.attrib['Name']) }}),
stats
);
{% endif %}
@@ -646,6 +647,16 @@ enum class RemoteProcedure
MAX
};
{% endmacro %}
{% macro DeclareNetworkPropertyEnumerations(Component) %}
enum class NetworkProperties
{
{% for NetworkProperty in Component.iter('NetworkProperty') %}
{{ UpperFirst(NetworkProperty.attrib['Name']) }},
{% endfor %}
MAX
};
{% endmacro %}
{#
@@ -881,6 +892,9 @@ m_{{ LowerFirst(Property.attrib['Name']) }} = m_{{ LowerFirst(Property.attrib['N
{% else %}
{% set ControllerBaseName = ControllerName %}
{% endif %}
{% set NetworkInputCount = Component.findall('NetworkInput') | len %}
{% set NetworkPropertyCount = Component.findall('NetworkProperty') | len %}
{% set RpcCount = Component.findall('RemoteProcedure') | len %}
#include "{{ includeFile }}"
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
@@ -901,9 +915,12 @@ m_{{ LowerFirst(Property.attrib['Name']) }} = m_{{ LowerFirst(Property.attrib['N
namespace {{ Component.attrib['Namespace'] }}
{
NetComponentId {{ UpperFirst(ComponentBaseName) }}::s_netComponentId = InvalidNetComponentId;
namespace {{ UpperFirst(Component.attrib['Name']) }}Internal
{
{{ DeclareRemoteProcedureEnumerations(Component)|indent(8) }}
{{ DeclareNetworkPropertyEnumerations(Component)|indent(8) }}
{{ DefineNetworkPropertyDirtyEnumeration(Component, ClassType, 'Authority', 'Authority')|indent(8) }}
{{ DefineNetworkPropertyDirtyEnumeration(Component, ClassType, 'Authority', 'Client')|indent(8) }}
{{ DefineNetworkPropertyDirtyEnumeration(Component, ClassType, 'Authority', 'Server')|indent(8) }}
@@ -1229,14 +1246,14 @@ namespace {{ Component.attrib['Namespace'] }}
Multiplayer::NetComponentId {{ ComponentBaseName }}::GetNetComponentId() const
{
return s_componentId;
return s_netComponentId;
}
#pragma warning(push)
#pragma warning(disable: 4065) // switch statement contains 'default' but no 'case' labels
bool {{ ComponentBaseName }}::HandleRpcMessage([[maybe_unused]] Multiplayer::NetEntityRole remoteRole, Multiplayer::NetworkEntityRpcMessage& message)
{
const {{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure rpcType = static_cast<{{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure>(message.GetRpcMessageType());
const {{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure rpcType = static_cast<{{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure>(message.GetRpcIndex());
switch (rpcType)
{
{{ DeclareRpcHandleCases(Component, ComponentDerived, 'Server', 'Authority', "(remoteRole == Multiplayer::NetEntityRole::Authority || remoteRole == Multiplayer::NetEntityRole::Server)" )|indent(8) }}
@@ -1379,6 +1396,35 @@ namespace {{ Component.attrib['Namespace'] }}
}
{% endif %}
const char* {{ ComponentBaseName }}::GetNetworkPropertyName([[maybe_unused]] PropertyIndex propertyIndex)
{
{% if NetworkPropertyCount > 0 %}
const {{ UpperFirst(Component.attrib['Name']) }}Internal::NetworkProperties propertyId = static_cast<{{ UpperFirst(Component.attrib['Name']) }}Internal::NetworkProperties>(propertyIndex);
switch (propertyId)
{
{% for NetworkProperty in Component.iter('NetworkProperty') %}
case {{ UpperFirst(Component.attrib['Name']) }}Internal::NetworkProperties::{{ UpperFirst(NetworkProperty.attrib['Name']) }}:
return "{{ UpperFirst(NetworkProperty.attrib['Name']) }}";
{% endfor %}
}
{% endif %}
return "Unknown network property";
}
const char* {{ ComponentBaseName }}::GetRpcName([[maybe_unused]] RpcIndex rpcIndex)
{
{% if RpcCount > 0 %}
const {{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure rpcId = static_cast<{{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure>(rpcIndex);
switch (rpcId)
{
{% for RemoteProcedure in Component.iter('RemoteProcedure') %}
case {{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ RemoteProcedure.attrib['Name'] }}:
return "{{ RemoteProcedure.attrib['Name'] }}";
{% endfor %}
}
{% endif %}
return "Unknown Rpc";
}
{% endfor %}
}
{% endfor %}
@@ -10,7 +10,7 @@
<ComponentRelation Constraint="Weak" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Source/Components/NetworkTransformComponent.h" />
<Include File="Source/MultiplayerTypes.h"/>
<Include File="Include/MultiplayerTypes.h"/>
<Include File="Source/NetworkInput/NetworkInput.h"/>
<Include File="Source/NetworkInput/NetworkInputHistory.h"/>
<Include File="Source/NetworkInput/NetworkInputVector.h"/>
@@ -2,7 +2,7 @@
<PacketGroup Name="MultiplayerPackets" PacketStart="CorePackets::PacketType::MAX">
<Include File="AzNetworking/AutoGen/CorePackets.AutoPackets.h" />
<Include File="Source/MultiplayerTypes.h" />
<Include File="Include/MultiplayerTypes.h" />
<Include File="Source/NetworkEntity/NetworkEntityRpcMessage.h" />
<Include File="Source/NetworkEntity/NetworkEntityUpdateMessage.h" />
@@ -10,7 +10,7 @@
<ComponentRelation Constraint="Weak" HasController="false" Name="TransformComponent" Namespace="AzFramework" Include="AzFramework/Components/TransformComponent.h" />
<Include File="Source/MultiplayerTypes.h"/>
<Include File="Include/MultiplayerTypes.h"/>
<NetworkProperty Type="AZ::Quaternion" Name="rotation" Init="AZ::Quaternion::CreateIdentity()" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="true" IsPredictable="true" IsPublic="true" Container="Object" ExposeToEditor="false" GenerateEventBindings="true" />
<NetworkProperty Type="AZ::Vector3" Name="translation" Init="AZ::Vector3::CreateZero()" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="true" IsPredictable="true" IsPublic="true" Container="Object" ExposeToEditor="false" GenerateEventBindings="true" />
@@ -16,7 +16,7 @@
#include <AzNetworking/Serialization/ISerializer.h>
#include <AzNetworking/DataStructures/FixedSizeBitsetView.h>
#include <Source/NetworkEntity/NetworkEntityHandle.h>
#include <Source/MultiplayerTypes.h>
#include <Include/MultiplayerTypes.h>
#include <Include/IMultiplayer.h>
//! Macro to declare bindings for a multiplayer component inheriting from MultiplayerComponent
@@ -104,13 +104,14 @@ namespace Multiplayer
template <typename TYPE>
inline void SerializeNetworkPropertyHelper
(
AzNetworking::ISerializer& serializer,
bool modifyRecord,
AzNetworking::FixedSizeBitsetView& bitset,
int32_t bitIndex,
TYPE& value,
const char* name,
[[maybe_unused]] NetComponentId componentId,
AzNetworking::ISerializer& serializer,
bool modifyRecord,
AzNetworking::FixedSizeBitsetView& bitset,
int32_t bitIndex,
TYPE& value,
const char* name,
NetComponentId componentId,
PropertyIndex propertyIndex,
MultiplayerStats& stats
)
{
@@ -131,13 +132,11 @@ namespace Multiplayer
{
if (modifyRecord)
{
stats.m_propertyUpdatesRecv++;
stats.m_propertyUpdatesRecvBytes += updateSize;
stats.RecordPropertyReceived(componentId, propertyIndex, updateSize);
}
else
{
stats.m_propertyUpdatesSent++;
stats.m_propertyUpdatesSentBytes += updateSize;
stats.RecordPropertySent(componentId, propertyIndex, updateSize);
}
}
}
@@ -0,0 +1,58 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Source/Components/MultiplayerComponentRegistry.h>
namespace Multiplayer
{
NetComponentId MultiplayerComponentRegistry::RegisterMultiplayerComponent(const ComponentData& componentData)
{
NetComponentId netComponentId = m_nextNetComponentId++;
m_componentData[netComponentId] = componentData;
return netComponentId;
}
const char* MultiplayerComponentRegistry::GetComponentGemName(NetComponentId netComponentId) const
{
const ComponentData& componentData = GetMultiplayerComponentData(netComponentId);
return componentData.m_gemName.GetCStr();
}
const char* MultiplayerComponentRegistry::GetComponentName(NetComponentId netComponentId) const
{
const ComponentData& componentData = GetMultiplayerComponentData(netComponentId);
return componentData.m_componentName.GetCStr();
}
const char* MultiplayerComponentRegistry::GetComponentPropertyName(NetComponentId netComponentId, PropertyIndex propertyIndex) const
{
const ComponentData& componentData = GetMultiplayerComponentData(netComponentId);
return componentData.m_componentPropertyNameLookupFunction(propertyIndex);
}
const char* MultiplayerComponentRegistry::GetComponentRpcName(NetComponentId netComponentId, RpcIndex rpcIndex) const
{
const ComponentData& componentData = GetMultiplayerComponentData(netComponentId);
return componentData.m_componentRpcNameLookupFunction(rpcIndex);
}
const MultiplayerComponentRegistry::ComponentData& MultiplayerComponentRegistry::GetMultiplayerComponentData(NetComponentId netComponentId) const
{
static ComponentData nullComponentData;
auto it = m_componentData.find(netComponentId);
if (it != m_componentData.end())
{
return it->second;
}
return nullComponentData;
}
}
@@ -0,0 +1,70 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Name/Name.h>
#include <AzCore/std/containers/unordered_map.h>
#include <Source/Components/MultiplayerComponent.h>
namespace Multiplayer
{
class MultiplayerComponentRegistry
{
public:
using PropertyNameLookupFunction = AZStd::function<const char*(PropertyIndex index)>;
using RpcNameLookupFunction = AZStd::function<const char* (RpcIndex index)>;
struct ComponentData
{
AZ::Name m_gemName;
AZ::Name m_componentName;
PropertyNameLookupFunction m_componentPropertyNameLookupFunction;
RpcNameLookupFunction m_componentRpcNameLookupFunction;
};
//! Registers a multiplayer component with the multiplayer system.
//! @param componentData the data associated with the component being registered
//! @return the NetComponentId assigned to this particular component
NetComponentId RegisterMultiplayerComponent(const ComponentData& componentData);
//! Returns the gem name associated with the provided NetComponentId.
//! @param netComponentId the NetComponentId to return the gem name of
//! @return the name of the gem that contains the requested component
const char* GetComponentGemName(NetComponentId netComponentId) const;
//! Returns the component name associated with the provided NetComponentId.
//! @param netComponentId the NetComponentId to return the component name of
//! @return the name of the component
const char* GetComponentName(NetComponentId netComponentId) const;
//! Returns the property name associated with the provided NetComponentId and propertyIndex.
//! @param netComponentId the NetComponentId to return the property name of
//! @param propertyIndex the index off the network property to return the property name of
//! @return the name of the network property
const char* GetComponentPropertyName(NetComponentId netComponentId, PropertyIndex propertyIndex) const;
//! Returns the Rpc name associated with the provided NetComponentId and rpcId.
//! @param netComponentId the NetComponentId to return the property name of
//! @param rpcIndex the index of the rpc to return the rpc name of
//! @return the name of the requested rpc
const char* GetComponentRpcName(NetComponentId netComponentId, RpcIndex rpcIndex) const;
//! Retrieves the stored component data for a given NetComponentId.
//! @param netComponentId the NetComponentId to return component data for
//! @return reference to the requested component data, an empty container will be returned if the NetComponentId does not exist
const ComponentData& GetMultiplayerComponentData(NetComponentId netComponentId) const;
private:
NetComponentId m_nextNetComponentId = NetComponentId{ 0 };
AZStd::unordered_map<NetComponentId, ComponentData> m_componentData;
};
}
@@ -23,7 +23,7 @@
#include <Source/NetworkEntity/EntityReplication/ReplicationRecord.h>
#include <Source/NetworkEntity/NetworkEntityHandle.h>
#include <Source/NetworkInput/IMultiplayerComponentInput.h>
#include <Source/MultiplayerTypes.h>
#include <Include/MultiplayerTypes.h>
#include <AzCore/EBus/Event.h>
namespace Multiplayer
@@ -14,10 +14,8 @@
namespace Multiplayer
{
static constexpr uint32_t Uint32Max = AZStd::numeric_limits<uint32_t>::max();
// This can be used to help mitigate client side performance when large numbers of entities are created off the network
AZ_CVAR(uint32_t, cl_ClientMaxRemoteEntitiesPendingCreationCount, Uint32Max, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Maximum number of entities that we have sent to the client, but have not had a confirmation back from the client");
AZ_CVAR(uint32_t, cl_ClientMaxRemoteEntitiesPendingCreationCount, AZStd::numeric_limits<uint32_t>::max(), nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Maximum number of entities that we have sent to the client, but have not had a confirmation back from the client");
AZ_CVAR(AZ::TimeMs, cl_ClientEntityReplicatorPendingRemovalTimeMs, AZ::TimeMs{ 10000 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "How long should wait prior to removing an entity for the client through a change in the replication window, entity deletes are still immediate");
ClientToServerConnectionData::ClientToServerConnectionData
@@ -14,11 +14,9 @@
namespace Multiplayer
{
static constexpr uint32_t Uint32Max = AZStd::numeric_limits<uint32_t>::max();
// This can be used to help mitigate client side performance when large numbers of entities are created off the network
AZ_CVAR(uint32_t, sv_ClientMaxRemoteEntitiesPendingCreationCount, Uint32Max, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Maximum number of entities that we have sent to the client, but have not had a confirmation back from the client");
AZ_CVAR(uint32_t, sv_ClientMaxRemoteEntitiesPendingCreationCountPostInit, Uint32Max, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Maximum number of entities that we will send to clients after gameplay has begun");
AZ_CVAR(uint32_t, sv_ClientMaxRemoteEntitiesPendingCreationCount, AZStd::numeric_limits<uint32_t>::max(), nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Maximum number of entities that we have sent to the client, but have not had a confirmation back from the client");
AZ_CVAR(uint32_t, sv_ClientMaxRemoteEntitiesPendingCreationCountPostInit, AZStd::numeric_limits<uint32_t>::max(), nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Maximum number of entities that we will send to clients after gameplay has begun");
AZ_CVAR(AZ::TimeMs, sv_ClientEntityReplicatorPendingRemovalTimeMs, AZ::TimeMs{ 10000 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "How long should wait prior to removing an entity for the client through a change in the replication window, entity deletes are still immediate");
ServerToClientConnectionData::ServerToClientConnectionData
@@ -60,63 +60,227 @@ namespace Multiplayer
{
if (ImGui::BeginMenu("Multiplayer"))
{
//{
// static int lossPercent{ 0 };
// lossPercent = static_cast<int>(net_UdpDebugLossPercent);
// if (ImGui::SliderInt("UDP Loss Percent", &lossPercent, 0, 100))
// {
// net_UdpDebugLossPercent = lossPercent;
// m_ClientAgent.UpdateConnectionCvars(net_UdpDebugLossPercent);
// }
//}
//
//{
// static int latency{ 0 };
// latency = static_cast<int>(net_UdpDebugLatencyMs);
// if (ImGui::SliderInt("UDP Latency Ms", &latency, 0, 3000))
// {
// net_UdpDebugLatencyMs = latency;
// m_ClientAgent.UpdateConnectionCvars(net_UdpDebugLatencyMs);
// }
//}
//
//{
// static int variance{ 0 };
// variance = static_cast<int>(net_UdpDebugVarianceMs);
// if (ImGui::SliderInt("UDP Variance Ms", &variance, 0, 1000))
// {
// net_UdpDebugVarianceMs = variance;
// m_ClientAgent.UpdateConnectionCvars(net_UdpDebugVarianceMs);
// }
//}
ImGui::Checkbox("Multiplayer Stats", &m_displayStats);
ImGui::Checkbox("Networking Stats", &m_displayNetworkingStats);
ImGui::Checkbox("Multiplayer Stats", &m_displayMultiplayerStats);
ImGui::EndMenu();
}
}
void AccumulatePerSecondValues(const MultiplayerStats& stats, const MultiplayerStats::Metric& metric, float& outCallsPerSecond, float& outBytesPerSecond)
{
uint64_t summedCalls = 0;
uint64_t summedBytes = 0;
for (uint32_t index = 0; index < MultiplayerStats::RingbufferSamples; ++index)
{
summedCalls += metric.m_callHistory[index];
summedBytes += metric.m_byteHistory[index];
}
const float totalTimeSeconds = static_cast<float>(stats.m_totalHistoryTimeMs) / 1000.0f;
outCallsPerSecond += (summedCalls > 0 && totalTimeSeconds > 0.0f) ? static_cast<float>(summedCalls) / totalTimeSeconds : 0.0f;
outBytesPerSecond += (summedBytes > 0 && totalTimeSeconds > 0.0f) ? static_cast<float>(summedBytes) / totalTimeSeconds : 0.0f;
}
bool DrawMetricsRow(const char* name, bool expandable, uint64_t totalCalls, uint64_t totalBytes, float callsPerSecond, float bytesPerSecond)
{
const ImGuiTreeNodeFlags flags = expandable ? ImGuiTreeNodeFlags_SpanFullWidth
: (ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::TableNextRow();
ImGui::TableNextColumn();
const bool open = ImGui::TreeNodeEx(name, flags);
ImGui::TableNextColumn();
ImGui::Text("%11llu", aznumeric_cast<AZ::u64>(totalCalls));
ImGui::TableNextColumn();
ImGui::Text("%11llu", aznumeric_cast<AZ::u64>(totalBytes));
ImGui::TableNextColumn();
ImGui::Text("%11.2f", callsPerSecond);
ImGui::TableNextColumn();
ImGui::Text("%11.2f", bytesPerSecond);
return open;
}
bool DrawSummaryRow(const char* name, const MultiplayerStats& stats)
{
const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateTotalPropertyUpdateSentMetrics();
const MultiplayerStats::Metric propertyUpdatesRecv = stats.CalculateTotalPropertyUpdateRecvMetrics();
const MultiplayerStats::Metric rpcsSent = stats.CalculateTotalRpcsSentMetrics();
const MultiplayerStats::Metric rpcsRecv = stats.CalculateTotalRpcsRecvMetrics();
const uint64_t totalCalls = propertyUpdatesSent.m_totalCalls + propertyUpdatesRecv.m_totalCalls + rpcsSent.m_totalCalls + rpcsRecv.m_totalCalls;
const uint64_t totalBytes = propertyUpdatesSent.m_totalBytes + propertyUpdatesRecv.m_totalBytes + rpcsSent.m_totalBytes + rpcsRecv.m_totalBytes;
float callsPerSecond = 0.0f;
float bytesPerSecond = 0.0f;
AccumulatePerSecondValues(stats, propertyUpdatesSent, callsPerSecond, bytesPerSecond);
AccumulatePerSecondValues(stats, propertyUpdatesRecv, callsPerSecond, bytesPerSecond);
AccumulatePerSecondValues(stats, rpcsSent, callsPerSecond, bytesPerSecond);
AccumulatePerSecondValues(stats, rpcsRecv, callsPerSecond, bytesPerSecond);
return DrawMetricsRow(name, true, totalCalls, totalBytes, callsPerSecond, bytesPerSecond);
}
bool DrawComponentRow(const char* name, const MultiplayerStats& stats, NetComponentId netComponentId)
{
const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateComponentPropertyUpdateSentMetrics(netComponentId);
const MultiplayerStats::Metric propertyUpdatesRecv = stats.CalculateComponentPropertyUpdateRecvMetrics(netComponentId);
const MultiplayerStats::Metric rpcsSent = stats.CalculateComponentRpcsSentMetrics(netComponentId);
const MultiplayerStats::Metric rpcsRecv = stats.CalculateComponentRpcsRecvMetrics(netComponentId);
const uint64_t totalCalls = propertyUpdatesSent.m_totalCalls + propertyUpdatesRecv.m_totalCalls + rpcsSent.m_totalCalls + rpcsRecv.m_totalCalls;
const uint64_t totalBytes = propertyUpdatesSent.m_totalBytes + propertyUpdatesRecv.m_totalBytes + rpcsSent.m_totalBytes + rpcsRecv.m_totalBytes;
float callsPerSecond = 0.0f;
float bytesPerSecond = 0.0f;
AccumulatePerSecondValues(stats, propertyUpdatesSent, callsPerSecond, bytesPerSecond);
AccumulatePerSecondValues(stats, propertyUpdatesRecv, callsPerSecond, bytesPerSecond);
AccumulatePerSecondValues(stats, rpcsSent, callsPerSecond, bytesPerSecond);
AccumulatePerSecondValues(stats, rpcsRecv, callsPerSecond, bytesPerSecond);
return DrawMetricsRow(name, true, totalCalls, totalBytes, callsPerSecond, bytesPerSecond);
}
void DrawComponentDetails(const MultiplayerStats& stats, NetComponentId netComponentId)
{
IMultiplayer* multiplayer = AZ::Interface<IMultiplayer>::Get();
{
const MultiplayerStats::Metric metric = stats.CalculateComponentPropertyUpdateSentMetrics(netComponentId);
float callsPerSecond = 0.0f;
float bytesPerSecond = 0.0f;
AccumulatePerSecondValues(stats, metric, callsPerSecond, bytesPerSecond);
if (DrawMetricsRow("PropertyUpdates Sent", true, metric.m_totalCalls, metric.m_totalBytes, callsPerSecond, bytesPerSecond))
{
const MultiplayerStats::ComponentStats& componentStats = stats.m_componentStats[aznumeric_cast<AZStd::size_t>(netComponentId)];
for (AZStd::size_t index = 0; index < componentStats.m_propertyUpdatesSent.size(); ++index)
{
const PropertyIndex propertyIndex = aznumeric_cast<PropertyIndex>(index);
const char* propertyName = multiplayer->GetComponentPropertyName(netComponentId, propertyIndex);
const MultiplayerStats::Metric& subMetric = componentStats.m_propertyUpdatesSent[index];
callsPerSecond = 0.0f;
bytesPerSecond = 0.0f;
AccumulatePerSecondValues(stats, subMetric, callsPerSecond, bytesPerSecond);
DrawMetricsRow(propertyName, false, subMetric.m_totalCalls, subMetric.m_totalBytes, callsPerSecond, bytesPerSecond);
}
ImGui::TreePop();
}
}
{
const MultiplayerStats::Metric metric = stats.CalculateComponentPropertyUpdateRecvMetrics(netComponentId);
float callsPerSecond = 0.0f;
float bytesPerSecond = 0.0f;
AccumulatePerSecondValues(stats, metric, callsPerSecond, bytesPerSecond);
if (DrawMetricsRow("PropertyUpdates Recv", true, metric.m_totalCalls, metric.m_totalBytes, callsPerSecond, bytesPerSecond))
{
const MultiplayerStats::ComponentStats& componentStats = stats.m_componentStats[aznumeric_cast<AZStd::size_t>(netComponentId)];
for (AZStd::size_t index = 0; index < componentStats.m_propertyUpdatesRecv.size(); ++index)
{
const PropertyIndex propertyIndex = aznumeric_cast<PropertyIndex>(index);
const char* propertyName = multiplayer->GetComponentPropertyName(netComponentId, propertyIndex);
const MultiplayerStats::Metric& subMetric = componentStats.m_propertyUpdatesRecv[index];
callsPerSecond = 0.0f;
bytesPerSecond = 0.0f;
AccumulatePerSecondValues(stats, subMetric, callsPerSecond, bytesPerSecond);
DrawMetricsRow(propertyName, false, subMetric.m_totalCalls, subMetric.m_totalBytes, callsPerSecond, bytesPerSecond);
}
ImGui::TreePop();
}
}
{
const MultiplayerStats::Metric metric = stats.CalculateComponentRpcsSentMetrics(netComponentId);
float callsPerSecond = 0.0f;
float bytesPerSecond = 0.0f;
AccumulatePerSecondValues(stats, metric, callsPerSecond, bytesPerSecond);
if (DrawMetricsRow("RemoteProcedures Sent", true, metric.m_totalCalls, metric.m_totalBytes, callsPerSecond, bytesPerSecond))
{
const MultiplayerStats::ComponentStats& componentStats = stats.m_componentStats[aznumeric_cast<AZStd::size_t>(netComponentId)];
for (AZStd::size_t index = 0; index < componentStats.m_rpcsSent.size(); ++index)
{
const RpcIndex rpcIndex = aznumeric_cast<RpcIndex>(index);
const char* rpcName = multiplayer->GetComponentRpcName(netComponentId, rpcIndex);
const MultiplayerStats::Metric& subMetric = componentStats.m_rpcsSent[index];
callsPerSecond = 0.0f;
bytesPerSecond = 0.0f;
AccumulatePerSecondValues(stats, subMetric, callsPerSecond, bytesPerSecond);
DrawMetricsRow(rpcName, false, subMetric.m_totalCalls, subMetric.m_totalBytes, callsPerSecond, bytesPerSecond);
}
ImGui::TreePop();
}
}
{
const MultiplayerStats::Metric metric = stats.CalculateComponentRpcsRecvMetrics(netComponentId);
float callsPerSecond = 0.0f;
float bytesPerSecond = 0.0f;
AccumulatePerSecondValues(stats, metric, callsPerSecond, bytesPerSecond);
if (DrawMetricsRow("RemoteProcedures Recv", true, metric.m_totalCalls, metric.m_totalBytes, callsPerSecond, bytesPerSecond))
{
const MultiplayerStats::ComponentStats& componentStats = stats.m_componentStats[aznumeric_cast<AZStd::size_t>(netComponentId)];
for (AZStd::size_t index = 0; index < componentStats.m_rpcsRecv.size(); ++index)
{
const RpcIndex rpcIndex = aznumeric_cast<RpcIndex>(index);
const char* rpcName = multiplayer->GetComponentRpcName(netComponentId, rpcIndex);
const MultiplayerStats::Metric& subMetric = componentStats.m_rpcsRecv[index];
callsPerSecond = 0.0f;
bytesPerSecond = 0.0f;
AccumulatePerSecondValues(stats, subMetric, callsPerSecond, bytesPerSecond);
DrawMetricsRow(rpcName, false, subMetric.m_totalCalls, subMetric.m_totalBytes, callsPerSecond, bytesPerSecond);
}
ImGui::TreePop();
}
}
}
void MultiplayerDebugSystemComponent::OnImGuiUpdate()
{
if (m_displayStats)
const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x;
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
if (m_displayMultiplayerStats)
{
if (ImGui::Begin("Multiplayer Stats", &m_displayStats, ImGuiWindowFlags_HorizontalScrollbar))
if (ImGui::Begin("Multiplayer Stats", &m_displayMultiplayerStats, ImGuiWindowFlags_HorizontalScrollbar))
{
IMultiplayer* multiplayer = AZ::Interface<IMultiplayer>::Get();
Multiplayer::MultiplayerStats& stats = multiplayer->GetStats();
const Multiplayer::MultiplayerStats& stats = multiplayer->GetStats();
ImGui::Text("Multiplayer operating in %s mode", GetEnumString(multiplayer->GetAgentType()));
ImGui::Text("Total networked entities: %llu", aznumeric_cast<AZ::u64>(stats.m_entityCount));
ImGui::Text("Total client connections: %llu", aznumeric_cast<AZ::u64>(stats.m_clientConnectionCount));
ImGui::Text("Total server connections: %llu", aznumeric_cast<AZ::u64>(stats.m_serverConnectionCount));
ImGui::Text("Total property updates sent: %llu", aznumeric_cast<AZ::u64>(stats.m_propertyUpdatesSent));
ImGui::Text("Total property updates sent bytes: %llu", aznumeric_cast<AZ::u64>(stats.m_propertyUpdatesSentBytes));
ImGui::Text("Total property updates received: %llu", aznumeric_cast<AZ::u64>(stats.m_propertyUpdatesRecv));
ImGui::Text("Total property updates received bytes: %llu", aznumeric_cast<AZ::u64>(stats.m_propertyUpdatesRecvBytes));
ImGui::Text("Total RPCs sent: %llu", aznumeric_cast<AZ::u64>(stats.m_rpcsSent));
ImGui::Text("Total RPCs sent bytes: %llu", aznumeric_cast<AZ::u64>(stats.m_rpcsSentBytes));
ImGui::Text("Total RPCs received: %llu", aznumeric_cast<AZ::u64>(stats.m_rpcsRecv));
ImGui::Text("Total RPCs received bytes: %llu", aznumeric_cast<AZ::u64>(stats.m_rpcsRecvBytes));
ImGui::NewLine();
static ImGuiTableFlags flags = ImGuiTableFlags_BordersV
| ImGuiTableFlags_BordersOuterH
| ImGuiTableFlags_Resizable
| ImGuiTableFlags_RowBg
| ImGuiTableFlags_NoBordersInBody;
if (ImGui::BeginTable("", 5, flags))
{
// The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoHide, TEXT_BASE_WIDTH * 36.0f);
ImGui::TableSetupColumn("Total Calls", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
ImGui::TableSetupColumn("Total Bytes", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
ImGui::TableSetupColumn("Calls/Sec", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
ImGui::TableSetupColumn("Bytes/Sec", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
ImGui::TableHeadersRow();
if (DrawSummaryRow("Totals", stats))
{
for (AZStd::size_t index = 0; index < stats.m_componentStats.size(); ++index)
{
const NetComponentId netComponentId = aznumeric_cast<NetComponentId>(index);
using StringLabel = AZStd::fixed_string<128>;
const StringLabel gemName = multiplayer->GetComponentGemName(netComponentId);
const StringLabel componentName = multiplayer->GetComponentName(netComponentId);
const StringLabel label = gemName + "::" + componentName;
if (DrawComponentRow(label.c_str(), stats, netComponentId))
{
DrawComponentDetails(stats, netComponentId);
ImGui::TreePop();
}
}
}
ImGui::EndTable();
}
ImGui::End();
}
ImGui::End();
}
}
#endif
@@ -51,6 +51,7 @@ namespace Multiplayer
//! @}
#endif
private:
bool m_displayStats = false;
bool m_displayNetworkingStats = false;
bool m_displayMultiplayerStats = false;
};
}
@@ -114,6 +114,9 @@ namespace Multiplayer
m_networkInterface = AZ::Interface<INetworking>::Get()->CreateNetworkInterface(AZ::Name(s_networkInterfaceName), sv_protocol, TrustZone::ExternalClientToServer, *this);
m_consoleCommandHandler.Connect(AZ::Interface<AZ::IConsole>::Get()->GetConsoleCommandInvokedEvent());
AZ::Interface<IMultiplayer>::Register(this);
//! Register our gems multiplayer components to assign NetComponentIds
RegisterMultiplayerComponents();
}
void MultiplayerSystemComponent::Deactivate()
@@ -503,6 +506,26 @@ namespace Multiplayer
handler.Connect(m_shutdownEvent);
}
const char* MultiplayerSystemComponent::GetComponentGemName(NetComponentId netComponentId) const
{
return GetMultiplayerComponentRegistry()->GetComponentGemName(netComponentId);
}
const char* MultiplayerSystemComponent::GetComponentName(NetComponentId netComponentId) const
{
return GetMultiplayerComponentRegistry()->GetComponentName(netComponentId);
}
const char* MultiplayerSystemComponent::GetComponentPropertyName(NetComponentId netComponentId, PropertyIndex propertyIndex) const
{
return GetMultiplayerComponentRegistry()->GetComponentPropertyName(netComponentId, propertyIndex);
}
const char* MultiplayerSystemComponent::GetComponentRpcName(NetComponentId netComponentId, RpcIndex rpcIndex) const
{
return GetMultiplayerComponentRegistry()->GetComponentRpcName(netComponentId, rpcIndex);
}
void MultiplayerSystemComponent::DumpStats([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
{
const MultiplayerStats& stats = GetStats();
@@ -510,14 +533,20 @@ namespace Multiplayer
AZLOG_INFO("Total networked entities: %llu", aznumeric_cast<AZ::u64>(stats.m_entityCount));
AZLOG_INFO("Total client connections: %llu", aznumeric_cast<AZ::u64>(stats.m_clientConnectionCount));
AZLOG_INFO("Total server connections: %llu", aznumeric_cast<AZ::u64>(stats.m_serverConnectionCount));
AZLOG_INFO("Total property updates sent: %llu", aznumeric_cast<AZ::u64>(stats.m_propertyUpdatesSent));
AZLOG_INFO("Total property updates sent bytes: %llu", aznumeric_cast<AZ::u64>(stats.m_propertyUpdatesSentBytes));
AZLOG_INFO("Total property updates received: %llu", aznumeric_cast<AZ::u64>(stats.m_propertyUpdatesRecv));
AZLOG_INFO("Total property updates received bytes: %llu", aznumeric_cast<AZ::u64>(stats.m_propertyUpdatesRecvBytes));
AZLOG_INFO("Total RPCs sent: %llu", aznumeric_cast<AZ::u64>(stats.m_rpcsSent));
AZLOG_INFO("Total RPCs sent bytes: %llu", aznumeric_cast<AZ::u64>(stats.m_rpcsSentBytes));
AZLOG_INFO("Total RPCs received: %llu", aznumeric_cast<AZ::u64>(stats.m_rpcsRecv));
AZLOG_INFO("Total RPCs received bytes: %llu", aznumeric_cast<AZ::u64>(stats.m_rpcsRecvBytes));
const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateTotalPropertyUpdateSentMetrics();
const MultiplayerStats::Metric propertyUpdatesRecv = stats.CalculateTotalPropertyUpdateRecvMetrics();
const MultiplayerStats::Metric rpcsSent = stats.CalculateTotalRpcsSentMetrics();
const MultiplayerStats::Metric rpcsRecv = stats.CalculateTotalRpcsRecvMetrics();
AZLOG_INFO("Total property updates sent: %llu", aznumeric_cast<AZ::u64>(propertyUpdatesSent.m_totalCalls));
AZLOG_INFO("Total property updates sent bytes: %llu", aznumeric_cast<AZ::u64>(propertyUpdatesSent.m_totalBytes));
AZLOG_INFO("Total property updates received: %llu", aznumeric_cast<AZ::u64>(propertyUpdatesRecv.m_totalCalls));
AZLOG_INFO("Total property updates received bytes: %llu", aznumeric_cast<AZ::u64>(propertyUpdatesRecv.m_totalBytes));
AZLOG_INFO("Total RPCs sent: %llu", aznumeric_cast<AZ::u64>(rpcsSent.m_totalCalls));
AZLOG_INFO("Total RPCs sent bytes: %llu", aznumeric_cast<AZ::u64>(rpcsSent.m_totalBytes));
AZLOG_INFO("Total RPCs received: %llu", aznumeric_cast<AZ::u64>(rpcsRecv.m_totalCalls));
AZLOG_INFO("Total RPCs received bytes: %llu", aznumeric_cast<AZ::u64>(rpcsRecv.m_totalBytes));
}
void MultiplayerSystemComponent::OnConsoleCommandInvoked
@@ -88,6 +88,10 @@ namespace Multiplayer
void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override;
void AddSessionInitHandler(SessionInitEvent::Handler& handler) override;
void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override;
const char* GetComponentGemName(NetComponentId netComponentId) const override;
const char* GetComponentName(NetComponentId netComponentId) const override;
const char* GetComponentPropertyName(NetComponentId netComponentId, PropertyIndex propertyIndex) const override;
const char* GetComponentRpcName(NetComponentId netComponentId, RpcIndex rpcIndex) const override;
//! @}
//! Console commands.
@@ -33,6 +33,9 @@ namespace Multiplayer
AZ_TYPE_SAFE_INTEGRAL(NetComponentId, uint16_t);
static constexpr NetComponentId InvalidNetComponentId = static_cast<NetComponentId>(-1);
AZ_TYPE_SAFE_INTEGRAL(PropertyIndex, uint16_t);
AZ_TYPE_SAFE_INTEGRAL(RpcIndex, uint16_t);
using LongNetworkString = AZ::CVarFixedString;
using ReliabilityType = AzNetworking::ReliabilityType;
@@ -111,3 +114,5 @@ namespace Multiplayer
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::HostId);
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::NetEntityId);
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::NetComponentId);
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::PropertyIndex);
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::RpcIndex);
@@ -21,6 +21,7 @@
#include <Source/NetworkEntity/INetworkEntityManager.h>
#include <Source/Components/NetBindComponent.h>
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
#include <Include/IMultiplayer.h>
#include <AzNetworking/ConnectionLayer/IConnection.h>
#include <AzNetworking/ConnectionLayer/IConnectionListener.h>
#include <AzNetworking/PacketLayer/IPacketHeader.h>
@@ -825,11 +826,12 @@ namespace Multiplayer
{
if (entityReplicator == nullptr)
{
IMultiplayer* multiplayer = AZ::Interface<IMultiplayer>::Get();
AZLOG_INFO
(
"EntityReplicationManager: Dropping remote RPC message for component %u of rpc type %d, entityId %u has already been deleted",
aznumeric_cast<uint32_t>(message.GetComponentId()),
message.GetRpcMessageType(),
"EntityReplicationManager: Dropping remote RPC message for component %s of rpc index %s, entityId %u has already been deleted",
multiplayer->GetComponentName(message.GetComponentId()),
multiplayer->GetComponentRpcName(message.GetComponentId(), message.GetRpcIndex()),
message.GetEntityId()
);
return false;
@@ -449,8 +449,7 @@ namespace Multiplayer
{
// Received rpc metrics, log rpc sent, number of bytes, and the componentId/rpcId for bandwidth metrics
MultiplayerStats& stats = AZ::Interface<IMultiplayer>::Get()->GetStats();
stats.m_rpcsSent++;
stats.m_rpcsSentBytes += entityRpcMessage.GetEstimatedSerializeSize();
stats.RecordRpcSent(entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex(), entityRpcMessage.GetEstimatedSerializeSize());
m_replicationManager.AddDeferredRpcMessage(entityRpcMessage);
}
@@ -604,7 +603,7 @@ namespace Multiplayer
aznumeric_cast<uint32_t>(GetRemoteNetworkRole()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetComponentId()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcMessageType()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcIndex()),
entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false",
IsMarkedForRemoval() ? "true" : "false"
);
@@ -621,7 +620,7 @@ namespace Multiplayer
aznumeric_cast<uint32_t>(GetRemoteNetworkRole()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetComponentId()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcMessageType()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcIndex()),
entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false",
IsMarkedForRemoval() ? "true" : "false"
);
@@ -633,8 +632,7 @@ namespace Multiplayer
{
// Received rpc metrics, log rpc received, time spent, number of bytes, and the componentId/rpcId for bandwidth metrics
MultiplayerStats& stats = AZ::Interface<IMultiplayer>::Get()->GetStats();
stats.m_rpcsRecv++;
stats.m_rpcsRecvBytes += entityRpcMessage.GetEstimatedSerializeSize();
stats.RecordRpcReceived(entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex(), entityRpcMessage.GetEstimatedSerializeSize());
if (!m_netBindComponent)
{
@@ -646,7 +644,7 @@ namespace Multiplayer
aznumeric_cast<uint32_t>(GetRemoteNetworkRole()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetComponentId()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcMessageType()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcIndex()),
entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false",
IsMarkedForRemoval() ? "true" : "false"
);
@@ -15,7 +15,7 @@
#include <AzNetworking/DataStructures/FixedSizeVectorBitset.h>
#include <AzNetworking/Serialization/ISerializer.h>
#include <AzNetworking/Utilities/NetworkCommon.h>
#include <Source/MultiplayerTypes.h>
#include <Include/MultiplayerTypes.h>
namespace Multiplayer
{
@@ -12,7 +12,7 @@
#pragma once
#include <Source/MultiplayerTypes.h>
#include <Include/MultiplayerTypes.h>
#include <Source/NetworkEntity/NetworkEntityHandle.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/EBus/Event.h>
@@ -23,6 +23,7 @@ namespace Multiplayer
class NetworkEntityTracker;
class NetworkEntityAuthorityTracker;
class NetworkEntityRpcMessage;
class MultiplayerComponentRegistry;
using EntityExitDomainEvent = AZ::Event<const ConstNetworkEntityHandle&>;
using ControllersActivatedEvent = AZ::Event<const ConstNetworkEntityHandle&, EntityIsMigrating>;
@@ -48,6 +49,10 @@ namespace Multiplayer
//! @return the NetworkEntityAuthorityTracker for this INetworkEntityManager instance
virtual NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker() = 0;
//! Returns the MultiplayerComponentRegistry for this INetworkEntityManager instance.
//! @return the MultiplayerComponentRegistry for this INetworkEntityManager instance
virtual MultiplayerComponentRegistry* GetMultiplayerComponentRegistry() = 0;
//! Returns the HostId for this INetworkEntityManager instance.
//! @return the HostId for this INetworkEntityManager instance
virtual HostId GetHostId() const = 0;
@@ -144,4 +149,9 @@ namespace Multiplayer
{
return GetNetworkEntityManager()->GetNetworkEntityAuthorityTracker();
}
inline MultiplayerComponentRegistry* GetMultiplayerComponentRegistry()
{
return GetNetworkEntityManager()->GetMultiplayerComponentRegistry();
}
}
@@ -13,7 +13,7 @@
#pragma once
#include <AzCore/Component/Entity.h>
#include <Source/MultiplayerTypes.h>
#include <Include/MultiplayerTypes.h>
namespace Multiplayer
{
@@ -62,6 +62,11 @@ namespace Multiplayer
return &m_networkEntityAuthorityTracker;
}
MultiplayerComponentRegistry* NetworkEntityManager::GetMultiplayerComponentRegistry()
{
return &m_multiplayerComponentRegistry;
}
HostId NetworkEntityManager::GetHostId() const
{
return m_hostId;
@@ -21,7 +21,7 @@
#include <Source/NetworkEntity/NetworkEntityRpcMessage.h>
#include <Source/EntityDomains/IEntityDomain.h>
#include <Source/NetworkEntity/NetworkSpawnableLibrary.h>
#include <Source/Components/MultiplayerComponentRegistry.h>
namespace Multiplayer
{
@@ -42,6 +42,7 @@ namespace Multiplayer
//! @{
NetworkEntityTracker* GetNetworkEntityTracker() override;
NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker() override;
MultiplayerComponentRegistry* GetMultiplayerComponentRegistry() override;
HostId GetHostId() const override;
ConstNetworkEntityHandle GetEntity(NetEntityId netEntityId) const override;
@@ -85,6 +86,8 @@ namespace Multiplayer
NetworkEntityTracker m_networkEntityTracker;
NetworkEntityAuthorityTracker m_networkEntityAuthorityTracker;
MultiplayerComponentRegistry m_multiplayerComponentRegistry;
AZ::ScheduledEvent m_removeEntitiesEvent;
AZStd::vector<NetEntityId> m_removeList;
AZStd::unique_ptr<IEntityDomain> m_entityDomain;
@@ -21,7 +21,7 @@ namespace Multiplayer
: m_rpcDeliveryType(rhs.m_rpcDeliveryType)
, m_entityId(rhs.m_entityId)
, m_componentId(rhs.m_componentId)
, m_rpcMessageType(rhs.m_rpcMessageType)
, m_rpcIndex(rhs.m_rpcIndex)
, m_data(AZStd::move(rhs.m_data))
, m_isReliable(rhs.m_isReliable)
{
@@ -32,7 +32,7 @@ namespace Multiplayer
: m_rpcDeliveryType(rhs.m_rpcDeliveryType)
, m_entityId(rhs.m_entityId)
, m_componentId(rhs.m_componentId)
, m_rpcMessageType(rhs.m_rpcMessageType)
, m_rpcIndex(rhs.m_rpcIndex)
, m_isReliable(rhs.m_isReliable)
{
if (rhs.m_data != nullptr)
@@ -42,11 +42,11 @@ namespace Multiplayer
}
}
NetworkEntityRpcMessage::NetworkEntityRpcMessage(RpcDeliveryType rpcDeliveryType, NetEntityId entityId, NetComponentId componentId, uint8_t rpcMessageType, ReliabilityType isReliable)
NetworkEntityRpcMessage::NetworkEntityRpcMessage(RpcDeliveryType rpcDeliveryType, NetEntityId entityId, NetComponentId componentId, RpcIndex rpcIndex, ReliabilityType isReliable)
: m_rpcDeliveryType(rpcDeliveryType)
, m_entityId(entityId)
, m_componentId(componentId)
, m_rpcMessageType(rpcMessageType)
, m_rpcIndex(rpcIndex)
, m_isReliable(isReliable)
{
;
@@ -57,7 +57,7 @@ namespace Multiplayer
m_rpcDeliveryType = rhs.m_rpcDeliveryType;
m_entityId = rhs.m_entityId;
m_componentId = rhs.m_componentId;
m_rpcMessageType = rhs.m_rpcMessageType;
m_rpcIndex = rhs.m_rpcIndex;
m_isReliable = rhs.m_isReliable;
m_data = AZStd::move(rhs.m_data);
return *this;
@@ -68,7 +68,7 @@ namespace Multiplayer
m_rpcDeliveryType = rhs.m_rpcDeliveryType;
m_entityId = rhs.m_entityId;
m_componentId = rhs.m_componentId;
m_rpcMessageType = rhs.m_rpcMessageType;
m_rpcIndex = rhs.m_rpcIndex;
m_isReliable = rhs.m_isReliable;
if (rhs.m_data != nullptr)
{
@@ -85,7 +85,7 @@ namespace Multiplayer
return ((m_rpcDeliveryType == rhs.m_rpcDeliveryType)
&& (m_entityId == rhs.m_entityId)
&& (m_componentId == rhs.m_componentId)
&& (m_rpcMessageType == rhs.m_rpcMessageType));
&& (m_rpcIndex == rhs.m_rpcIndex));
}
bool NetworkEntityRpcMessage::operator !=(const NetworkEntityRpcMessage& rhs) const
@@ -98,7 +98,7 @@ namespace Multiplayer
static constexpr uint32_t sizeOfFields = sizeof(RpcDeliveryType)
+ sizeof(NetEntityId)
+ sizeof(NetComponentId)
+ sizeof(uint8_t);
+ sizeof(RpcIndex);
// 2-byte size header + the actual blob payload itself
const uint32_t sizeOfBlob = (m_data != nullptr) ? sizeof(uint16_t) + m_data->GetSize() : 0;
@@ -127,9 +127,9 @@ namespace Multiplayer
return m_componentId;
}
uint8_t NetworkEntityRpcMessage::GetRpcMessageType() const
RpcIndex NetworkEntityRpcMessage::GetRpcIndex() const
{
return m_rpcMessageType;
return m_rpcIndex;
}
bool NetworkEntityRpcMessage::SetRpcParams(IRpcParamStruct& params)
@@ -167,7 +167,7 @@ namespace Multiplayer
serializer.Serialize(m_rpcDeliveryType, "RpcDeliveryType");
serializer.Serialize(m_entityId, "EntityId");
serializer.Serialize(m_componentId, "ComponentId");
serializer.Serialize(m_rpcMessageType, "RpcMessageType");
serializer.Serialize(m_rpcIndex, "RpcIndex");
// m_data should never be nullptr, it contains serialized data for our Rpc params struct
if (m_data == nullptr)
@@ -14,7 +14,7 @@
#include <AzNetworking/Serialization/ISerializer.h>
#include <AzNetworking/DataStructures/ByteBuffer.h>
#include <Source/MultiplayerTypes.h>
#include <Include/MultiplayerTypes.h>
namespace Multiplayer
{
@@ -35,12 +35,12 @@ namespace Multiplayer
NetworkEntityRpcMessage(const NetworkEntityRpcMessage& rhs);
//! Fill explicit constructor.
//! @param rpcDeliveryType the delivery type (origin and target) for this RPC
//! @param entityId the networked entityId of the entity handling this RPC
//! @param componentType the networked componentId of the component handling this RPC
//! @param rpcMessageType the component defined RPC type, so the component knows which RPC this message corresponds to
//! @param isReliable whether or not this RPC should be sent reliably
explicit NetworkEntityRpcMessage(RpcDeliveryType rpcDeliveryType, NetEntityId entityId, NetComponentId componentId, uint8_t rpcMessageType, ReliabilityType isReliable);
//! @param rpcDeliveryType the delivery type (origin and target) for this rpc
//! @param entityId the networked entityId of the entity handling this rpc
//! @param componentType the networked componentId of the component handling this rpc
//! @param rpcIndex the component defined rpc index, so the component knows which rpc this message corresponds to
//! @param isReliable whether or not this rpc should be sent reliably
explicit NetworkEntityRpcMessage(RpcDeliveryType rpcDeliveryType, NetEntityId entityId, NetComponentId componentId, RpcIndex rpcIndex, ReliabilityType isReliable);
NetworkEntityRpcMessage& operator =(NetworkEntityRpcMessage&& rhs);
NetworkEntityRpcMessage& operator =(const NetworkEntityRpcMessage& rhs);
@@ -67,9 +67,9 @@ namespace Multiplayer
//! @return the current value of EntityComponentType
NetComponentId GetComponentId() const;
//! Gets the current value of RpcMessageType.
//! @return the current value of RpcMessageType
uint8_t GetRpcMessageType() const;
//! Gets the current value of RpcIndex.
//! @return the current value of RpcIndex
RpcIndex GetRpcIndex() const;
//! Writes the data contained inside a_Params to this NetworkEntityRpcMessage's blob buffer.
//! @param params the parameters to save inside this NetworkEntityRpcMessage instance
@@ -98,7 +98,7 @@ namespace Multiplayer
RpcDeliveryType m_rpcDeliveryType = RpcDeliveryType::None;
NetEntityId m_entityId = InvalidNetEntityId;
NetComponentId m_componentId = InvalidNetComponentId;
uint8_t m_rpcMessageType = 0;
RpcIndex m_rpcIndex = RpcIndex{ 0 };
// Only allocated if we actually have data
// This is to prevent blowing out stack memory if we declare an array of these EntityUpdateMessages
@@ -12,7 +12,7 @@
#pragma once
#include <Source/MultiplayerTypes.h>
#include <Include/MultiplayerTypes.h>
#include <Source/NetworkEntity/NetworkEntityHandle.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/Component/Entity.h>
@@ -131,7 +131,7 @@ namespace Multiplayer
}
// 2-byte size header + the actual blob payload itself
const uint32_t sizeOfBlob = (m_data != nullptr) ? sizeof(uint16_t) + m_data->GetSize() : 0;
const uint32_t sizeOfBlob = (m_data != nullptr) ? sizeof(PropertyIndex) + m_data->GetSize() : 0;
if (m_hasValidPrefabId)
{
@@ -15,7 +15,7 @@
#include <AzNetworking/Serialization/ISerializer.h>
#include <AzNetworking/DataStructures/ByteBuffer.h>
#include <AzCore/Name/Name.h>
#include <Source/MultiplayerTypes.h>
#include <Include/MultiplayerTypes.h>
namespace Multiplayer
{
@@ -46,6 +46,7 @@ namespace Multiplayer
const AZ::Name name = AZ::Name(relativePath);
m_spawnables[name] = id;
m_spawnablesReverseLookup[id] = name;
}
void NetworkSpawnableLibrary::OnCatalogLoaded([[maybe_unused]] const char* catalogFile)
@@ -12,7 +12,7 @@
#pragma once
#include <Source/MultiplayerTypes.h>
#include <Include/MultiplayerTypes.h>
#include <AzNetworking/DataStructures/FixedSizeBitset.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
@@ -12,7 +12,7 @@
#pragma once
#include <Source/MultiplayerTypes.h>
#include <Include/MultiplayerTypes.h>
#include <Source/NetworkEntity/NetworkEntityHandle.h>
#include <AzCore/std/containers/unordered_map.h>
@@ -11,11 +11,13 @@
set(FILES
Include/IMultiplayer.h
Include/MultiplayerStats.cpp
Include/MultiplayerStats.h
Include/MultiplayerTypes.h
Source/Multiplayer_precompiled.cpp
Source/Multiplayer_precompiled.h
Source/MultiplayerSystemComponent.cpp
Source/MultiplayerSystemComponent.h
Source/MultiplayerTypes.h
Source/AutoGen/AutoComponent_Header.jinja
Source/AutoGen/AutoComponent_Source.jinja
Source/AutoGen/AutoComponent_Common.jinja
@@ -26,6 +28,8 @@ set(FILES
Source/AutoGen/NetworkTransformComponent.AutoComponent.xml
Source/Components/LocalPredictionPlayerInputComponent.cpp
Source/Components/LocalPredictionPlayerInputComponent.h
Source/Components/MultiplayerComponentRegistry.cpp
Source/Components/MultiplayerComponentRegistry.h
Source/Components/MultiplayerComponent.cpp
Source/Components/MultiplayerComponent.h
Source/Components/MultiplayerController.cpp