Ajout de Jolt Physics + 1ere version des factory entitecomposants - camera, transform, rigidbody, collider, renderer
This commit is contained in:
433
lib/All/JoltPhysics/TestFramework/Application/Application.cpp
Normal file
433
lib/All/JoltPhysics/TestFramework/Application/Application.cpp
Normal file
@@ -0,0 +1,433 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Application/Application.h>
|
||||
#include <UI/UIManager.h>
|
||||
#include <Application/DebugUI.h>
|
||||
#include <Utils/Log.h>
|
||||
#include <Utils/CustomMemoryHook.h>
|
||||
#include <Jolt/Core/Factory.h>
|
||||
#include <Jolt/RegisterTypes.h>
|
||||
#include <Renderer/DebugRendererImp.h>
|
||||
#ifdef JPH_PLATFORM_WINDOWS
|
||||
#include <crtdbg.h>
|
||||
#include <Input/Win/KeyboardWin.h>
|
||||
#include <Input/Win/MouseWin.h>
|
||||
#include <Window/ApplicationWindowWin.h>
|
||||
#elif defined(JPH_PLATFORM_LINUX)
|
||||
#include <Input/Linux/KeyboardLinux.h>
|
||||
#include <Input/Linux/MouseLinux.h>
|
||||
#include <Window/ApplicationWindowLinux.h>
|
||||
#elif defined(JPH_PLATFORM_MACOS)
|
||||
#include <Input/MacOS/KeyboardMacOS.h>
|
||||
#include <Input/MacOS/MouseMacOS.h>
|
||||
#include <Window/ApplicationWindowMacOS.h>
|
||||
#endif
|
||||
|
||||
JPH_GCC_SUPPRESS_WARNING("-Wswitch")
|
||||
|
||||
// Constructor
|
||||
Application::Application(const char *inApplicationName, [[maybe_unused]] const String &inCommandLine) :
|
||||
mDebugRenderer(nullptr),
|
||||
mRenderer(nullptr),
|
||||
mKeyboard(nullptr),
|
||||
mMouse(nullptr),
|
||||
mUI(nullptr),
|
||||
mDebugUI(nullptr)
|
||||
{
|
||||
#if defined(JPH_PLATFORM_WINDOWS) && defined(_DEBUG)
|
||||
// Enable leak detection
|
||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
||||
#endif
|
||||
|
||||
// Register trace implementation
|
||||
Trace = TraceImpl;
|
||||
|
||||
#ifdef JPH_ENABLE_ASSERTS
|
||||
// Register assert failed handler
|
||||
AssertFailed = [](const char *inExpression, const char *inMessage, const char *inFile, uint inLine)
|
||||
{
|
||||
Trace("%s (%d): Assert Failed: %s", inFile, inLine, inMessage != nullptr? inMessage : inExpression);
|
||||
return true;
|
||||
};
|
||||
#endif // JPH_ENABLE_ASSERTS
|
||||
|
||||
// Create factory
|
||||
Factory::sInstance = new Factory;
|
||||
|
||||
// Register physics types with the factory
|
||||
RegisterTypes();
|
||||
|
||||
{
|
||||
// Disable allocation checking
|
||||
DisableCustomMemoryHook dcmh;
|
||||
|
||||
// Create window
|
||||
#ifdef JPH_PLATFORM_WINDOWS
|
||||
mWindow = new ApplicationWindowWin;
|
||||
#elif defined(JPH_PLATFORM_LINUX)
|
||||
mWindow = new ApplicationWindowLinux;
|
||||
#elif defined(JPH_PLATFORM_MACOS)
|
||||
mWindow = new ApplicationWindowMacOS;
|
||||
#else
|
||||
#error No window defined
|
||||
#endif
|
||||
mWindow->Initialize(inApplicationName);
|
||||
|
||||
// Create renderer
|
||||
mRenderer = Renderer::sCreate();
|
||||
mRenderer->Initialize(mWindow);
|
||||
|
||||
// Create font
|
||||
Font *font = new Font(mRenderer);
|
||||
font->Create("Roboto-Regular", 24);
|
||||
mFont = font;
|
||||
|
||||
// Init debug renderer
|
||||
mDebugRenderer = new DebugRendererImp(mRenderer, mFont);
|
||||
|
||||
// Init keyboard
|
||||
#ifdef JPH_PLATFORM_WINDOWS
|
||||
mKeyboard = new KeyboardWin;
|
||||
#elif defined(JPH_PLATFORM_LINUX)
|
||||
mKeyboard = new KeyboardLinux;
|
||||
#elif defined(JPH_PLATFORM_MACOS)
|
||||
mKeyboard = new KeyboardMacOS;
|
||||
#else
|
||||
#error No keyboard defined
|
||||
#endif
|
||||
mKeyboard->Initialize(mWindow);
|
||||
|
||||
// Init mouse
|
||||
#ifdef JPH_PLATFORM_WINDOWS
|
||||
mMouse = new MouseWin;
|
||||
#elif defined(JPH_PLATFORM_LINUX)
|
||||
mMouse = new MouseLinux;
|
||||
#elif defined(JPH_PLATFORM_MACOS)
|
||||
mMouse = new MouseMacOS;
|
||||
#else
|
||||
#error No mouse defined
|
||||
#endif
|
||||
mMouse->Initialize(mWindow);
|
||||
|
||||
// Init UI
|
||||
mUI = new UIManager(mRenderer);
|
||||
mUI->SetVisible(false);
|
||||
|
||||
// Init debug UI
|
||||
mDebugUI = new DebugUI(mUI, mFont);
|
||||
}
|
||||
|
||||
// Get initial time
|
||||
mLastUpdateTime = chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
// Destructor
|
||||
Application::~Application()
|
||||
{
|
||||
{
|
||||
// Disable allocation checking
|
||||
DisableCustomMemoryHook dcmh;
|
||||
|
||||
delete mDebugUI;
|
||||
delete mUI;
|
||||
delete mMouse;
|
||||
delete mKeyboard;
|
||||
delete mDebugRenderer;
|
||||
mFont = nullptr;
|
||||
delete mRenderer;
|
||||
delete mWindow;
|
||||
}
|
||||
|
||||
// Unregisters all types with the factory and cleans up the default material
|
||||
UnregisterTypes();
|
||||
|
||||
delete Factory::sInstance;
|
||||
Factory::sInstance = nullptr;
|
||||
}
|
||||
|
||||
String Application::sCreateCommandLine(int inArgC, char **inArgV)
|
||||
{
|
||||
String command_line;
|
||||
for (int i = 0; i < inArgC; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
command_line += " ";
|
||||
command_line += inArgV[i];
|
||||
}
|
||||
return command_line;
|
||||
}
|
||||
|
||||
// Clear debug lines / triangles / texts that have been accumulated
|
||||
void Application::ClearDebugRenderer()
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
static_cast<DebugRendererImp *>(mDebugRenderer)->Clear();
|
||||
|
||||
mDebugRendererCleared = true;
|
||||
}
|
||||
|
||||
// Main loop
|
||||
void Application::Run()
|
||||
{
|
||||
// Set initial camera position
|
||||
ResetCamera();
|
||||
|
||||
// Enter the main loop
|
||||
mWindow->MainLoop([this]() { return RenderFrame(); });
|
||||
}
|
||||
|
||||
bool Application::RenderFrame()
|
||||
{
|
||||
// Get new input
|
||||
mKeyboard->Poll();
|
||||
mMouse->Poll();
|
||||
|
||||
// Handle keyboard input
|
||||
for (EKey key = mKeyboard->GetFirstKey(); key != EKey::Invalid; key = mKeyboard->GetNextKey())
|
||||
switch (key)
|
||||
{
|
||||
case EKey::P:
|
||||
mIsPaused = !mIsPaused;
|
||||
break;
|
||||
|
||||
case EKey::O:
|
||||
mSingleStep = true;
|
||||
break;
|
||||
|
||||
case EKey::T:
|
||||
// Dump timing info to file
|
||||
JPH_PROFILE_DUMP();
|
||||
break;
|
||||
|
||||
case EKey::Escape:
|
||||
mDebugUI->ToggleVisibility();
|
||||
break;
|
||||
}
|
||||
|
||||
// Calculate delta time
|
||||
chrono::high_resolution_clock::time_point time = chrono::high_resolution_clock::now();
|
||||
chrono::microseconds delta = chrono::duration_cast<chrono::microseconds>(time - mLastUpdateTime);
|
||||
mLastUpdateTime = time;
|
||||
float clock_delta_time = 1.0e-6f * delta.count();
|
||||
float world_delta_time = 0.0f;
|
||||
if (mRequestedDeltaTime <= 0.0f)
|
||||
{
|
||||
// If no fixed frequency update is requested, update with variable time step
|
||||
world_delta_time = !mIsPaused || mSingleStep? clock_delta_time : 0.0f;
|
||||
mResidualDeltaTime = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Else use fixed time steps
|
||||
if (mSingleStep)
|
||||
{
|
||||
// Single step
|
||||
world_delta_time = mRequestedDeltaTime;
|
||||
}
|
||||
else if (!mIsPaused)
|
||||
{
|
||||
// Calculate how much time has passed since the last render
|
||||
world_delta_time = clock_delta_time + mResidualDeltaTime;
|
||||
if (world_delta_time < mRequestedDeltaTime)
|
||||
{
|
||||
// Too soon, set the residual time and don't update
|
||||
mResidualDeltaTime = world_delta_time;
|
||||
world_delta_time = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update and clamp the residual time to a full update to avoid spiral of death
|
||||
mResidualDeltaTime = min(mRequestedDeltaTime, world_delta_time - mRequestedDeltaTime);
|
||||
world_delta_time = mRequestedDeltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
mSingleStep = false;
|
||||
|
||||
// Clear debug lines if we're going to step
|
||||
if (world_delta_time > 0.0f)
|
||||
ClearDebugRenderer();
|
||||
|
||||
{
|
||||
JPH_PROFILE("UpdateFrame");
|
||||
if (!UpdateFrame(world_delta_time))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Draw coordinate axis
|
||||
if (mDebugRendererCleared)
|
||||
mDebugRenderer->DrawCoordinateSystem(RMat44::sIdentity());
|
||||
|
||||
// For next frame: mark that we haven't cleared debug stuff
|
||||
mDebugRendererCleared = false;
|
||||
|
||||
// Update the camera position
|
||||
if (!mUI->IsVisible())
|
||||
UpdateCamera(clock_delta_time);
|
||||
|
||||
// Start rendering
|
||||
if (!mRenderer->BeginFrame(mWorldCamera, GetWorldScale()))
|
||||
return true;
|
||||
|
||||
// Draw from light
|
||||
static_cast<DebugRendererImp *>(mDebugRenderer)->DrawShadowPass();
|
||||
|
||||
// Start drawing normally
|
||||
mRenderer->EndShadowPass();
|
||||
|
||||
// Draw debug information
|
||||
static_cast<DebugRendererImp *>(mDebugRenderer)->Draw();
|
||||
|
||||
// Draw the frame rate counter
|
||||
DrawFPS(clock_delta_time);
|
||||
|
||||
if (mUI->IsVisible())
|
||||
{
|
||||
// Send mouse input to UI
|
||||
bool left_pressed = mMouse->IsLeftPressed();
|
||||
if (left_pressed && !mLeftMousePressed)
|
||||
mUI->MouseDown(mMouse->GetX(), mMouse->GetY());
|
||||
else if (!left_pressed && mLeftMousePressed)
|
||||
mUI->MouseUp(mMouse->GetX(), mMouse->GetY());
|
||||
mLeftMousePressed = left_pressed;
|
||||
mUI->MouseMove(mMouse->GetX(), mMouse->GetY());
|
||||
|
||||
{
|
||||
// Disable allocation checking
|
||||
DisableCustomMemoryHook dcmh;
|
||||
|
||||
// Update and draw the menu
|
||||
mUI->Update(clock_delta_time);
|
||||
mUI->Draw();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Menu not visible, cancel any mouse operations
|
||||
mUI->MouseCancel();
|
||||
}
|
||||
|
||||
// Show the frame
|
||||
mRenderer->EndFrame();
|
||||
|
||||
// Notify of next frame
|
||||
JPH_PROFILE_NEXTFRAME();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Application::GetCameraLocalHeadingAndPitch(float &outHeading, float &outPitch)
|
||||
{
|
||||
outHeading = ATan2(mLocalCamera.mForward.GetZ(), mLocalCamera.mForward.GetX());
|
||||
outPitch = ATan2(mLocalCamera.mForward.GetY(), Vec3(mLocalCamera.mForward.GetX(), 0, mLocalCamera.mForward.GetZ()).Length());
|
||||
}
|
||||
|
||||
void Application::ConvertCameraLocalToWorld(float inCameraHeading, float inCameraPitch)
|
||||
{
|
||||
// Convert local to world space using the camera pivot
|
||||
RMat44 pivot = GetCameraPivot(inCameraHeading, inCameraPitch);
|
||||
mWorldCamera = mLocalCamera;
|
||||
mWorldCamera.mPos = pivot * mLocalCamera.mPos;
|
||||
mWorldCamera.mForward = pivot.Multiply3x3(mLocalCamera.mForward);
|
||||
mWorldCamera.mUp = pivot.Multiply3x3(mLocalCamera.mUp);
|
||||
}
|
||||
|
||||
void Application::ResetCamera()
|
||||
{
|
||||
// Get local space camera state
|
||||
mLocalCamera = CameraState();
|
||||
GetInitialCamera(mLocalCamera);
|
||||
|
||||
// Convert to world space
|
||||
float heading, pitch;
|
||||
GetCameraLocalHeadingAndPitch(heading, pitch);
|
||||
ConvertCameraLocalToWorld(heading, pitch);
|
||||
}
|
||||
|
||||
// Update camera position
|
||||
void Application::UpdateCamera(float inDeltaTime)
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
// Determine speed
|
||||
float speed = 20.0f * GetWorldScale() * inDeltaTime;
|
||||
bool shift = mKeyboard->IsKeyPressed(EKey::LShift) || mKeyboard->IsKeyPressed(EKey::RShift);
|
||||
bool control = mKeyboard->IsKeyPressed(EKey::LControl) || mKeyboard->IsKeyPressed(EKey::RControl);
|
||||
bool alt = mKeyboard->IsKeyPressed(EKey::LAlt) || mKeyboard->IsKeyPressed(EKey::RAlt);
|
||||
if (shift) speed *= 10.0f;
|
||||
else if (control) speed /= 25.0f;
|
||||
else if (alt) speed = 0.0f;
|
||||
|
||||
// Position
|
||||
Vec3 right = mLocalCamera.mForward.Cross(mLocalCamera.mUp);
|
||||
if (mKeyboard->IsKeyPressed(EKey::A)) mLocalCamera.mPos -= speed * right;
|
||||
if (mKeyboard->IsKeyPressed(EKey::D)) mLocalCamera.mPos += speed * right;
|
||||
if (mKeyboard->IsKeyPressed(EKey::W)) mLocalCamera.mPos += speed * mLocalCamera.mForward;
|
||||
if (mKeyboard->IsKeyPressed(EKey::S)) mLocalCamera.mPos -= speed * mLocalCamera.mForward;
|
||||
|
||||
// Forward
|
||||
float heading, pitch;
|
||||
GetCameraLocalHeadingAndPitch(heading, pitch);
|
||||
heading += DegreesToRadians(mMouse->GetDX() * 0.5f);
|
||||
pitch = Clamp(pitch - DegreesToRadians(mMouse->GetDY() * 0.5f), -0.49f * JPH_PI, 0.49f * JPH_PI);
|
||||
mLocalCamera.mForward = Vec3(Cos(pitch) * Cos(heading), Sin(pitch), Cos(pitch) * Sin(heading));
|
||||
|
||||
// Convert to world space
|
||||
ConvertCameraLocalToWorld(heading, pitch);
|
||||
}
|
||||
|
||||
void Application::DrawFPS(float inDeltaTime)
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
// Don't divide by zero
|
||||
if (inDeltaTime <= 0.0f)
|
||||
return;
|
||||
|
||||
// Switch tho ortho mode
|
||||
mRenderer->SetOrthoMode();
|
||||
|
||||
// Update stats
|
||||
mTotalDeltaTime += inDeltaTime;
|
||||
mNumFrames++;
|
||||
if (mNumFrames > 10)
|
||||
{
|
||||
mFPS = mNumFrames / mTotalDeltaTime;
|
||||
mNumFrames = 0;
|
||||
mTotalDeltaTime = 0.0f;
|
||||
}
|
||||
|
||||
// Create string
|
||||
String fps = StringFormat("%.1f", (double)mFPS);
|
||||
|
||||
// Get size of text on screen
|
||||
Float2 text_size = mFont->MeasureText(fps);
|
||||
int text_w = int(text_size.x * mFont->GetCharHeight());
|
||||
int text_h = int(text_size.y * mFont->GetCharHeight());
|
||||
|
||||
// Draw FPS counter
|
||||
int x = (mWindow->GetWindowWidth() - text_w) / 2 - 20;
|
||||
int y = 10;
|
||||
mUI->DrawQuad(x - 5, y - 3, text_w + 10, text_h + 6, UITexturedQuad(), Color(0, 0, 0, 128));
|
||||
mUI->DrawText(x, y, fps, mFont);
|
||||
|
||||
// Draw status string
|
||||
if (!mStatusString.empty())
|
||||
mUI->DrawText(5, 5, mStatusString, mFont);
|
||||
|
||||
// Draw paused string if the app is paused
|
||||
if (mIsPaused)
|
||||
{
|
||||
string_view paused_str = "P: Unpause, ESC: Menu";
|
||||
Float2 pause_size = mFont->MeasureText(paused_str);
|
||||
mUI->DrawText(mWindow->GetWindowWidth() - 5 - int(pause_size.x * mFont->GetCharHeight()), 5, paused_str, mFont);
|
||||
}
|
||||
|
||||
// Restore state
|
||||
mRenderer->SetProjectionMode();
|
||||
}
|
||||
121
lib/All/JoltPhysics/TestFramework/Application/Application.h
Normal file
121
lib/All/JoltPhysics/TestFramework/Application/Application.h
Normal file
@@ -0,0 +1,121 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Renderer/Font.h>
|
||||
#include <Input/Keyboard.h>
|
||||
#include <Input/Mouse.h>
|
||||
#include <Jolt/Core/Reference.h>
|
||||
|
||||
// STL includes
|
||||
JPH_SUPPRESS_WARNINGS_STD_BEGIN
|
||||
#include <chrono>
|
||||
JPH_SUPPRESS_WARNINGS_STD_END
|
||||
|
||||
class UIManager;
|
||||
class DebugUI;
|
||||
namespace JPH {
|
||||
class DebugRenderer;
|
||||
}
|
||||
|
||||
class Application
|
||||
{
|
||||
private:
|
||||
/// Camera state
|
||||
CameraState mLocalCamera;
|
||||
CameraState mWorldCamera;
|
||||
|
||||
protected:
|
||||
/// Debug renderer module
|
||||
DebugRenderer * mDebugRenderer;
|
||||
|
||||
/// Main window
|
||||
ApplicationWindow * mWindow;
|
||||
|
||||
/// Render module
|
||||
Renderer * mRenderer;
|
||||
|
||||
/// Default font
|
||||
RefConst<Font> mFont;
|
||||
|
||||
/// Input
|
||||
Keyboard * mKeyboard;
|
||||
Mouse * mMouse;
|
||||
|
||||
/// Menu
|
||||
UIManager * mUI;
|
||||
DebugUI * mDebugUI;
|
||||
|
||||
/// A string that is shown on screen to indicate the status of the application
|
||||
String mStatusString;
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
Application(const char *inApplicationName, const String &inCommandLine);
|
||||
virtual ~Application();
|
||||
|
||||
/// Create a single string command line
|
||||
static String sCreateCommandLine(int inArgC, char **inArgV);
|
||||
|
||||
/// Enter the main loop
|
||||
void Run();
|
||||
|
||||
protected:
|
||||
/// Update the application
|
||||
virtual bool UpdateFrame(float inDeltaTime) { return false; }
|
||||
|
||||
/// Pause / unpause the simulation
|
||||
void Pause(bool inPaused) { mIsPaused = inPaused; }
|
||||
|
||||
/// Programmatically single step the simulation
|
||||
void SingleStep() { mIsPaused = true; mSingleStep = true; }
|
||||
|
||||
/// Set the frequency at which we want to render frames
|
||||
void SetRenderFrequency(float inFrequency) { mRequestedDeltaTime = 1.0f / inFrequency; }
|
||||
|
||||
/// Will restore camera position to that returned by GetInitialCamera
|
||||
void ResetCamera();
|
||||
|
||||
/// Override to specify the initial camera state (local to GetCameraPivot)
|
||||
virtual void GetInitialCamera(CameraState &ioState) const { }
|
||||
|
||||
/// Override to specify a camera pivot point and orientation (world space)
|
||||
virtual RMat44 GetCameraPivot(float inCameraHeading, float inCameraPitch) const { return RMat44::sIdentity(); }
|
||||
|
||||
/// Get scale factor for this world, used to boost camera speed and to scale detail of the shadows
|
||||
virtual float GetWorldScale() const { return 1.0f; }
|
||||
|
||||
/// Get current state of the camera (world space)
|
||||
const CameraState & GetCamera() const { return mWorldCamera; }
|
||||
|
||||
/// Clear debug lines / triangles / texts that have been accumulated
|
||||
void ClearDebugRenderer();
|
||||
|
||||
private:
|
||||
/// Render a frame
|
||||
bool RenderFrame();
|
||||
|
||||
/// Extract heading and pitch from the local space (relative to the camera pivot) camera forward
|
||||
void GetCameraLocalHeadingAndPitch(float &outHeading, float &outPitch);
|
||||
|
||||
/// Convert local space camera to world space camera
|
||||
void ConvertCameraLocalToWorld(float inCameraHeading, float inCameraPitch);
|
||||
|
||||
/// Update the local and world space camera transform
|
||||
void UpdateCamera(float inDeltaTime);
|
||||
|
||||
/// Draw the frame rate counter
|
||||
void DrawFPS(float inDeltaTime);
|
||||
|
||||
chrono::high_resolution_clock::time_point mLastUpdateTime;
|
||||
bool mIsPaused = false;
|
||||
bool mSingleStep = false;
|
||||
bool mDebugRendererCleared = true;
|
||||
bool mLeftMousePressed = false;
|
||||
float mFPS = 0.0f;
|
||||
float mRequestedDeltaTime = 0.0f;
|
||||
float mResidualDeltaTime = 0.0f;
|
||||
float mTotalDeltaTime = 0.0f;
|
||||
int mNumFrames = 0;
|
||||
};
|
||||
215
lib/All/JoltPhysics/TestFramework/Application/DebugUI.cpp
Normal file
215
lib/All/JoltPhysics/TestFramework/Application/DebugUI.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Application/DebugUI.h>
|
||||
#include <Renderer/Texture.h>
|
||||
#include <UI/UIManager.h>
|
||||
#include <UI/UIImage.h>
|
||||
#include <UI/UIStaticText.h>
|
||||
#include <UI/UICheckBox.h>
|
||||
#include <UI/UIHorizontalStack.h>
|
||||
#include <UI/UIVerticalStack.h>
|
||||
#include <UI/UITextButton.h>
|
||||
#include <Image/LoadTGA.h>
|
||||
#include <Utils/Log.h>
|
||||
#include <Utils/AssetStream.h>
|
||||
|
||||
JPH_SUPPRESS_WARNINGS_STD_BEGIN
|
||||
#include <fstream>
|
||||
JPH_SUPPRESS_WARNINGS_STD_END
|
||||
|
||||
DebugUI::DebugUI(UIManager *inUIManager, const Font *inFont) :
|
||||
mUI(inUIManager),
|
||||
mFont(inFont)
|
||||
{
|
||||
// Load UI texture with commonly used UI elements
|
||||
AssetStream texture_stream("UI.tga", std::ios::in | std::ios::binary);
|
||||
Ref<Surface> texture_surface = LoadTGA(texture_stream.Get());
|
||||
if (texture_surface == nullptr)
|
||||
FatalError("Failed to load UI.tga");
|
||||
mUITexture = mUI->GetRenderer()->CreateTexture(texture_surface);
|
||||
|
||||
// Install callback that pops a layer when the deactivate animation finishes
|
||||
mUI->SetDeactivatedAction([this]() {
|
||||
mUI->PopLayer();
|
||||
});
|
||||
|
||||
// Don't want to draw any layers that are not active
|
||||
mUI->SetDrawInactiveLayers(false);
|
||||
}
|
||||
|
||||
UIElement *DebugUI::CreateMenu()
|
||||
{
|
||||
mUI->PushLayer();
|
||||
|
||||
UIImage *background = new UIImage();
|
||||
background->SetRelativeX(10);
|
||||
background->SetRelativeY(10);
|
||||
background->SetImage(UITexturedQuad(mUITexture, 0, 0, 33, 30, 4, 4, 24, 21));
|
||||
mUI->Add(background);
|
||||
|
||||
UIVerticalStack *stack = new UIVerticalStack();
|
||||
stack->SetRelativeX(10);
|
||||
stack->SetRelativeY(10);
|
||||
stack->SetPaddingRight(10);
|
||||
stack->SetPaddingBottom(10);
|
||||
background->Add(stack);
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
UIStaticText *DebugUI::CreateStaticText(UIElement *inMenu, const string_view &inText)
|
||||
{
|
||||
UIStaticText *text = new UIStaticText();
|
||||
text->SetText(inText);
|
||||
text->SetFont(mFont);
|
||||
inMenu->Add(text);
|
||||
return text;
|
||||
}
|
||||
|
||||
UITextButton *DebugUI::CreateTextButton(UIElement *inMenu, const string_view &inName, UITextButton::ClickAction inAction)
|
||||
{
|
||||
UITextButton *button = new UITextButton();
|
||||
button->SetText(inName);
|
||||
button->SetFont(mFont);
|
||||
button->SetClickAction(inAction);
|
||||
button->SetTextPadding(0, 24, 0, 0);
|
||||
button->SetPaddingRight(24);
|
||||
inMenu->Add(button);
|
||||
return button;
|
||||
}
|
||||
|
||||
UICheckBox *DebugUI::CreateCheckBox(UIElement *inMenu, const string_view &inName, bool inInitiallyChecked, UICheckBox::ClickAction inAction)
|
||||
{
|
||||
UICheckBox *check_box = new UICheckBox();
|
||||
check_box->SetUncheckedStateQuad(UITexturedQuad(mUITexture, 48, 0, 16, 16));
|
||||
check_box->SetCheckedStateQuad(UITexturedQuad(mUITexture, 65, 0, 16, 16));
|
||||
check_box->SetFont(mFont);
|
||||
check_box->SetText(inName);
|
||||
check_box->SetClickAction(inAction);
|
||||
check_box->SetState(inInitiallyChecked? UICheckBox::STATE_CHECKED : UICheckBox::STATE_UNCHECKED);
|
||||
check_box->SetPaddingRight(24);
|
||||
inMenu->Add(check_box);
|
||||
return check_box;
|
||||
}
|
||||
|
||||
UISlider *DebugUI::CreateSlider(UIElement *inMenu, const string_view &inName, float inInitialValue, float inMinValue, float inMaxValue, float inStepValue, UISlider::ValueChangedAction inAction)
|
||||
{
|
||||
UIHorizontalStack *horiz = new UIHorizontalStack();
|
||||
horiz->SetPaddingRight(24);
|
||||
inMenu->Add(horiz);
|
||||
|
||||
UIStaticText *text = new UIStaticText();
|
||||
text->SetFont(mFont);
|
||||
text->SetTextPadding(0, 24, 0, 0);
|
||||
text->SetText(inName);
|
||||
text->SetPaddingRight(20);
|
||||
horiz->Add(text);
|
||||
|
||||
UISlider *slider = new UISlider();
|
||||
slider->SetHeight(24);
|
||||
slider->SetWidth(250);
|
||||
slider->SetPaddingRight(20);
|
||||
slider->SetValue(inInitialValue);
|
||||
slider->SetRange(inMinValue, inMaxValue, inStepValue);
|
||||
slider->SetValueChangedAction(inAction);
|
||||
slider->SetSlider(UITexturedQuad(mUITexture, 44, 37, 1, 9));
|
||||
slider->SetThumb(UITexturedQuad(mUITexture, 31, 32, 11, 19));
|
||||
horiz->Add(slider);
|
||||
|
||||
UIButton *decr_button = new UIButton();
|
||||
decr_button->SetRepeat(0.5f, 0.2f);
|
||||
decr_button->SetButtonQuad(UITexturedQuad(mUITexture, 0, 31, 17, 21));
|
||||
slider->Add(decr_button);
|
||||
slider->SetDecreaseButton(decr_button);
|
||||
|
||||
UIButton *incr_button = new UIButton();
|
||||
incr_button->SetRepeat(0.5f, 0.2f);
|
||||
incr_button->SetButtonQuad(UITexturedQuad(mUITexture, 13, 31, 17, 21));
|
||||
slider->Add(incr_button);
|
||||
slider->SetIncreaseButton(incr_button);
|
||||
|
||||
UIImage *image = new UIImage();
|
||||
image->SetImage(UITexturedQuad(mUITexture, 34, 0, 13, 24, 36, 2, 9, 20));
|
||||
horiz->Add(image);
|
||||
|
||||
UIStaticText *value = new UIStaticText();
|
||||
value->SetWidth(75);
|
||||
value->SetTextPadding(0, 5, 0, 5);
|
||||
value->SetWrap(true);
|
||||
value->SetTextAlignment(UIElement::RIGHT);
|
||||
value->SetFont(mFont);
|
||||
image->Add(value);
|
||||
slider->SetStaticText(value);
|
||||
|
||||
return slider;
|
||||
}
|
||||
|
||||
UIComboBox *DebugUI::CreateComboBox(UIElement *inMenu, const string_view &inName, const Array<String> &inItems, int inInitialItem, UIComboBox::ItemChangedAction inAction)
|
||||
{
|
||||
UIHorizontalStack *horiz = new UIHorizontalStack();
|
||||
horiz->SetPaddingRight(24);
|
||||
inMenu->Add(horiz);
|
||||
|
||||
UIStaticText *text = new UIStaticText();
|
||||
text->SetFont(mFont);
|
||||
text->SetTextPadding(0, 24, 0, 0);
|
||||
text->SetText(inName);
|
||||
text->SetPaddingRight(20);
|
||||
horiz->Add(text);
|
||||
|
||||
UIComboBox *combo = new UIComboBox();
|
||||
combo->SetHeight(24);
|
||||
combo->SetWidth(250);
|
||||
combo->SetPaddingRight(20);
|
||||
combo->SetItems(inItems);
|
||||
combo->SetCurrentItem(inInitialItem);
|
||||
combo->SetItemChangedAction(inAction);
|
||||
horiz->Add(combo);
|
||||
|
||||
UIButton *prev_button = new UIButton();
|
||||
prev_button->SetRepeat(0.5f, 0.2f);
|
||||
prev_button->SetButtonQuad(UITexturedQuad(mUITexture, 0, 31, 17, 21));
|
||||
combo->Add(prev_button);
|
||||
combo->SetPreviousButton(prev_button);
|
||||
|
||||
UIButton *next_button = new UIButton();
|
||||
next_button->SetRepeat(0.5f, 0.2f);
|
||||
next_button->SetButtonQuad(UITexturedQuad(mUITexture, 13, 31, 17, 21));
|
||||
combo->Add(next_button);
|
||||
combo->SetNextButton(next_button);
|
||||
|
||||
UIStaticText *value = new UIStaticText();
|
||||
value->SetTextPadding(0, 5, 0, 5);
|
||||
value->SetWrap(false);
|
||||
value->SetTextAlignment(UIElement::CENTER);
|
||||
value->SetFont(mFont);
|
||||
combo->Add(value);
|
||||
combo->SetStaticText(value);
|
||||
|
||||
return combo;
|
||||
}
|
||||
|
||||
void DebugUI::ShowMenu(UIElement *inMenu)
|
||||
{
|
||||
UIHorizontalStack::sUniformChildWidth(inMenu);
|
||||
mUI->AutoLayout();
|
||||
mUI->SwitchToState(UIManager::STATE_ACTIVATING);
|
||||
}
|
||||
|
||||
void DebugUI::BackToMain()
|
||||
{
|
||||
while (mUI->GetNumLayers() > 2)
|
||||
mUI->PopLayer();
|
||||
}
|
||||
|
||||
void DebugUI::ToggleVisibility()
|
||||
{
|
||||
if (mUI->GetNumLayers() > 2)
|
||||
mUI->SwitchToState(UIManager::STATE_DEACTIVATING);
|
||||
else
|
||||
mUI->SetVisible(!mUI->IsVisible());
|
||||
}
|
||||
48
lib/All/JoltPhysics/TestFramework/Application/DebugUI.h
Normal file
48
lib/All/JoltPhysics/TestFramework/Application/DebugUI.h
Normal file
@@ -0,0 +1,48 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
#include <UI/UICheckBox.h>
|
||||
#include <UI/UITextButton.h>
|
||||
#include <UI/UISlider.h>
|
||||
#include <UI/UIComboBox.h>
|
||||
#include <UI/UIStaticText.h>
|
||||
|
||||
class UIManager;
|
||||
class Texture;
|
||||
class Font;
|
||||
class Keyboard;
|
||||
|
||||
class DebugUI
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
DebugUI(UIManager *inUIManager, const Font *inFont);
|
||||
|
||||
/// Create a new (sub) menu
|
||||
UIElement * CreateMenu();
|
||||
|
||||
/// Add items to the menu
|
||||
UIStaticText * CreateStaticText(UIElement *inMenu, const string_view &inText);
|
||||
UITextButton * CreateTextButton(UIElement *inMenu, const string_view &inName, UITextButton::ClickAction inAction);
|
||||
UICheckBox * CreateCheckBox(UIElement *inMenu, const string_view &inName, bool inInitiallyChecked, UICheckBox::ClickAction inAction);
|
||||
UISlider * CreateSlider(UIElement *inMenu, const string_view &inName, float inInitialValue, float inMinValue, float inMaxValue, float inStepValue, UISlider::ValueChangedAction inAction);
|
||||
UIComboBox * CreateComboBox(UIElement *inMenu, const string_view &inName, const Array<String> &inItems, int inInitialItem, UIComboBox::ItemChangedAction inAction);
|
||||
|
||||
/// Show it
|
||||
void ShowMenu(UIElement *inMenu);
|
||||
|
||||
/// Go back to the main menu
|
||||
void BackToMain();
|
||||
|
||||
/// Show or hide the entire menu
|
||||
void ToggleVisibility();
|
||||
|
||||
private:
|
||||
UIManager * mUI;
|
||||
RefConst<Font> mFont;
|
||||
RefConst<Texture> mUITexture;
|
||||
};
|
||||
74
lib/All/JoltPhysics/TestFramework/Application/EntryPoint.h
Normal file
74
lib/All/JoltPhysics/TestFramework/Application/EntryPoint.h
Normal file
@@ -0,0 +1,74 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/FPException.h>
|
||||
|
||||
#if defined(JPH_PLATFORM_WINDOWS)
|
||||
|
||||
#define ENTRY_POINT(AppName, RegisterAllocator) \
|
||||
\
|
||||
int WINAPI wWinMain(_In_ HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) \
|
||||
{ \
|
||||
RegisterAllocator(); \
|
||||
\
|
||||
JPH_PROFILE_START("Main"); \
|
||||
\
|
||||
FPExceptionsEnable enable_exceptions; \
|
||||
JPH_UNUSED(enable_exceptions); \
|
||||
\
|
||||
{ \
|
||||
AppName app(GetCommandLineA()); \
|
||||
app.Run(); \
|
||||
} \
|
||||
\
|
||||
JPH_PROFILE_END(); \
|
||||
\
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
int __cdecl main(int inArgC, char **inArgV) \
|
||||
{ \
|
||||
RegisterAllocator(); \
|
||||
\
|
||||
JPH_PROFILE_START("Main"); \
|
||||
\
|
||||
FPExceptionsEnable enable_exceptions; \
|
||||
JPH_UNUSED(enable_exceptions); \
|
||||
\
|
||||
{ \
|
||||
AppName app(Application::sCreateCommandLine(inArgC, inArgV)); \
|
||||
app.Run(); \
|
||||
} \
|
||||
\
|
||||
JPH_PROFILE_END(); \
|
||||
\
|
||||
return 0; \
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define ENTRY_POINT(AppName, RegisterAllocator) \
|
||||
\
|
||||
int main(int inArgC, char **inArgV) \
|
||||
{ \
|
||||
RegisterAllocator(); \
|
||||
\
|
||||
JPH_PROFILE_START("Main"); \
|
||||
\
|
||||
FPExceptionsEnable enable_exceptions; \
|
||||
JPH_UNUSED(enable_exceptions); \
|
||||
\
|
||||
{ \
|
||||
AppName app(Application::sCreateCommandLine(inArgC, inArgV)); \
|
||||
app.Run(); \
|
||||
} \
|
||||
\
|
||||
JPH_PROFILE_END(); \
|
||||
\
|
||||
return 0; \
|
||||
}
|
||||
|
||||
#endif
|
||||
240
lib/All/JoltPhysics/TestFramework/External/Perlin.cpp
vendored
Normal file
240
lib/All/JoltPhysics/TestFramework/External/Perlin.cpp
vendored
Normal file
@@ -0,0 +1,240 @@
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <External/Perlin.h>
|
||||
|
||||
// not same permutation table as Perlin's reference to avoid copyright issues;
|
||||
// Perlin's table can be found at http://mrl.nyu.edu/~perlin/noise/
|
||||
// @OPTIMIZE: should this be unsigned char instead of int for cache?
|
||||
static unsigned char stb_perlin_randtab[512] =
|
||||
{
|
||||
23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123,
|
||||
152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72,
|
||||
175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240,
|
||||
8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57,
|
||||
225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233,
|
||||
94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172,
|
||||
165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243,
|
||||
65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122,
|
||||
26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76,
|
||||
250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246,
|
||||
132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3,
|
||||
91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231,
|
||||
38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221,
|
||||
131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62,
|
||||
27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135,
|
||||
61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5,
|
||||
|
||||
// and a second copy so we don't need an extra mask or static initializer
|
||||
23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123,
|
||||
152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72,
|
||||
175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240,
|
||||
8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57,
|
||||
225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233,
|
||||
94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172,
|
||||
165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243,
|
||||
65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122,
|
||||
26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76,
|
||||
250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246,
|
||||
132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3,
|
||||
91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231,
|
||||
38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221,
|
||||
131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62,
|
||||
27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135,
|
||||
61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5,
|
||||
};
|
||||
|
||||
static float stb_perlin_lerp(float a, float b, float t)
|
||||
{
|
||||
return a + (b-a) * t;
|
||||
}
|
||||
|
||||
static int stb_perlin_fastfloor(float a)
|
||||
{
|
||||
int ai = (int) a;
|
||||
return (a < ai) ? ai-1 : ai;
|
||||
}
|
||||
|
||||
// different grad function from Perlin's, but easy to modify to match reference
|
||||
static float stb_perlin_grad(int hash, float x, float y, float z)
|
||||
{
|
||||
static float basis[12][4] =
|
||||
{
|
||||
{ 1, 1, 0 },
|
||||
{ -1, 1, 0 },
|
||||
{ 1,-1, 0 },
|
||||
{ -1,-1, 0 },
|
||||
{ 1, 0, 1 },
|
||||
{ -1, 0, 1 },
|
||||
{ 1, 0,-1 },
|
||||
{ -1, 0,-1 },
|
||||
{ 0, 1, 1 },
|
||||
{ 0,-1, 1 },
|
||||
{ 0, 1,-1 },
|
||||
{ 0,-1,-1 },
|
||||
};
|
||||
|
||||
// perlin's gradient has 12 cases so some get used 1/16th of the time
|
||||
// and some 2/16ths. We reduce bias by changing those fractions
|
||||
// to 5/64ths and 6/64ths, and the same 4 cases get the extra weight.
|
||||
static unsigned char indices[64] =
|
||||
{
|
||||
0,1,2,3,4,5,6,7,8,9,10,11,
|
||||
0,9,1,11,
|
||||
0,1,2,3,4,5,6,7,8,9,10,11,
|
||||
0,1,2,3,4,5,6,7,8,9,10,11,
|
||||
0,1,2,3,4,5,6,7,8,9,10,11,
|
||||
0,1,2,3,4,5,6,7,8,9,10,11,
|
||||
};
|
||||
|
||||
// if you use reference permutation table, change 63 below to 15 to match reference
|
||||
// (this is why the ordering of the table above is funky)
|
||||
float *grad = basis[indices[hash & 63]];
|
||||
return grad[0]*x + grad[1]*y + grad[2]*z;
|
||||
}
|
||||
|
||||
float PerlinNoise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap)
|
||||
{
|
||||
float u,v,w;
|
||||
float n000,n001,n010,n011,n100,n101,n110,n111;
|
||||
float n00,n01,n10,n11;
|
||||
float n0,n1;
|
||||
|
||||
int x_mask = (x_wrap-1) & 255;
|
||||
int y_mask = (y_wrap-1) & 255;
|
||||
int z_mask = (z_wrap-1) & 255;
|
||||
int px = stb_perlin_fastfloor(x);
|
||||
int py = stb_perlin_fastfloor(y);
|
||||
int pz = stb_perlin_fastfloor(z);
|
||||
int x0 = px & x_mask, x1 = (px+1) & x_mask;
|
||||
int y0 = py & y_mask, y1 = (py+1) & y_mask;
|
||||
int z0 = pz & z_mask, z1 = (pz+1) & z_mask;
|
||||
int r0,r1, r00,r01,r10,r11;
|
||||
|
||||
#define stb_perlin_ease(a) (((a*6-15)*a + 10) * a * a * a)
|
||||
|
||||
x -= px; u = stb_perlin_ease(x);
|
||||
y -= py; v = stb_perlin_ease(y);
|
||||
z -= pz; w = stb_perlin_ease(z);
|
||||
|
||||
r0 = stb_perlin_randtab[x0];
|
||||
r1 = stb_perlin_randtab[x1];
|
||||
|
||||
r00 = stb_perlin_randtab[r0+y0];
|
||||
r01 = stb_perlin_randtab[r0+y1];
|
||||
r10 = stb_perlin_randtab[r1+y0];
|
||||
r11 = stb_perlin_randtab[r1+y1];
|
||||
|
||||
n000 = stb_perlin_grad(stb_perlin_randtab[r00+z0], x , y , z );
|
||||
n001 = stb_perlin_grad(stb_perlin_randtab[r00+z1], x , y , z-1 );
|
||||
n010 = stb_perlin_grad(stb_perlin_randtab[r01+z0], x , y-1, z );
|
||||
n011 = stb_perlin_grad(stb_perlin_randtab[r01+z1], x , y-1, z-1 );
|
||||
n100 = stb_perlin_grad(stb_perlin_randtab[r10+z0], x-1, y , z );
|
||||
n101 = stb_perlin_grad(stb_perlin_randtab[r10+z1], x-1, y , z-1 );
|
||||
n110 = stb_perlin_grad(stb_perlin_randtab[r11+z0], x-1, y-1, z );
|
||||
n111 = stb_perlin_grad(stb_perlin_randtab[r11+z1], x-1, y-1, z-1 );
|
||||
|
||||
n00 = stb_perlin_lerp(n000,n001,w);
|
||||
n01 = stb_perlin_lerp(n010,n011,w);
|
||||
n10 = stb_perlin_lerp(n100,n101,w);
|
||||
n11 = stb_perlin_lerp(n110,n111,w);
|
||||
|
||||
n0 = stb_perlin_lerp(n00,n01,v);
|
||||
n1 = stb_perlin_lerp(n10,n11,v);
|
||||
|
||||
return stb_perlin_lerp(n0,n1,u);
|
||||
}
|
||||
|
||||
float PerlinRidgeNoise3(float x, float y, float z, float lacunarity, float gain, float offset, int octaves, int x_wrap, int y_wrap, int z_wrap)
|
||||
{
|
||||
int i;
|
||||
float frequency = 1.0f;
|
||||
float prev = 1.0f;
|
||||
float amplitude = 0.5f;
|
||||
float sum = 0.0f;
|
||||
|
||||
for (i = 0; i < octaves; i++) {
|
||||
float r = (float)(PerlinNoise3(x*frequency,y*frequency,z*frequency,x_wrap,y_wrap,z_wrap));
|
||||
r = r<0 ? -r : r; // abs()
|
||||
r = offset - r;
|
||||
r = r*r;
|
||||
sum += r*amplitude*prev;
|
||||
prev = r;
|
||||
frequency *= lacunarity;
|
||||
amplitude *= gain;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
float PerlinFBMNoise3(float x, float y, float z, float lacunarity, float gain, int octaves, int x_wrap, int y_wrap, int z_wrap)
|
||||
{
|
||||
int i;
|
||||
float frequency = 1.0f;
|
||||
float amplitude = 1.0f;
|
||||
float sum = 0.0f;
|
||||
|
||||
for (i = 0; i < octaves; i++) {
|
||||
sum += PerlinNoise3(x*frequency,y*frequency,z*frequency,x_wrap,y_wrap,z_wrap)*amplitude;
|
||||
frequency *= lacunarity;
|
||||
amplitude *= gain;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
float PerlinTurbulenceNoise3(float x, float y, float z, float lacunarity, float gain, int octaves, int x_wrap, int y_wrap, int z_wrap)
|
||||
{
|
||||
int i;
|
||||
float frequency = 1.0f;
|
||||
float amplitude = 1.0f;
|
||||
float sum = 0.0f;
|
||||
|
||||
for (i = 0; i < octaves; i++) {
|
||||
float r = PerlinNoise3(x*frequency,y*frequency,z*frequency,x_wrap,y_wrap,z_wrap)*amplitude;
|
||||
r = r<0 ? -r : r; // abs()
|
||||
sum += r;
|
||||
frequency *= lacunarity;
|
||||
amplitude *= gain;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
62
lib/All/JoltPhysics/TestFramework/External/Perlin.h
vendored
Normal file
62
lib/All/JoltPhysics/TestFramework/External/Perlin.h
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
// Taken from: https://github.com/nothings/stb/blob/master/stb_perlin.h
|
||||
//
|
||||
// stb_perlin.h - v0.3 - perlin noise
|
||||
// public domain single-file C implementation by Sean Barrett
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// See cpp file.
|
||||
//
|
||||
// Contributors:
|
||||
// Jack Mott - additional noise functions
|
||||
//
|
||||
// float PerlinNoise3(float x,
|
||||
// float y,
|
||||
// float z,
|
||||
// int x_wrap = 0,
|
||||
// int y_wrap = 0,
|
||||
// int z_wrap = 0)
|
||||
//
|
||||
// This function computes a random value at the coordinate (x,y,z).
|
||||
// Adjacent random values are continuous but the noise fluctuates
|
||||
// its randomness with period 1, i.e. takes on wholly unrelated values
|
||||
// at integer points. Specifically, this implements Ken Perlin's
|
||||
// revised noise function from 2002.
|
||||
//
|
||||
// The "wrap" parameters can be used to create wraparound noise that
|
||||
// wraps at powers of two. The numbers MUST be powers of two. Specify
|
||||
// 0 to mean "don't care". (The noise always wraps every 256 due
|
||||
// details of the implementation, even if you ask for larger or no
|
||||
// wrapping.)
|
||||
//
|
||||
// Fractal Noise:
|
||||
//
|
||||
// Three common fractal noise functions are included, which produce
|
||||
// a wide variety of nice effects depending on the parameters
|
||||
// provided. Note that each function will call PerlinNoise3
|
||||
// 'octaves' times, so this parameter will affect runtime.
|
||||
//
|
||||
// float PerlinRidgeNoise3(float x, float y, float z,
|
||||
// float lacunarity, float gain, float offset, int octaves,
|
||||
// int x_wrap, int y_wrap, int z_wrap);
|
||||
//
|
||||
// float PerlinFBMNoise3(float x, float y, float z,
|
||||
// float lacunarity, float gain, int octaves,
|
||||
// int x_wrap, int y_wrap, int z_wrap);
|
||||
//
|
||||
// float PerlinTurbulenceNoise3(float x, float y, float z,
|
||||
// float lacunarity, float gain,int octaves,
|
||||
// int x_wrap, int y_wrap, int z_wrap);
|
||||
//
|
||||
// Typical values to start playing with:
|
||||
// octaves = 6 -- number of "octaves" of noise3() to sum
|
||||
// lacunarity = ~ 2.0 -- spacing between successive octaves (use exactly 2.0 for wrapping output)
|
||||
// gain = 0.5 -- relative weighting applied to each successive octave
|
||||
// offset = 1.0? -- used to invert the ridges, may need to be larger, not sure
|
||||
//
|
||||
float PerlinNoise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap);
|
||||
float PerlinRidgeNoise3(float x, float y, float z, float lacunarity, float gain, float offset, int octaves, int x_wrap, int y_wrap, int z_wrap);
|
||||
float PerlinFBMNoise3(float x, float y, float z, float lacunarity, float gain, int octaves,int x_wrap, int y_wrap, int z_wrap);
|
||||
float PerlinTurbulenceNoise3(float x, float y, float z, float lacunarity, float gain, int octaves, int x_wrap, int y_wrap, int z_wrap);
|
||||
5082
lib/All/JoltPhysics/TestFramework/External/stb_truetype.h
vendored
Normal file
5082
lib/All/JoltPhysics/TestFramework/External/stb_truetype.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
356
lib/All/JoltPhysics/TestFramework/Image/BlitSurface.cpp
Normal file
356
lib/All/JoltPhysics/TestFramework/Image/BlitSurface.cpp
Normal file
@@ -0,0 +1,356 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Image/BlitSurface.h>
|
||||
#include <Image/Surface.h>
|
||||
#include <Jolt/Core/Color.h>
|
||||
#include <Jolt/Core/Profiler.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// BlitSettings
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const BlitSettings BlitSettings::sDefault;
|
||||
|
||||
BlitSettings::BlitSettings() :
|
||||
mConvertRGBToAlpha(false),
|
||||
mConvertAlphaToRGB(false),
|
||||
mConvertToGrayScale(false),
|
||||
mInvertAlpha(false),
|
||||
mColorKeyAlpha(false),
|
||||
mColorKeyStart(240, 0, 240),
|
||||
mColorKeyEnd(255, 15, 255)
|
||||
{
|
||||
}
|
||||
|
||||
bool BlitSettings::operator == (const BlitSettings &inRHS) const
|
||||
{
|
||||
return mConvertRGBToAlpha == inRHS.mConvertRGBToAlpha
|
||||
&& mConvertAlphaToRGB == inRHS.mConvertAlphaToRGB
|
||||
&& mConvertToGrayScale == inRHS.mConvertToGrayScale
|
||||
&& mInvertAlpha == inRHS.mInvertAlpha
|
||||
&& mColorKeyAlpha == inRHS.mColorKeyAlpha
|
||||
&& mColorKeyStart == inRHS.mColorKeyStart
|
||||
&& mColorKeyEnd == inRHS.mColorKeyEnd
|
||||
&& mZoomSettings == inRHS.mZoomSettings;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Converting from one format to another
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// The macro COL(s) converts color s to another color given the mapping tables
|
||||
#define CMP(s, c) map[256 * c + ((s & src_mask[c]) >> src_shift[c])]
|
||||
#define COL(s) (CMP(s, 0) + CMP(s, 1) + CMP(s, 2) + CMP(s, 3))
|
||||
|
||||
static void sComputeTranslationTable(const FormatDescription & inSrcDesc, const FormatDescription & inDstDesc, uint32 *outMask, uint32 *outShift, uint32 *outMap)
|
||||
{
|
||||
JPH_PROFILE("sComputeTranslationTable");
|
||||
|
||||
// Compute translation tables for each color component
|
||||
uint32 written_mask = 0;
|
||||
for (int c = 0; c < 4; ++c)
|
||||
{
|
||||
outMask[c] = inSrcDesc.GetComponentMask(c);
|
||||
outShift[c] = CountTrailingZeros(outMask[c]);
|
||||
uint32 src_shifted_mask = outMask[c] >> outShift[c];
|
||||
|
||||
uint32 dst_mask = inDstDesc.GetComponentMask(c);
|
||||
uint32 dst_shift = CountTrailingZeros(dst_mask);
|
||||
uint32 dst_shifted_mask = dst_mask >> dst_shift;
|
||||
|
||||
if ((written_mask & dst_mask) != 0)
|
||||
{
|
||||
dst_mask = 0;
|
||||
dst_shift = 0;
|
||||
dst_shifted_mask = 0;
|
||||
}
|
||||
else
|
||||
written_mask |= dst_mask;
|
||||
|
||||
float scale = src_shifted_mask > 0? float(dst_shifted_mask) / src_shifted_mask : 0.0f;
|
||||
|
||||
uint32 entry = 0;
|
||||
|
||||
if (src_shifted_mask != 0)
|
||||
for (; entry <= src_shifted_mask; ++entry)
|
||||
outMap[256 * c + entry] = uint32(round(scale * entry)) << dst_shift;
|
||||
|
||||
for (; entry < 256; ++entry)
|
||||
outMap[256 * c + entry] = dst_mask;
|
||||
}
|
||||
}
|
||||
|
||||
static bool sConvertImageDifferentTypes(RefConst<Surface> inSrc, Ref<Surface> ioDst)
|
||||
{
|
||||
JPH_PROFILE("sConvertImageDifferentTypes");
|
||||
|
||||
// Get image properties
|
||||
int sbpp = inSrc->GetBytesPerPixel();
|
||||
int dbpp = ioDst->GetBytesPerPixel();
|
||||
int width = inSrc->GetWidth();
|
||||
int height = inSrc->GetHeight();
|
||||
JPH_ASSERT(width == ioDst->GetWidth());
|
||||
JPH_ASSERT(height == ioDst->GetHeight());
|
||||
|
||||
// Compute conversion map
|
||||
uint32 src_mask[4];
|
||||
uint32 src_shift[4];
|
||||
uint32 map[4 * 256];
|
||||
sComputeTranslationTable(inSrc->GetFormatDescription(), ioDst->GetFormatDescription(), src_mask, src_shift, map);
|
||||
|
||||
inSrc->Lock(ESurfaceLockMode::Read);
|
||||
ioDst->Lock(ESurfaceLockMode::Write);
|
||||
|
||||
// Convert the image
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
const uint8 *s = inSrc->GetScanLine(y);
|
||||
const uint8 *s_end = inSrc->GetScanLine(y) + width * sbpp;
|
||||
uint8 *d = ioDst->GetScanLine(y);
|
||||
|
||||
while (s < s_end)
|
||||
{
|
||||
uint32 src = 0;
|
||||
memcpy(&src, s, sbpp);
|
||||
uint32 dst = COL(src);
|
||||
memcpy(d, &dst, dbpp);
|
||||
s += sbpp;
|
||||
d += dbpp;
|
||||
}
|
||||
}
|
||||
|
||||
inSrc->UnLock();
|
||||
ioDst->UnLock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sConvertImageSameTypes(RefConst<Surface> inSrc, Ref<Surface> ioDst)
|
||||
{
|
||||
JPH_PROFILE("sConvertImageSameTypes");
|
||||
|
||||
// Get image properties
|
||||
int dbpp = ioDst->GetBytesPerPixel();
|
||||
int width = inSrc->GetWidth();
|
||||
int height = inSrc->GetHeight();
|
||||
JPH_ASSERT(inSrc->GetFormat() == ioDst->GetFormat());
|
||||
JPH_ASSERT(dbpp == inSrc->GetBytesPerPixel());
|
||||
JPH_ASSERT(width == ioDst->GetWidth());
|
||||
JPH_ASSERT(height == ioDst->GetHeight());
|
||||
|
||||
inSrc->Lock(ESurfaceLockMode::Read);
|
||||
ioDst->Lock(ESurfaceLockMode::Write);
|
||||
|
||||
// Copy the image line by line to compensate for stride
|
||||
for (int y = 0; y < height; ++y)
|
||||
memcpy(ioDst->GetScanLine(y), inSrc->GetScanLine(y), width * dbpp);
|
||||
|
||||
inSrc->UnLock();
|
||||
ioDst->UnLock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sConvertImage(RefConst<Surface> inSrc, Ref<Surface> ioDst)
|
||||
{
|
||||
JPH_PROFILE("sConvertImage");
|
||||
|
||||
if (inSrc->GetFormat() == ioDst->GetFormat())
|
||||
return sConvertImageSameTypes(inSrc, ioDst);
|
||||
else
|
||||
return sConvertImageDifferentTypes(inSrc, ioDst);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Special color conversions
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void sConvertRGBToAlpha(Ref<Surface> ioSurface)
|
||||
{
|
||||
JPH_PROFILE("sConvertRGBToAlpha");
|
||||
|
||||
// Check surface format
|
||||
JPH_ASSERT(ioSurface->GetFormat() == ESurfaceFormat::A8R8G8B8);
|
||||
|
||||
// Get dimensions of image
|
||||
int width = ioSurface->GetWidth();
|
||||
int height = ioSurface->GetHeight();
|
||||
|
||||
// Convert RGB values to alpha values
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
Color *c = (Color *)ioSurface->GetScanLine(y);
|
||||
Color *c_end = (Color *)(ioSurface->GetScanLine(y) + width * sizeof(Color));
|
||||
|
||||
while (c < c_end)
|
||||
{
|
||||
c->a = c->GetIntensity();
|
||||
++c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sConvertAlphaToRGB(Ref<Surface> ioSurface)
|
||||
{
|
||||
JPH_PROFILE("sConvertAlphaToRGB");
|
||||
|
||||
// Check surface format
|
||||
JPH_ASSERT(ioSurface->GetFormat() == ESurfaceFormat::A8R8G8B8);
|
||||
|
||||
// Get dimensions of image
|
||||
int width = ioSurface->GetWidth();
|
||||
int height = ioSurface->GetHeight();
|
||||
|
||||
// Convert alpha values to RGB values
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
Color *c = (Color *)ioSurface->GetScanLine(y);
|
||||
Color *c_end = (Color *)(ioSurface->GetScanLine(y) + width * sizeof(Color));
|
||||
|
||||
while (c < c_end)
|
||||
{
|
||||
c->r = c->g = c->b = c->a;
|
||||
++c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sConvertToGrayScale(Ref<Surface> ioSurface)
|
||||
{
|
||||
JPH_PROFILE("sConvertToGrayScale");
|
||||
|
||||
// Check surface format
|
||||
JPH_ASSERT(ioSurface->GetFormat() == ESurfaceFormat::A8R8G8B8);
|
||||
|
||||
// Get dimensions of image
|
||||
int width = ioSurface->GetWidth();
|
||||
int height = ioSurface->GetHeight();
|
||||
|
||||
// Convert RGB values to grayscale values
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
Color *c = (Color *)ioSurface->GetScanLine(y);
|
||||
Color *c_end = (Color *)(ioSurface->GetScanLine(y) + width * sizeof(Color));
|
||||
|
||||
while (c < c_end)
|
||||
{
|
||||
uint8 intensity = c->GetIntensity();
|
||||
c->r = intensity;
|
||||
c->g = intensity;
|
||||
c->b = intensity;
|
||||
++c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sInvertAlpha(Ref<Surface> ioSurface)
|
||||
{
|
||||
JPH_PROFILE("sInvertAlpha");
|
||||
|
||||
// Check surface format
|
||||
JPH_ASSERT(ioSurface->GetFormat() == ESurfaceFormat::A8R8G8B8);
|
||||
|
||||
// Get dimensions of image
|
||||
int width = ioSurface->GetWidth();
|
||||
int height = ioSurface->GetHeight();
|
||||
|
||||
// Invert all alpha values
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
Color *c = (Color *)ioSurface->GetScanLine(y);
|
||||
Color *c_end = (Color *)(ioSurface->GetScanLine(y) + width * sizeof(Color));
|
||||
|
||||
while (c < c_end)
|
||||
{
|
||||
c->a = uint8(255 - c->a);
|
||||
++c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sColorKeyAlpha(Ref<Surface> ioSurface, ColorArg inStart, ColorArg inEnd)
|
||||
{
|
||||
JPH_PROFILE("sColorKeyAlpha");
|
||||
|
||||
// Check surface format
|
||||
JPH_ASSERT(ioSurface->GetFormat() == ESurfaceFormat::A8R8G8B8);
|
||||
|
||||
// Get dimensions of image
|
||||
int width = ioSurface->GetWidth();
|
||||
int height = ioSurface->GetHeight();
|
||||
|
||||
// Set alpha values
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
Color *c = (Color *)ioSurface->GetScanLine(y);
|
||||
Color *c_end = (Color *)(ioSurface->GetScanLine(y) + width * sizeof(Color));
|
||||
|
||||
while (c < c_end)
|
||||
{
|
||||
if (c->r >= inStart.r && c->r <= inEnd.r && c->g >= inStart.g && c->g <= inEnd.g && c->b >= inStart.b && c->b <= inEnd.b)
|
||||
c->a = 0;
|
||||
else
|
||||
c->a = 255;
|
||||
++c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// BlitSurface
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool BlitSurface(RefConst<Surface> inSrc, Ref<Surface> ioDst, const BlitSettings &inBlitSettings)
|
||||
{
|
||||
JPH_PROFILE("BlitSurface");
|
||||
|
||||
// Do extra conversion options
|
||||
RefConst<Surface> src = inSrc;
|
||||
if (inBlitSettings.mConvertRGBToAlpha || inBlitSettings.mConvertAlphaToRGB || inBlitSettings.mConvertToGrayScale || inBlitSettings.mInvertAlpha || inBlitSettings.mColorKeyAlpha)
|
||||
{
|
||||
// Do them on A8R8G8B8 format so the conversion routines are simple
|
||||
Ref<Surface> tmp = new SoftwareSurface(inSrc->GetWidth(), inSrc->GetHeight(), ESurfaceFormat::A8R8G8B8);
|
||||
sConvertImage(inSrc, tmp);
|
||||
src = tmp;
|
||||
|
||||
// Perform all optional conversions
|
||||
tmp->Lock(ESurfaceLockMode::ReadWrite);
|
||||
|
||||
if (inBlitSettings.mConvertRGBToAlpha)
|
||||
sConvertRGBToAlpha(tmp);
|
||||
|
||||
if (inBlitSettings.mConvertAlphaToRGB)
|
||||
sConvertAlphaToRGB(tmp);
|
||||
|
||||
if (inBlitSettings.mConvertToGrayScale)
|
||||
sConvertToGrayScale(tmp);
|
||||
|
||||
if (inBlitSettings.mInvertAlpha)
|
||||
sInvertAlpha(tmp);
|
||||
|
||||
if (inBlitSettings.mColorKeyAlpha)
|
||||
sColorKeyAlpha(tmp, inBlitSettings.mColorKeyStart, inBlitSettings.mColorKeyEnd);
|
||||
|
||||
tmp->UnLock();
|
||||
}
|
||||
|
||||
if (src->GetWidth() != ioDst->GetWidth() || src->GetHeight() != ioDst->GetHeight())
|
||||
{
|
||||
// Zoom the image if the destination size is not equal to the source size
|
||||
if (!ZoomImage(src, ioDst, inBlitSettings.mZoomSettings))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert the image if the sizes are equal
|
||||
if (!sConvertImage(src, ioDst))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
37
lib/All/JoltPhysics/TestFramework/Image/BlitSurface.h
Normal file
37
lib/All/JoltPhysics/TestFramework/Image/BlitSurface.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Image/ZoomImage.h>
|
||||
#include <Jolt/Core/Color.h>
|
||||
|
||||
/// Settings for blitting one surface to another with possibly different formats and dimensions. The blit
|
||||
/// routine can use filtering or blurring on the fly. Also it can perform some other
|
||||
/// basic operations like converting an image to grayscale or alpha only surfaces.
|
||||
class BlitSettings
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
BlitSettings();
|
||||
|
||||
/// Comparison operators
|
||||
bool operator == (const BlitSettings &inRHS) const;
|
||||
|
||||
/// Default settings
|
||||
static const BlitSettings sDefault;
|
||||
|
||||
/// Special operations that can be applied during the blit
|
||||
bool mConvertRGBToAlpha; ///< Convert RGB values to alpha values (RGB values remain untouched)
|
||||
bool mConvertAlphaToRGB; ///< Convert alpha values to grayscale RGB values (Alpha values remain untouched)
|
||||
bool mConvertToGrayScale; ///< Convert RGB values to grayscale values (Alpha values remain untouched)
|
||||
bool mInvertAlpha; ///< Invert alpha values
|
||||
bool mColorKeyAlpha; ///< If true, colors in the range mColorKeyStart..mColorKeyEnd will get an alpha of 0, other colors will get an alpha of 255
|
||||
Color mColorKeyStart;
|
||||
Color mColorKeyEnd;
|
||||
ZoomSettings mZoomSettings; ///< Settings for resizing the image
|
||||
};
|
||||
|
||||
/// Copies an image from inSrc to inDst, converting it on the fly as defined by inBlitSettings
|
||||
bool BlitSurface(RefConst<Surface> inSrc, Ref<Surface> ioDst, const BlitSettings &inBlitSettings = BlitSettings::sDefault);
|
||||
199
lib/All/JoltPhysics/TestFramework/Image/LoadBMP.cpp
Normal file
199
lib/All/JoltPhysics/TestFramework/Image/LoadBMP.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Image/LoadBMP.h>
|
||||
#include <Image/BlitSurface.h>
|
||||
#include <Image/Surface.h>
|
||||
|
||||
#pragma pack (1)
|
||||
|
||||
struct BitmapFileHeader
|
||||
{
|
||||
char mTypeB;
|
||||
char mTypeM;
|
||||
uint32 mSize;
|
||||
uint16 mReserved1;
|
||||
uint16 mReserved2;
|
||||
uint32 mOffBits;
|
||||
};
|
||||
|
||||
struct BitmapInfoHeader
|
||||
{
|
||||
uint32 mSize;
|
||||
uint32 mWidth;
|
||||
uint32 mHeight;
|
||||
uint16 mPlanes;
|
||||
uint16 mBitCount;
|
||||
uint32 mCompression;
|
||||
uint32 mSizeImage;
|
||||
uint32 mXPelsPerMeter;
|
||||
uint32 mYPelsPerMeter;
|
||||
uint32 mClrUsed;
|
||||
uint32 mClrImportant;
|
||||
};
|
||||
|
||||
#pragma pack ()
|
||||
|
||||
Ref<Surface> LoadBMP(istream &inStream)
|
||||
{
|
||||
bool loaded = true;
|
||||
|
||||
// Read bitmap info
|
||||
BitmapFileHeader bfh;
|
||||
BitmapInfoHeader bih;
|
||||
inStream.read((char *)&bfh, sizeof(bfh));
|
||||
if (inStream.fail())
|
||||
return nullptr;
|
||||
inStream.read((char *)&bih, sizeof(bih));
|
||||
if (inStream.fail())
|
||||
return nullptr;
|
||||
|
||||
// Get properties
|
||||
int bpp = (bih.mBitCount + 7) >> 3;
|
||||
int scan_width = (bih.mWidth * bpp + 3) & (~3);
|
||||
|
||||
// Check if it is a bitmap
|
||||
if (bfh.mTypeB != 'B' || bfh.mTypeM != 'M')
|
||||
{
|
||||
Trace("Not a BMP");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if bitmap is bottom-up
|
||||
if (bih.mHeight <= 0)
|
||||
{
|
||||
Trace("Not bottom-up");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if it is not compressed
|
||||
if (bih.mCompression != 0)
|
||||
{
|
||||
Trace("Is compressed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ref<Surface> surface;
|
||||
|
||||
if (bih.mBitCount == 8)
|
||||
{
|
||||
// Load palette
|
||||
uint32 *palette = new uint32 [256];
|
||||
int pal_bytes = 4 * (bih.mClrUsed != 0? bih.mClrUsed : 256);
|
||||
inStream.read((char *)palette, pal_bytes);
|
||||
loaded = loaded && !inStream.fail();
|
||||
|
||||
// Seek to image data
|
||||
inStream.seekg(bfh.mOffBits);
|
||||
|
||||
// Convert pixel data to a surface
|
||||
surface = new SoftwareSurface(bih.mWidth, bih.mHeight, ESurfaceFormat::X8R8G8B8);
|
||||
surface->Lock(ESurfaceLockMode::Write);
|
||||
uint8 *scan_line = new uint8 [scan_width];
|
||||
for (int y = bih.mHeight - 1; y >= 0; --y)
|
||||
{
|
||||
// Load one scan line
|
||||
inStream.read((char *)scan_line, scan_width);
|
||||
loaded = loaded && !inStream.fail();
|
||||
|
||||
// Copy one scan line
|
||||
uint8 *in_pixel = scan_line;
|
||||
uint32 *out_pixel = (uint32 *)surface->GetScanLine(y);
|
||||
for (uint x = 0; x < bih.mWidth; ++x, ++in_pixel, ++out_pixel)
|
||||
*out_pixel = palette[*in_pixel];
|
||||
}
|
||||
surface->UnLock();
|
||||
|
||||
// Release temporaries
|
||||
delete [] palette;
|
||||
delete [] scan_line;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Determine pixel format
|
||||
ESurfaceFormat format;
|
||||
switch (bih.mBitCount)
|
||||
{
|
||||
case 16: format = ESurfaceFormat::X1R5G5B5; break;
|
||||
case 24: format = ESurfaceFormat::R8G8B8; break;
|
||||
default: Trace("Has invalid format"); return nullptr;
|
||||
}
|
||||
|
||||
// Seek to image data
|
||||
inStream.seekg(bfh.mOffBits);
|
||||
|
||||
// Convert pixel data to a surface
|
||||
surface = new SoftwareSurface(bih.mWidth, bih.mHeight, format, scan_width);
|
||||
surface->Lock(ESurfaceLockMode::Write);
|
||||
for (int y = bih.mHeight - 1; y >= 0; --y)
|
||||
{
|
||||
inStream.read((char *)surface->GetScanLine(y), scan_width);
|
||||
loaded = loaded && !inStream.fail();
|
||||
}
|
||||
surface->UnLock();
|
||||
}
|
||||
|
||||
return loaded? surface : Ref<Surface>(nullptr);
|
||||
}
|
||||
|
||||
bool SaveBMP(RefConst<Surface> inSurface, ostream &inStream)
|
||||
{
|
||||
bool stored = true;
|
||||
|
||||
// Convert surface if required
|
||||
const Surface *src = inSurface;
|
||||
Ref<Surface> tmp_src;
|
||||
if (inSurface->GetFormat() != ESurfaceFormat::R8G8B8)
|
||||
{
|
||||
tmp_src = new SoftwareSurface(inSurface->GetWidth(), inSurface->GetHeight(), ESurfaceFormat::R8G8B8);
|
||||
BlitSurface(inSurface, tmp_src);
|
||||
src = tmp_src.GetPtr();
|
||||
}
|
||||
|
||||
// Lock the surface
|
||||
src->Lock(ESurfaceLockMode::Read);
|
||||
JPH_ASSERT(src->GetStride() % 4 == 0);
|
||||
|
||||
BitmapFileHeader bfh;
|
||||
BitmapInfoHeader bih;
|
||||
|
||||
// Fill in headers
|
||||
bfh.mTypeB = 'B';
|
||||
bfh.mTypeM = 'M';
|
||||
bfh.mSize = sizeof(bfh) + sizeof(bih) + src->GetHeight() * src->GetStride();
|
||||
bfh.mReserved1 = 0;
|
||||
bfh.mReserved2 = 0;
|
||||
bfh.mOffBits = sizeof(bfh) + sizeof(bih);
|
||||
|
||||
bih.mSize = sizeof(bih);
|
||||
bih.mWidth = src->GetWidth();
|
||||
bih.mHeight = src->GetHeight();
|
||||
bih.mPlanes = 1;
|
||||
bih.mBitCount = 24;
|
||||
bih.mCompression = 0;
|
||||
bih.mSizeImage = src->GetHeight() * src->GetStride();
|
||||
bih.mXPelsPerMeter = 300;
|
||||
bih.mYPelsPerMeter = 300;
|
||||
bih.mClrUsed = 0;
|
||||
bih.mClrImportant = 0;
|
||||
|
||||
// Write headers
|
||||
inStream.write((char *)&bfh, sizeof(bfh));
|
||||
stored = stored && !inStream.fail();
|
||||
inStream.write((char *)&bih, sizeof(bih));
|
||||
stored = stored && !inStream.fail();
|
||||
|
||||
// Write image data
|
||||
for (int y = src->GetHeight() - 1; y >= 0; --y)
|
||||
{
|
||||
inStream.write((const char *)src->GetScanLine(y), src->GetStride());
|
||||
stored = stored && !inStream.fail();
|
||||
}
|
||||
|
||||
src->UnLock();
|
||||
|
||||
return stored;
|
||||
}
|
||||
15
lib/All/JoltPhysics/TestFramework/Image/LoadBMP.h
Normal file
15
lib/All/JoltPhysics/TestFramework/Image/LoadBMP.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
|
||||
class Surface;
|
||||
|
||||
/// Load a windows BMP file
|
||||
Ref<Surface> LoadBMP(istream &inStream);
|
||||
|
||||
/// Write a windows BMP file
|
||||
bool SaveBMP(RefConst<Surface> inSurface, ostream &inStream);
|
||||
130
lib/All/JoltPhysics/TestFramework/Image/LoadTGA.cpp
Normal file
130
lib/All/JoltPhysics/TestFramework/Image/LoadTGA.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Image/LoadTGA.h>
|
||||
#include <Image/Surface.h>
|
||||
|
||||
#pragma pack (1)
|
||||
|
||||
struct TGAHeader
|
||||
{
|
||||
uint8 mIDLength;
|
||||
uint8 mColorMapType;
|
||||
uint8 mImageType;
|
||||
uint16 mColorMapFirstEntryIndex;
|
||||
uint16 mColorMapLength;
|
||||
uint8 mColorMapEntrySize;
|
||||
uint16 mXOrigin;
|
||||
uint16 mYOrigin;
|
||||
uint16 mWidth;
|
||||
uint16 mHeight;
|
||||
uint8 mPixelDepth;
|
||||
uint8 mImageDescriptor;
|
||||
};
|
||||
|
||||
#pragma pack ()
|
||||
|
||||
Ref<Surface> LoadTGA(istream &inStream)
|
||||
{
|
||||
bool loaded = true;
|
||||
|
||||
// Read header
|
||||
TGAHeader header;
|
||||
inStream.read((char *)&header, sizeof(header));
|
||||
if (inStream.fail())
|
||||
return nullptr;
|
||||
|
||||
// Get properties
|
||||
int bytes_per_pixel = (header.mPixelDepth + 7) >> 3;
|
||||
int scan_width = bytes_per_pixel * header.mWidth;
|
||||
|
||||
// Check type
|
||||
if (header.mImageType < 1 || header.mImageType > 2)
|
||||
{
|
||||
Trace("Not a readable TGA");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check compression
|
||||
if ((header.mImageType == 1 && header.mColorMapType != 1) || (header.mImageType == 2 && header.mColorMapType != 0))
|
||||
{
|
||||
Trace("Not an uncompressed TGA");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ref<Surface> surface;
|
||||
|
||||
if (header.mPixelDepth == 8)
|
||||
{
|
||||
// Determine pixel format
|
||||
ESurfaceFormat format;
|
||||
int pixel_size;
|
||||
switch (header.mColorMapEntrySize)
|
||||
{
|
||||
case 15: format = ESurfaceFormat::X1R5G5B5; pixel_size = 2; break;
|
||||
case 16: format = ESurfaceFormat::X1R5G5B5; pixel_size = 2; break;
|
||||
case 24: format = ESurfaceFormat::R8G8B8; pixel_size = 3; break;
|
||||
case 32: format = ESurfaceFormat::A8R8G8B8; pixel_size = 4; break;
|
||||
default: Trace("Has invalid format"); return nullptr;
|
||||
}
|
||||
|
||||
// Seek to beginning of palette
|
||||
inStream.seekg(sizeof(TGAHeader) + header.mIDLength);
|
||||
|
||||
// Load palette
|
||||
int pal_bytes = pixel_size * header.mColorMapLength;
|
||||
uint8 *palette = new uint8 [pal_bytes];
|
||||
inStream.read((char *)palette, pal_bytes);
|
||||
loaded = loaded && !inStream.fail();
|
||||
|
||||
// Convert pixel data to a surface
|
||||
surface = new SoftwareSurface(header.mWidth, header.mHeight, format);
|
||||
surface->Lock(ESurfaceLockMode::Write);
|
||||
uint8 *scan_line = new uint8 [scan_width];
|
||||
for (int y = header.mHeight - 1; y >= 0; --y)
|
||||
{
|
||||
// Load one scan line
|
||||
inStream.read((char *)scan_line, scan_width);
|
||||
loaded = loaded && !inStream.fail();
|
||||
|
||||
// Copy one scan line
|
||||
uint8 *in_pixel = scan_line;
|
||||
uint8 *out_pixel = (uint8 *)surface->GetScanLine(y);
|
||||
for (int x = 0; x < header.mWidth; ++x, ++in_pixel, out_pixel += pixel_size)
|
||||
memcpy(out_pixel, palette + (*in_pixel - header.mColorMapFirstEntryIndex) * pixel_size, pixel_size);
|
||||
}
|
||||
surface->UnLock();
|
||||
|
||||
// Release temporaries
|
||||
delete [] palette;
|
||||
delete [] scan_line;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Determine pixel format
|
||||
ESurfaceFormat format;
|
||||
switch (header.mPixelDepth)
|
||||
{
|
||||
case 15: format = ESurfaceFormat::X1R5G5B5; break;
|
||||
case 16: format = ESurfaceFormat::X1R5G5B5; break;
|
||||
case 24: format = ESurfaceFormat::R8G8B8; break;
|
||||
case 32: format = ESurfaceFormat::A8R8G8B8; break;
|
||||
default: Trace("Invalid format"); return nullptr;
|
||||
}
|
||||
|
||||
// Convert pixel data to a surface
|
||||
surface = new SoftwareSurface(header.mWidth, header.mHeight, format, scan_width);
|
||||
surface->Lock(ESurfaceLockMode::Write);
|
||||
for (int y = header.mHeight - 1; y >= 0; --y)
|
||||
{
|
||||
inStream.read((char *)surface->GetScanLine(y), scan_width);
|
||||
loaded = loaded && !inStream.fail();
|
||||
}
|
||||
surface->UnLock();
|
||||
}
|
||||
|
||||
return loaded? surface : Ref<Surface>(nullptr);
|
||||
}
|
||||
12
lib/All/JoltPhysics/TestFramework/Image/LoadTGA.h
Normal file
12
lib/All/JoltPhysics/TestFramework/Image/LoadTGA.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
|
||||
class Surface;
|
||||
|
||||
/// Image routines, loads a Targa (TGA) file.
|
||||
Ref<Surface> LoadTGA(istream &inStream);
|
||||
220
lib/All/JoltPhysics/TestFramework/Image/Surface.cpp
Normal file
220
lib/All/JoltPhysics/TestFramework/Image/Surface.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Image/Surface.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FormatDescription
|
||||
//
|
||||
// Description of a surface format
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Format descriptions
|
||||
static FormatDescription sFormats[] =
|
||||
{
|
||||
// Description BPP #CMP Closest 8 Bit Closest Alpha Red Mask Green Mask Blue Mask Alpha Mask
|
||||
FormatDescription("A4L4", 8, 2, ESurfaceFormat::A8L8, ESurfaceFormat::A4L4, 0x0000000f, 0x0000000f, 0x0000000f, 0x000000f0),
|
||||
FormatDescription("L8", 8, 1, ESurfaceFormat::L8, ESurfaceFormat::A8L8, 0x000000ff, 0x000000ff, 0x000000ff, 0x00000000),
|
||||
FormatDescription("A8", 8, 1, ESurfaceFormat::A8, ESurfaceFormat::A8, 0x00000000, 0x00000000, 0x00000000, 0x000000ff),
|
||||
FormatDescription("A8L8", 16, 2, ESurfaceFormat::A8L8, ESurfaceFormat::A8L8, 0x000000ff, 0x000000ff, 0x000000ff, 0x0000ff00),
|
||||
FormatDescription("R5G6B5", 16, 3, ESurfaceFormat::R8G8B8, ESurfaceFormat::A1R5G5B5, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000),
|
||||
FormatDescription("X1R5G5B5", 16, 3, ESurfaceFormat::R8G8B8, ESurfaceFormat::A1R5G5B5, 0x00007c00, 0x000003e0, 0x0000001f, 0x00000000),
|
||||
FormatDescription("X4R4G4B4", 16, 3, ESurfaceFormat::R8G8B8, ESurfaceFormat::A4R4G4B4, 0x00000f00, 0x000000f0, 0x0000000f, 0x00000000),
|
||||
FormatDescription("A1R5G5B5", 16, 4, ESurfaceFormat::A8R8G8B8, ESurfaceFormat::A1R5G5B5, 0x00007c00, 0x000003e0, 0x0000001f, 0x00008000),
|
||||
FormatDescription("A4R4G4B4", 16, 4, ESurfaceFormat::A8R8G8B8, ESurfaceFormat::A4R4G4B4, 0x00000f00, 0x000000f0, 0x0000000f, 0x0000f000),
|
||||
FormatDescription("R8G8B8", 24, 3, ESurfaceFormat::R8G8B8, ESurfaceFormat::A8R8G8B8, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000),
|
||||
FormatDescription("B8G8R8", 24, 3, ESurfaceFormat::B8G8R8, ESurfaceFormat::A8B8G8R8, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000),
|
||||
FormatDescription("X8R8G8B8", 32, 3, ESurfaceFormat::X8R8G8B8, ESurfaceFormat::A8R8G8B8, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000),
|
||||
FormatDescription("X8B8G8R8", 32, 3, ESurfaceFormat::X8B8G8R8, ESurfaceFormat::A8B8G8R8, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000),
|
||||
FormatDescription("A8R8G8B8", 32, 4, ESurfaceFormat::A8R8G8B8, ESurfaceFormat::A8R8G8B8, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000),
|
||||
FormatDescription("A8B8G8R8", 32, 4, ESurfaceFormat::A8B8G8R8, ESurfaceFormat::A8B8G8R8, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000),
|
||||
FormatDescription("Invalid", 0, 0, ESurfaceFormat::Invalid, ESurfaceFormat::Invalid, 0x00000000, 0x00000000, 0x00000000, 0x00000000),
|
||||
};
|
||||
|
||||
FormatDescription::FormatDescription(const char *inFormatName, int inBitsPerPixel, int inNumberOfComponents, ESurfaceFormat inClosest8BitFormat, ESurfaceFormat inClosestAlphaFormat, uint32 inRedMask, uint32 inGreenMask, uint32 inBlueMask, uint32 inAlphaMask) :
|
||||
mFormatName(inFormatName),
|
||||
mBitsPerPixel(inBitsPerPixel),
|
||||
mNumberOfComponents(inNumberOfComponents),
|
||||
mClosest8BitFormat(inClosest8BitFormat),
|
||||
mClosestAlphaFormat(inClosestAlphaFormat),
|
||||
mRedMask(inRedMask),
|
||||
mGreenMask(inGreenMask),
|
||||
mBlueMask(inBlueMask),
|
||||
mAlphaMask(inAlphaMask)
|
||||
{
|
||||
}
|
||||
|
||||
uint32 FormatDescription::Encode(ColorArg inColor) const
|
||||
{
|
||||
uint32 col = 0;
|
||||
uint32 written_mask = 0;
|
||||
|
||||
// Loop through all components
|
||||
for (int c = 0; c < 4; ++c)
|
||||
{
|
||||
// Check that we have not yet written this part of the color yet
|
||||
uint32 mask = GetComponentMask(c);
|
||||
if ((written_mask & mask) != 0) continue;
|
||||
written_mask |= mask;
|
||||
|
||||
// Or in this component
|
||||
col |= int(round((1.0f / 255.0f) * mask * inColor(c))) & mask;
|
||||
}
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
const Color FormatDescription::Decode(uint32 inColor) const
|
||||
{
|
||||
Color col(0, 0, 0, 0);
|
||||
|
||||
// Loop through all components
|
||||
for (int c = 0; c < 4; ++c)
|
||||
{
|
||||
uint32 mask = GetComponentMask(c);
|
||||
if (mask != 0)
|
||||
{
|
||||
uint32 shift = CountTrailingZeros(mask);
|
||||
uint32 shifted_color = (inColor & mask) >> shift;
|
||||
uint32 shifted_mask = mask >> shift;
|
||||
col(c) = uint8((255 * shifted_color + 127) / shifted_mask);
|
||||
}
|
||||
else
|
||||
col(c) = 255;
|
||||
}
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
const FormatDescription &GetFormatDescription(ESurfaceFormat inFormat)
|
||||
{
|
||||
if (inFormat <= ESurfaceFormat::Invalid)
|
||||
return sFormats[uint(inFormat)];
|
||||
|
||||
return sFormats[uint(ESurfaceFormat::Invalid)];
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Surface
|
||||
//
|
||||
// Class that contains an image in arbitrary format
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Surface::Surface(int inWidth, int inHeight, ESurfaceFormat inFormat) :
|
||||
mFormat(inFormat),
|
||||
mWidth(inWidth),
|
||||
mHeight(inHeight),
|
||||
mLength(0),
|
||||
mLockMode(ESurfaceLockMode::None),
|
||||
mStride(0),
|
||||
mData(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
Surface::~Surface()
|
||||
{
|
||||
JPH_ASSERT(!IsLocked());
|
||||
JPH_ASSERT(mData == nullptr);
|
||||
JPH_ASSERT(mStride == 0);
|
||||
JPH_ASSERT(mLength == 0);
|
||||
}
|
||||
|
||||
void Surface::Lock(ESurfaceLockMode inMode) const
|
||||
{
|
||||
// Check if this resource can be locked
|
||||
JPH_ASSERT(!IsLocked());
|
||||
JPH_ASSERT((uint(inMode) & uint(ESurfaceLockMode::ReadWrite)) != 0);
|
||||
|
||||
// Store mode
|
||||
mLockMode = inMode;
|
||||
|
||||
// Lock the buffer
|
||||
HardwareLock();
|
||||
|
||||
// Check that data and stride were filled in
|
||||
JPH_ASSERT(mData != nullptr);
|
||||
JPH_ASSERT(mStride > 0);
|
||||
JPH_ASSERT(mLength > 0);
|
||||
}
|
||||
|
||||
void Surface::UnLock() const
|
||||
{
|
||||
// Check if this resource was locked
|
||||
JPH_ASSERT(IsLocked());
|
||||
|
||||
// Unlock the hardware resource
|
||||
HardwareUnLock();
|
||||
|
||||
// Reset members, so we are sure they will be set next time
|
||||
mLockMode = ESurfaceLockMode::None;
|
||||
mStride = 0;
|
||||
mLength = 0;
|
||||
mData = nullptr;
|
||||
}
|
||||
|
||||
void Surface::Clear(ColorArg inColor)
|
||||
{
|
||||
Lock(ESurfaceLockMode::Write);
|
||||
|
||||
// Get image properties
|
||||
int bpp = GetBytesPerPixel();
|
||||
int width = GetWidth();
|
||||
int height = GetHeight();
|
||||
|
||||
// Determine clear color
|
||||
uint32 col = GetFormatDescription().Encode(inColor);
|
||||
|
||||
// Clear the image
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
uint8 *d = GetScanLine(y);
|
||||
uint8 *d_end = GetScanLine(y) + width * bpp;
|
||||
|
||||
while (d < d_end)
|
||||
{
|
||||
memcpy(d, &col, bpp);
|
||||
d += bpp;
|
||||
}
|
||||
}
|
||||
|
||||
UnLock();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SoftwareSurface
|
||||
//
|
||||
// Class that contains an image in arbitrary format
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SoftwareSurface::SoftwareSurface(int inWidth, int inHeight, ESurfaceFormat inFormat, int inStride) :
|
||||
Surface(inWidth, inHeight, inFormat)
|
||||
{
|
||||
// Determine stride and length
|
||||
mPixelStride = inStride == 0? ((mWidth * GetBytesPerPixel() + 3) & ~3) : inStride;
|
||||
mPixelLength = mPixelStride * inHeight;
|
||||
|
||||
// Allocate pixel data
|
||||
JPH_ASSERT(mPixelLength > 0);
|
||||
mPixelData = new uint8 [mPixelLength];
|
||||
}
|
||||
|
||||
SoftwareSurface::~SoftwareSurface()
|
||||
{
|
||||
delete [] mPixelData;
|
||||
}
|
||||
|
||||
void SoftwareSurface::HardwareLock() const
|
||||
{
|
||||
// Get pointer to data
|
||||
mData = mPixelData;
|
||||
mStride = mPixelStride;
|
||||
mLength = mPixelLength;
|
||||
}
|
||||
|
||||
void SoftwareSurface::HardwareUnLock() const
|
||||
{
|
||||
}
|
||||
|
||||
181
lib/All/JoltPhysics/TestFramework/Image/Surface.h
Normal file
181
lib/All/JoltPhysics/TestFramework/Image/Surface.h
Normal file
@@ -0,0 +1,181 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
#include <Jolt/Core/Color.h>
|
||||
#include <Jolt/Core/StringTools.h>
|
||||
|
||||
/// Possible lock modes of a Surface
|
||||
enum class ESurfaceLockMode : uint
|
||||
{
|
||||
None = 0 << 0, ///< Not locked, cannot be used as a parameter
|
||||
Read = 1 << 0,
|
||||
Write = 2 << 0,
|
||||
ReadWrite = Read | Write,
|
||||
};
|
||||
|
||||
/// Possible surface formats, most significant bit (MSB) first
|
||||
enum class ESurfaceFormat : uint
|
||||
{
|
||||
A4L4, ///< 4 bit alpha, 4 bit luminance (grayscale)
|
||||
L8, ///< 8 bit luminance (grayscale)
|
||||
A8, ///< 8 bit alpha
|
||||
A8L8, ///< 8 bit luminance and 8 bit alpha
|
||||
R5G6B5, ///< 16 bit RGB
|
||||
X1R5G5B5, ///< 16 bit RGB
|
||||
X4R4G4B4, ///< 16 bit RGB
|
||||
A1R5G5B5, ///< 16 bit RGBA
|
||||
A4R4G4B4, ///< 16 bit RGBA
|
||||
R8G8B8, ///< 24 bit RGB
|
||||
B8G8R8, ///< 24 bit BGR
|
||||
X8R8G8B8, ///< 32 bit RGB
|
||||
X8B8G8R8, ///< 32 bit RGB
|
||||
A8R8G8B8, ///< 32 bit RGBA
|
||||
A8B8G8R8, ///< 32 bit BGRA
|
||||
Invalid, ///< Invalid value
|
||||
Count = Invalid, ///< Number of pixel formats
|
||||
};
|
||||
|
||||
/// Description of a surface format
|
||||
class FormatDescription
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
FormatDescription(const char *inFormatName, int inBitsPerPixel, int inNumberOfComponents, ESurfaceFormat inClosest8BitFormat, ESurfaceFormat inClosestAlphaFormat, uint32 inRedMask, uint32 inGreenMask, uint32 inBlueMask, uint32 inAlphaMask);
|
||||
|
||||
/// General properties
|
||||
const string_view & GetFormatName() const { return mFormatName; }
|
||||
int GetBytesPerPixel() const { return (mBitsPerPixel + 7) >> 3; }
|
||||
int GetNumberOfComponents() const { return mNumberOfComponents; }
|
||||
ESurfaceFormat GetClosest8BitFormat() const { return mClosest8BitFormat; }
|
||||
ESurfaceFormat GetClosestAlphaFormat() const { return mClosestAlphaFormat; }
|
||||
|
||||
/// Bitcounts for the various components of the image
|
||||
int GetBitsPerPixel() const { return mBitsPerPixel; }
|
||||
int GetRedBitsPerPixel() const { return CountBits(mRedMask); }
|
||||
int GetGreenBitsPerPixel() const { return CountBits(mGreenMask); }
|
||||
int GetBlueBitsPerPixel() const { return CountBits(mBlueMask); }
|
||||
int GetAlphaBitsPerPixel() const { return CountBits(mAlphaMask); }
|
||||
int GetComponentBitCount(int inComponent) const { return CountBits(GetComponentMask(inComponent)); }
|
||||
|
||||
/// Bitmasks indicating the various components of the image
|
||||
uint32 GetRedMask() const { return mRedMask; }
|
||||
uint32 GetGreenMask() const { return mGreenMask; }
|
||||
uint32 GetBlueMask() const { return mBlueMask; }
|
||||
uint32 GetAlphaMask() const { return mAlphaMask; }
|
||||
uint32 GetComponentMask(int inComponent) const { return *(&mRedMask + inComponent); }
|
||||
|
||||
/// Convert a single color
|
||||
uint32 Encode(ColorArg inColor) const;
|
||||
const Color Decode(uint32 inColor) const;
|
||||
|
||||
private:
|
||||
string_view mFormatName; ///< User displayable String describing the format
|
||||
int mBitsPerPixel; ///< Number of bits per pixel
|
||||
int mNumberOfComponents; ///< Number of color components per pixel
|
||||
ESurfaceFormat mClosest8BitFormat; ///< Closest matching format that has 8 bit color components
|
||||
ESurfaceFormat mClosestAlphaFormat; ///< Closest matching format that has an alpha channel
|
||||
|
||||
uint32 mRedMask; ///< Bitmasks indicating which bits are used by which color components
|
||||
uint32 mGreenMask;
|
||||
uint32 mBlueMask;
|
||||
uint32 mAlphaMask;
|
||||
};
|
||||
|
||||
/// Get the description for a specific surface format
|
||||
const FormatDescription & GetFormatDescription(ESurfaceFormat inFormat);
|
||||
|
||||
/// Class that contains an image in arbitrary format
|
||||
class Surface : public RefTarget<Surface>
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
Surface(int inWidth, int inHeight, ESurfaceFormat inFormat);
|
||||
virtual ~Surface();
|
||||
|
||||
/// Type of the image data
|
||||
const FormatDescription & GetFormatDescription() const { return ::GetFormatDescription(mFormat); }
|
||||
|
||||
const string_view & GetFormatName() const { return GetFormatDescription().GetFormatName(); }
|
||||
int GetBytesPerPixel() const { return GetFormatDescription().GetBytesPerPixel(); }
|
||||
int GetNumberOfComponents() const { return GetFormatDescription().GetNumberOfComponents(); }
|
||||
ESurfaceFormat GetClosest8BitFormat() const { return GetFormatDescription().GetClosest8BitFormat(); }
|
||||
|
||||
int GetBitsPerPixel() const { return GetFormatDescription().GetBitsPerPixel(); }
|
||||
int GetRedBitsPerPixel() const { return GetFormatDescription().GetRedBitsPerPixel(); }
|
||||
int GetGreenBitsPerPixel() const { return GetFormatDescription().GetGreenBitsPerPixel(); }
|
||||
int GetBlueBitsPerPixel() const { return GetFormatDescription().GetBlueBitsPerPixel(); }
|
||||
int GetAlphaBitsPerPixel() const { return GetFormatDescription().GetAlphaBitsPerPixel(); }
|
||||
int GetComponentBitCount(int inComponent) const { return GetFormatDescription().GetComponentBitCount(inComponent); }
|
||||
|
||||
uint32 GetRedMask() const { return GetFormatDescription().GetRedMask(); }
|
||||
uint32 GetGreenMask() const { return GetFormatDescription().GetGreenMask(); }
|
||||
uint32 GetBlueMask() const { return GetFormatDescription().GetBlueMask(); }
|
||||
uint32 GetAlphaMask() const { return GetFormatDescription().GetAlphaMask(); }
|
||||
uint32 GetComponentMask(int inComponent) const { return GetFormatDescription().GetComponentMask(inComponent); }
|
||||
|
||||
/// Get properties of this surface
|
||||
inline ESurfaceFormat GetFormat() const { return mFormat; }
|
||||
inline int GetWidth() const { return mWidth; }
|
||||
inline int GetHeight() const { return mHeight; }
|
||||
|
||||
/// Sets the image to a specific color
|
||||
void Clear(ColorArg inColor = Color::sBlack);
|
||||
|
||||
/// Locking functions
|
||||
void Lock(ESurfaceLockMode inMode) const;
|
||||
void UnLock() const;
|
||||
|
||||
/// Current lock state
|
||||
inline ESurfaceLockMode GetLockMode() const { return mLockMode; }
|
||||
inline bool IsLocked() const { return mLockMode != ESurfaceLockMode::None; }
|
||||
inline bool IsLockedForRead() const { return (uint(mLockMode) & uint(ESurfaceLockMode::Read)) != 0; }
|
||||
inline bool IsLockedForWrite() const { return (uint(mLockMode) & uint(ESurfaceLockMode::Write)) != 0; }
|
||||
inline bool IsLockedForReadWrite() const { return IsLockedForRead() && IsLockedForWrite(); }
|
||||
|
||||
/// Access to the image data
|
||||
inline const uint8 * GetData() const { JPH_ASSERT(IsLockedForRead()); return mData; }
|
||||
inline uint8 * GetData() { JPH_ASSERT(IsLockedForWrite()); return mData; }
|
||||
inline int GetStride() const { JPH_ASSERT(IsLocked()); return mStride; }
|
||||
inline int GetLength() const { JPH_ASSERT(IsLocked()); return mLength; }
|
||||
|
||||
/// Get start of a specific scanline
|
||||
inline const uint8 * GetScanLine(int inScanLine) const { JPH_ASSERT(inScanLine >= 0 && inScanLine < GetHeight()); return GetData() + inScanLine * GetStride(); }
|
||||
inline uint8 * GetScanLine(int inScanLine) { JPH_ASSERT(inScanLine >= 0 && inScanLine < GetHeight()); return GetData() + inScanLine * GetStride(); }
|
||||
|
||||
protected:
|
||||
/// These functions must be overridden by the hardware buffer
|
||||
virtual void HardwareLock() const = 0;
|
||||
virtual void HardwareUnLock() const = 0;
|
||||
|
||||
/// Data
|
||||
ESurfaceFormat mFormat; ///< Pixel format of the surface
|
||||
int mWidth; ///< Width of the image
|
||||
int mHeight; ///< Height of the image
|
||||
mutable int mLength; ///< Length in bytes of the image
|
||||
|
||||
mutable ESurfaceLockMode mLockMode;
|
||||
mutable int mStride; ///< Width of one scanline in bytes
|
||||
mutable uint8 * mData; ///< Pointer to image data, starting at top-left of locked rectangle
|
||||
};
|
||||
|
||||
/// Class that contains an image in arbitrary format, backed by normal memory (not device specific)
|
||||
class SoftwareSurface : public Surface
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
SoftwareSurface(int inWidth, int inHeight, ESurfaceFormat inFormat, int inStride = 0);
|
||||
virtual ~SoftwareSurface() override;
|
||||
|
||||
protected:
|
||||
/// These functions must be overridden by the hardware buffer
|
||||
virtual void HardwareLock() const override;
|
||||
virtual void HardwareUnLock() const override;
|
||||
|
||||
uint8 * mPixelData;
|
||||
int mPixelStride;
|
||||
int mPixelLength;
|
||||
};
|
||||
526
lib/All/JoltPhysics/TestFramework/Image/ZoomImage.cpp
Normal file
526
lib/All/JoltPhysics/TestFramework/Image/ZoomImage.cpp
Normal file
@@ -0,0 +1,526 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Image/ZoomImage.h>
|
||||
#include <Image/Surface.h>
|
||||
#include <Image/BlitSurface.h>
|
||||
#include <Jolt/Core/Profiler.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ImageFilter
|
||||
//
|
||||
// Abstract class and some implementations of a filter, essentially an 1D weighting function
|
||||
// which is not zero for t e [-GetSupport(), GetSupport()] and zero for all other t
|
||||
// The integrand is usually 1 although it is not required for this implementation,
|
||||
// since the filter is renormalized when it is sampled.
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ImageFilter
|
||||
{
|
||||
public:
|
||||
// Destructor
|
||||
virtual ~ImageFilter() = default;
|
||||
|
||||
// Get support of this filter (+/- the range the filter function is not zero)
|
||||
virtual float GetSupport() const = 0;
|
||||
|
||||
// Sample filter function at a certain point
|
||||
virtual float GetValue(float t) const = 0;
|
||||
};
|
||||
|
||||
class ImageFilterBox : public ImageFilter
|
||||
{
|
||||
virtual float GetSupport() const override
|
||||
{
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
virtual float GetValue(float t) const override
|
||||
{
|
||||
if (abs(t) <= 0.5f)
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
class ImageFilterTriangle : public ImageFilter
|
||||
{
|
||||
virtual float GetSupport() const override
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
virtual float GetValue(float t) const override
|
||||
{
|
||||
t = abs(t);
|
||||
|
||||
if (t < 1.0f)
|
||||
return 1.0f - t;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
class ImageFilterBell : public ImageFilter
|
||||
{
|
||||
virtual float GetSupport() const override
|
||||
{
|
||||
return 1.5f;
|
||||
}
|
||||
|
||||
virtual float GetValue(float t) const override
|
||||
{
|
||||
t = abs(t);
|
||||
|
||||
if (t < 0.5f)
|
||||
return 0.75f - t * t;
|
||||
else if (t < 1.5f)
|
||||
{
|
||||
t = t - 1.5f;
|
||||
return 0.5f * t * t;
|
||||
}
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
class ImageFilterBSpline : public ImageFilter
|
||||
{
|
||||
virtual float GetSupport() const override
|
||||
{
|
||||
return 2.0f;
|
||||
}
|
||||
|
||||
virtual float GetValue(float t) const override
|
||||
{
|
||||
t = abs(t);
|
||||
|
||||
if (t < 1.0f)
|
||||
{
|
||||
float tt = t * t;
|
||||
return (0.5f * tt * t) - tt + (2.0f / 3.0f);
|
||||
}
|
||||
else if (t < 2.0f)
|
||||
{
|
||||
t = 2.0f - t;
|
||||
return (1.0f / 6.0f) * (t * t * t);
|
||||
}
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
class ImageFilterLanczos3 : public ImageFilter
|
||||
{
|
||||
virtual float GetSupport() const override
|
||||
{
|
||||
return 3.0f;
|
||||
}
|
||||
|
||||
virtual float GetValue(float t) const override
|
||||
{
|
||||
t = abs(t);
|
||||
|
||||
if (t < 3.0f)
|
||||
return Sinc(t) * Sinc(t / 3.0f);
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
private:
|
||||
static float Sinc(float x)
|
||||
{
|
||||
x *= JPH_PI;
|
||||
|
||||
if (abs(x) < 1.0e-5f)
|
||||
return 1.0f;
|
||||
|
||||
return Sin(x) / x;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class ImageFilterMitchell : public ImageFilter
|
||||
{
|
||||
virtual float GetSupport() const override
|
||||
{
|
||||
return 2.0f;
|
||||
}
|
||||
|
||||
virtual float GetValue(float t) const override
|
||||
{
|
||||
float tt = t * t;
|
||||
t = abs(t);
|
||||
|
||||
if (t < 1.0f)
|
||||
return (7.0f * (t * tt) - 12.0f * tt + (16.0f / 3.0f)) / 6.0f;
|
||||
else if (t < 2.0f)
|
||||
return ((-7.0f / 3.0f) * (t * tt) + 12.0f * tt + -20.0f * t + (32.0f / 3.0f)) / 6.0f;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
static const ImageFilter &GetFilter(EFilter inFilter)
|
||||
{
|
||||
static ImageFilterBox box;
|
||||
static ImageFilterTriangle triangle;
|
||||
static ImageFilterBell bell;
|
||||
static ImageFilterBSpline bspline;
|
||||
static ImageFilterLanczos3 lanczos3;
|
||||
static ImageFilterMitchell mitchell;
|
||||
|
||||
switch (inFilter)
|
||||
{
|
||||
case FilterBox: return box;
|
||||
case FilterTriangle: return triangle;
|
||||
case FilterBell: return bell;
|
||||
case FilterBSpline: return bspline;
|
||||
case FilterLanczos3: return lanczos3;
|
||||
case FilterMitchell: return mitchell;
|
||||
default: JPH_ASSERT(false); return mitchell;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ZoomSettings
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const ZoomSettings ZoomSettings::sDefault;
|
||||
|
||||
ZoomSettings::ZoomSettings() :
|
||||
mFilter(FilterMitchell),
|
||||
mWrapFilter(true),
|
||||
mBlur(1.0f)
|
||||
{
|
||||
}
|
||||
|
||||
bool ZoomSettings::operator == (const ZoomSettings &inRHS) const
|
||||
{
|
||||
return mFilter == inRHS.mFilter
|
||||
&& mWrapFilter == inRHS.mWrapFilter
|
||||
&& mBlur == inRHS.mBlur;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Resizing a surface
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Structure used for zooming
|
||||
struct Contrib
|
||||
{
|
||||
int mOffset; // Offset of this pixel (relative to start of scanline)
|
||||
int mWeight; // Weight of this pixel in 0.12 fixed point format
|
||||
};
|
||||
|
||||
static void sPrecalculateFilter(const ZoomSettings &inZoomSettings, int inOldLength, int inNewLength, int inOffsetFactor, Array<Array<Contrib>> &outContrib)
|
||||
{
|
||||
JPH_PROFILE("PrecalculateFilter");
|
||||
|
||||
// Get filter
|
||||
const ImageFilter &filter = GetFilter(inZoomSettings.mFilter);
|
||||
|
||||
// Get scale
|
||||
float scale = float(inNewLength) / inOldLength;
|
||||
|
||||
float fwidth, fscale;
|
||||
if (scale < 1.0f)
|
||||
{
|
||||
// Minify, broaden filter
|
||||
fwidth = filter.GetSupport() / scale;
|
||||
fscale = scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Enlarge, filter is always used as is
|
||||
fwidth = filter.GetSupport();
|
||||
fscale = 1.0f;
|
||||
}
|
||||
|
||||
// Adjust filter for blur
|
||||
fwidth *= inZoomSettings.mBlur;
|
||||
fscale /= inZoomSettings.mBlur;
|
||||
float min_fwidth = 1.0f;
|
||||
if (fwidth < min_fwidth)
|
||||
{
|
||||
fwidth = min_fwidth;
|
||||
fscale = filter.GetSupport() / min_fwidth;
|
||||
}
|
||||
|
||||
// Make room for a whole scanline
|
||||
outContrib.resize(inNewLength);
|
||||
|
||||
// Loop over the whole scanline
|
||||
for (int i = 0; i < inNewLength; ++i)
|
||||
{
|
||||
// Compute center and left- and rightmost pixels affected
|
||||
float center = float(i) / scale;
|
||||
int left = int(floor(center - fwidth));
|
||||
int right = int(ceil(center + fwidth));
|
||||
|
||||
// Reserve required elements
|
||||
Array<Contrib> &a = outContrib[i];
|
||||
a.reserve(right - left + 1);
|
||||
|
||||
// Total sum of all weights, for renormalization of the filter
|
||||
int filter_sum = 0;
|
||||
|
||||
// Compute the contributions for each
|
||||
for (int source = left; source <= right; ++source)
|
||||
{
|
||||
Contrib c;
|
||||
|
||||
// Initialize the offset
|
||||
c.mOffset = source;
|
||||
|
||||
// Compute weight at this position in 0.12 fixed point
|
||||
c.mWeight = int(4096.0f * filter.GetValue(fscale * (center - source)));
|
||||
if (c.mWeight == 0) continue;
|
||||
|
||||
// Add weight to filter total
|
||||
filter_sum += c.mWeight;
|
||||
|
||||
// Reflect the filter at the edges if the filter is not to be wrapped (clamp)
|
||||
if (!inZoomSettings.mWrapFilter && (c.mOffset < 0 || c.mOffset >= inOldLength))
|
||||
c.mOffset = -c.mOffset - 1;
|
||||
|
||||
// Wrap the offset so that it falls within the image
|
||||
c.mOffset = (c.mOffset % inOldLength + inOldLength) % inOldLength;
|
||||
|
||||
// Check that the offset falls within the image
|
||||
JPH_ASSERT(c.mOffset >= 0 && c.mOffset < inOldLength);
|
||||
|
||||
// Multiply the offset with the specified factor
|
||||
c.mOffset *= inOffsetFactor;
|
||||
|
||||
// Add the filter element
|
||||
a.push_back(c);
|
||||
}
|
||||
|
||||
// Normalize the filter to 0.12 fixed point
|
||||
if (filter_sum != 0)
|
||||
for (uint n = 0; n < a.size(); ++n)
|
||||
a[n].mWeight = (a[n].mWeight * 4096) / filter_sum;
|
||||
}
|
||||
}
|
||||
|
||||
static void sZoomHorizontal(RefConst<Surface> inSrc, Ref<Surface> ioDst, const ZoomSettings &inZoomSettings)
|
||||
{
|
||||
JPH_PROFILE("ZoomHorizontal");
|
||||
|
||||
// Check zoom parameters
|
||||
JPH_ASSERT(inSrc->GetHeight() == ioDst->GetHeight());
|
||||
JPH_ASSERT(inSrc->GetFormat() == ioDst->GetFormat());
|
||||
|
||||
const int width = ioDst->GetWidth();
|
||||
const int height = ioDst->GetHeight();
|
||||
const int components = ioDst->GetNumberOfComponents();
|
||||
const int delta_s = -components;
|
||||
const int delta_d = ioDst->GetBytesPerPixel() - components;
|
||||
|
||||
// Pre-calculate filter contributions for a row
|
||||
Array<Array<Contrib>> contrib;
|
||||
sPrecalculateFilter(inZoomSettings, inSrc->GetWidth(), ioDst->GetWidth(), inSrc->GetBytesPerPixel(), contrib);
|
||||
|
||||
// Do the zoom
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
const uint8 *s = inSrc->GetScanLine(y);
|
||||
uint8 *d = ioDst->GetScanLine(y);
|
||||
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
const Array<Contrib> &line = contrib[x];
|
||||
const size_t line_size_min_one = line.size() - 1;
|
||||
|
||||
int c = components;
|
||||
do
|
||||
{
|
||||
int pixel = 0;
|
||||
|
||||
// Apply the filter for one color component
|
||||
size_t j = line_size_min_one;
|
||||
do
|
||||
{
|
||||
const Contrib &cmp = line[j];
|
||||
pixel += cmp.mWeight * s[cmp.mOffset];
|
||||
}
|
||||
while (j--);
|
||||
|
||||
// Clamp the pixel value
|
||||
if (pixel <= 0)
|
||||
*d = 0;
|
||||
else if (pixel >= (255 << 12))
|
||||
*d = 255;
|
||||
else
|
||||
*d = uint8(pixel >> 12);
|
||||
|
||||
++s;
|
||||
++d;
|
||||
}
|
||||
while (--c);
|
||||
|
||||
// Skip unused components if there are any
|
||||
s += delta_s;
|
||||
d += delta_d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sZoomVertical(RefConst<Surface> inSrc, Ref<Surface> ioDst, const ZoomSettings &inZoomSettings)
|
||||
{
|
||||
JPH_PROFILE("ZoomVertical");
|
||||
|
||||
// Check zoom parameters
|
||||
JPH_ASSERT(inSrc->GetWidth() == ioDst->GetWidth());
|
||||
JPH_ASSERT(inSrc->GetFormat() == ioDst->GetFormat());
|
||||
|
||||
const int width = ioDst->GetWidth();
|
||||
const int height = ioDst->GetHeight();
|
||||
const int components = ioDst->GetNumberOfComponents();
|
||||
const int delta_s = inSrc->GetBytesPerPixel() - components;
|
||||
const int delta_d = ioDst->GetBytesPerPixel() - components;
|
||||
|
||||
// Pre-calculate filter contributions for a row
|
||||
Array<Array<Contrib>> contrib;
|
||||
sPrecalculateFilter(inZoomSettings, inSrc->GetHeight(), ioDst->GetHeight(), inSrc->GetStride(), contrib);
|
||||
|
||||
// Do the zoom
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
const uint8 *s = inSrc->GetScanLine(0);
|
||||
uint8 *d = ioDst->GetScanLine(y);
|
||||
const Array<Contrib> &line = contrib[y];
|
||||
const size_t line_size_min_one = line.size() - 1;
|
||||
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
int c = components;
|
||||
do
|
||||
{
|
||||
int pixel = 0;
|
||||
|
||||
// Apply the filter for one color component
|
||||
size_t j = line_size_min_one;
|
||||
do
|
||||
{
|
||||
const Contrib &cmp = line[j];
|
||||
pixel += cmp.mWeight * s[cmp.mOffset];
|
||||
}
|
||||
while (j--);
|
||||
|
||||
// Clamp the pixel value
|
||||
if (pixel <= 0)
|
||||
*d = 0;
|
||||
else if (pixel >= (255 << 12))
|
||||
*d = 255;
|
||||
else
|
||||
*d = uint8(pixel >> 12);
|
||||
|
||||
++s;
|
||||
++d;
|
||||
}
|
||||
while (--c);
|
||||
|
||||
// Skip unused components if there are any
|
||||
s += delta_s;
|
||||
d += delta_d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ZoomImage(RefConst<Surface> inSrc, Ref<Surface> ioDst, const ZoomSettings &inZoomSettings)
|
||||
{
|
||||
JPH_PROFILE("ZoomImage");
|
||||
|
||||
// Get filter
|
||||
const ImageFilter &filter = GetFilter(inZoomSettings.mFilter);
|
||||
|
||||
// Determine the temporary format that will require the least amount of components to be zoomed and the least amount of bytes pushed around
|
||||
ESurfaceFormat tmp_format;
|
||||
ESurfaceFormat src_format = inSrc->GetClosest8BitFormat();
|
||||
ESurfaceFormat dst_format = ioDst->GetClosest8BitFormat();
|
||||
const FormatDescription &src_desc = GetFormatDescription(src_format);
|
||||
const FormatDescription &dst_desc = GetFormatDescription(dst_format);
|
||||
if (src_desc.GetNumberOfComponents() < dst_desc.GetNumberOfComponents())
|
||||
tmp_format = src_format;
|
||||
else if (src_desc.GetNumberOfComponents() > dst_desc.GetNumberOfComponents())
|
||||
tmp_format = dst_format;
|
||||
else if (src_desc.GetBytesPerPixel() < dst_desc.GetBytesPerPixel())
|
||||
tmp_format = src_format;
|
||||
else
|
||||
tmp_format = dst_format;
|
||||
|
||||
// Create temporary source buffer if necessary
|
||||
RefConst<Surface> src = inSrc;
|
||||
if (inSrc->GetFormat() != tmp_format)
|
||||
{
|
||||
Ref<Surface> tmp = new SoftwareSurface(inSrc->GetWidth(), inSrc->GetHeight(), tmp_format);
|
||||
if (!BlitSurface(inSrc, tmp))
|
||||
return false;
|
||||
src = tmp;
|
||||
}
|
||||
|
||||
// Create temporary destination buffer if necessary
|
||||
Ref<Surface> dst = ioDst;
|
||||
if (ioDst->GetFormat() != tmp_format)
|
||||
dst = new SoftwareSurface(ioDst->GetWidth(), ioDst->GetHeight(), tmp_format);
|
||||
|
||||
src->Lock(ESurfaceLockMode::Read);
|
||||
dst->Lock(ESurfaceLockMode::Write);
|
||||
|
||||
if (src->GetWidth() == dst->GetWidth())
|
||||
{
|
||||
// Only vertical zoom required
|
||||
sZoomVertical(src, dst, inZoomSettings);
|
||||
}
|
||||
else if (src->GetHeight() == dst->GetHeight())
|
||||
{
|
||||
// Only horizontal zoom required
|
||||
sZoomHorizontal(src, dst, inZoomSettings);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Determine most optimal order
|
||||
float operations_vh = float(dst->GetWidth()) * (filter.GetSupport() * src->GetHeight() + filter.GetSupport() * dst->GetHeight());
|
||||
float operations_hv = float(dst->GetHeight()) * (filter.GetSupport() * src->GetWidth() + filter.GetSupport() * dst->GetWidth());
|
||||
if (operations_vh < operations_hv)
|
||||
{
|
||||
// Create temporary buffer to hold the vertical scale
|
||||
Ref<Surface> tmp = new SoftwareSurface(src->GetWidth(), dst->GetHeight(), tmp_format);
|
||||
tmp->Lock(ESurfaceLockMode::ReadWrite);
|
||||
|
||||
// First scale vertically then horizontally
|
||||
sZoomVertical(src, tmp, inZoomSettings);
|
||||
sZoomHorizontal(tmp, dst, inZoomSettings);
|
||||
|
||||
tmp->UnLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create temporary buffer to hold the horizontal scale
|
||||
Ref<Surface> tmp = new SoftwareSurface(dst->GetWidth(), src->GetHeight(), tmp_format);
|
||||
tmp->Lock(ESurfaceLockMode::ReadWrite);
|
||||
|
||||
// First scale horizontally then vertically
|
||||
sZoomHorizontal(src, tmp, inZoomSettings);
|
||||
sZoomVertical(tmp, dst, inZoomSettings);
|
||||
|
||||
tmp->UnLock();
|
||||
}
|
||||
}
|
||||
|
||||
src->UnLock();
|
||||
dst->UnLock();
|
||||
|
||||
// Convert to destination if required
|
||||
if (dst != ioDst)
|
||||
if (!BlitSurface(dst, ioDst))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
41
lib/All/JoltPhysics/TestFramework/Image/ZoomImage.h
Normal file
41
lib/All/JoltPhysics/TestFramework/Image/ZoomImage.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
|
||||
class Surface;
|
||||
|
||||
/// Filter function used to rescale the image
|
||||
enum EFilter
|
||||
{
|
||||
FilterBox,
|
||||
FilterTriangle,
|
||||
FilterBell,
|
||||
FilterBSpline,
|
||||
FilterLanczos3,
|
||||
FilterMitchell,
|
||||
};
|
||||
|
||||
/// Zoom settings for ZoomImage
|
||||
class ZoomSettings
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
ZoomSettings();
|
||||
|
||||
/// Comparison operators
|
||||
bool operator == (const ZoomSettings &inRHS) const;
|
||||
|
||||
/// Default settings
|
||||
static const ZoomSettings sDefault;
|
||||
|
||||
EFilter mFilter; ///< Filter function for image scaling
|
||||
bool mWrapFilter; ///< If true, the filter will be applied wrapping around the image, this provides better results for repeating textures
|
||||
float mBlur; ///< If > 1 then the image will be blurred, if < 1 the image will be sharpened
|
||||
};
|
||||
|
||||
/// Function to resize an image
|
||||
bool ZoomImage(RefConst<Surface> inSrc, Ref<Surface> ioDst, const ZoomSettings &inZoomSettings = ZoomSettings::sDefault);
|
||||
96
lib/All/JoltPhysics/TestFramework/Input/Keyboard.h
Normal file
96
lib/All/JoltPhysics/TestFramework/Input/Keyboard.h
Normal file
@@ -0,0 +1,96 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
class ApplicationWindow;
|
||||
|
||||
enum class EKey
|
||||
{
|
||||
Invalid,
|
||||
Unknown,
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
E,
|
||||
F,
|
||||
G,
|
||||
H,
|
||||
I,
|
||||
J,
|
||||
K,
|
||||
L,
|
||||
M,
|
||||
N,
|
||||
O,
|
||||
P,
|
||||
Q,
|
||||
R,
|
||||
S,
|
||||
T,
|
||||
U,
|
||||
V,
|
||||
W,
|
||||
X,
|
||||
Y,
|
||||
Z,
|
||||
Num0,
|
||||
Num1,
|
||||
Num2,
|
||||
Num3,
|
||||
Num4,
|
||||
Num5,
|
||||
Num6,
|
||||
Num7,
|
||||
Num8,
|
||||
Num9,
|
||||
Space,
|
||||
Comma,
|
||||
Period,
|
||||
Escape,
|
||||
LShift,
|
||||
RShift,
|
||||
LControl,
|
||||
RControl,
|
||||
LAlt,
|
||||
RAlt,
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Down,
|
||||
Return,
|
||||
NumKeys,
|
||||
};
|
||||
|
||||
/// Keyboard interface class which keeps track on the status of all keys and keeps track of the list of keys pressed.
|
||||
class Keyboard
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
Keyboard() = default;
|
||||
virtual ~Keyboard() = default;
|
||||
|
||||
/// Initialization / shutdown
|
||||
virtual bool Initialize(ApplicationWindow *inWindow) = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
/// Update the keyboard state
|
||||
virtual void Poll() = 0;
|
||||
|
||||
/// Checks if a key is pressed or not
|
||||
virtual bool IsKeyPressed(EKey inKey) const = 0;
|
||||
|
||||
/// Checks if a key is pressed and was not pressed the last time this function was called (state is stored in ioPrevState)
|
||||
bool IsKeyPressedAndTriggered(EKey inKey, bool &ioPrevState) const
|
||||
{
|
||||
bool prev_state = ioPrevState;
|
||||
ioPrevState = IsKeyPressed(inKey);
|
||||
return ioPrevState && !prev_state;
|
||||
}
|
||||
|
||||
/// Buffered keyboard input, returns EKey::Invalid for none
|
||||
virtual EKey GetFirstKey() = 0;
|
||||
virtual EKey GetNextKey() = 0;
|
||||
};
|
||||
153
lib/All/JoltPhysics/TestFramework/Input/Linux/KeyboardLinux.cpp
Normal file
153
lib/All/JoltPhysics/TestFramework/Input/Linux/KeyboardLinux.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Input/Linux/KeyboardLinux.h>
|
||||
#include <Window/ApplicationWindowLinux.h>
|
||||
|
||||
KeyboardLinux::~KeyboardLinux()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
bool KeyboardLinux::Initialize(ApplicationWindow *inWindow)
|
||||
{
|
||||
mWindow = static_cast<ApplicationWindowLinux *>(inWindow);
|
||||
mWindow->SetEventListener([this](const XEvent &inEvent) { HandleEvent(inEvent); });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void KeyboardLinux::Shutdown()
|
||||
{
|
||||
if (mWindow != nullptr)
|
||||
{
|
||||
mWindow->SetEventListener({});
|
||||
mWindow = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardLinux::Poll()
|
||||
{
|
||||
// Reset the keys pressed
|
||||
memset(mKeysPressed, 0, sizeof(mKeysPressed));
|
||||
|
||||
Display *display = mWindow->GetDisplay();
|
||||
|
||||
// Get pressed keys
|
||||
char keymap[32];
|
||||
XQueryKeymap(display, keymap);
|
||||
for (int i = 0; i < 32; ++i)
|
||||
{
|
||||
// Iterate 8 bits at a time
|
||||
uint keycode = i << 3;
|
||||
uint32 value = uint8(keymap[i]);
|
||||
while (value != 0)
|
||||
{
|
||||
// Get the next bit
|
||||
uint lz = CountTrailingZeros(value);
|
||||
keycode += lz;
|
||||
|
||||
// Convert to key
|
||||
KeySym keysym = XkbKeycodeToKeysym(display, keycode, 0, 0);
|
||||
EKey key = ToKey(keysym);
|
||||
if (key != EKey::Unknown)
|
||||
mKeysPressed[(int)key] = true;
|
||||
|
||||
// Skip this bit
|
||||
keycode++;
|
||||
value >>= lz + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Make the pending buffer the active buffer
|
||||
mKeyBuffer = mPendingKeyBuffer;
|
||||
mPendingKeyBuffer.clear();
|
||||
}
|
||||
|
||||
EKey KeyboardLinux::GetFirstKey()
|
||||
{
|
||||
mCurrentKey = 0;
|
||||
return GetNextKey();
|
||||
}
|
||||
|
||||
EKey KeyboardLinux::GetNextKey()
|
||||
{
|
||||
if (mCurrentKey < mKeyBuffer.size())
|
||||
return mKeyBuffer[mCurrentKey++];
|
||||
return EKey::Invalid;
|
||||
}
|
||||
|
||||
void KeyboardLinux::HandleEvent(const XEvent &inEvent)
|
||||
{
|
||||
// If this is a key press event and the buffer is not yet full
|
||||
if (inEvent.type == KeyPress && mPendingKeyBuffer.size() < mPendingKeyBuffer.capacity())
|
||||
{
|
||||
// Convert to key
|
||||
KeySym keysym = XkbKeycodeToKeysym(mWindow->GetDisplay(), inEvent.xkey.keycode, 0, 0);
|
||||
EKey key = ToKey(keysym);
|
||||
if (key != EKey::Unknown)
|
||||
mPendingKeyBuffer.push_back(key);
|
||||
}
|
||||
}
|
||||
|
||||
EKey KeyboardLinux::ToKey(int inValue) const
|
||||
{
|
||||
switch (inValue)
|
||||
{
|
||||
case XK_a: return EKey::A;
|
||||
case XK_b: return EKey::B;
|
||||
case XK_c: return EKey::C;
|
||||
case XK_d: return EKey::D;
|
||||
case XK_e: return EKey::E;
|
||||
case XK_f: return EKey::F;
|
||||
case XK_g: return EKey::G;
|
||||
case XK_h: return EKey::H;
|
||||
case XK_i: return EKey::I;
|
||||
case XK_j: return EKey::J;
|
||||
case XK_k: return EKey::K;
|
||||
case XK_l: return EKey::L;
|
||||
case XK_m: return EKey::M;
|
||||
case XK_n: return EKey::N;
|
||||
case XK_o: return EKey::O;
|
||||
case XK_p: return EKey::P;
|
||||
case XK_q: return EKey::Q;
|
||||
case XK_r: return EKey::R;
|
||||
case XK_s: return EKey::S;
|
||||
case XK_t: return EKey::T;
|
||||
case XK_u: return EKey::U;
|
||||
case XK_v: return EKey::V;
|
||||
case XK_w: return EKey::W;
|
||||
case XK_x: return EKey::X;
|
||||
case XK_y: return EKey::Y;
|
||||
case XK_z: return EKey::Z;
|
||||
case XK_0: return EKey::Num0;
|
||||
case XK_1: return EKey::Num1;
|
||||
case XK_2: return EKey::Num2;
|
||||
case XK_3: return EKey::Num3;
|
||||
case XK_4: return EKey::Num4;
|
||||
case XK_5: return EKey::Num5;
|
||||
case XK_6: return EKey::Num6;
|
||||
case XK_7: return EKey::Num7;
|
||||
case XK_8: return EKey::Num8;
|
||||
case XK_9: return EKey::Num9;
|
||||
case XK_space: return EKey::Space;
|
||||
case XK_comma: return EKey::Comma;
|
||||
case XK_period: return EKey::Period;
|
||||
case XK_Escape: return EKey::Escape;
|
||||
case XK_Shift_L: return EKey::LShift;
|
||||
case XK_Shift_R: return EKey::RShift;
|
||||
case XK_Control_L: return EKey::LControl;
|
||||
case XK_Control_R: return EKey::RControl;
|
||||
case XK_Alt_L: return EKey::LAlt;
|
||||
case XK_Alt_R: return EKey::RAlt;
|
||||
case XK_Left: return EKey::Left;
|
||||
case XK_Right: return EKey::Right;
|
||||
case XK_Up: return EKey::Up;
|
||||
case XK_Down: return EKey::Down;
|
||||
case XK_Return: return EKey::Return;
|
||||
default: return EKey::Unknown;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Input/Keyboard.h>
|
||||
#include <Jolt/Core/StaticArray.h>
|
||||
|
||||
class ApplicationWindowLinux;
|
||||
|
||||
/// Keyboard interface class which keeps track on the status of all keys and keeps track of the list of keys pressed.
|
||||
class KeyboardLinux : public Keyboard
|
||||
{
|
||||
public:
|
||||
/// Destructor
|
||||
virtual ~KeyboardLinux() override;
|
||||
|
||||
/// Initialization / shutdown
|
||||
virtual bool Initialize(ApplicationWindow *inWindow) override;
|
||||
virtual void Shutdown() override;
|
||||
|
||||
/// Update the keyboard state
|
||||
virtual void Poll() override;
|
||||
|
||||
/// Checks if a key is pressed or not
|
||||
virtual bool IsKeyPressed(EKey inKey) const override { return mKeysPressed[(int)inKey]; }
|
||||
|
||||
/// Buffered keyboard input, returns EKey::Invalid for none
|
||||
virtual EKey GetFirstKey() override;
|
||||
virtual EKey GetNextKey() override;
|
||||
|
||||
private:
|
||||
void HandleEvent(const XEvent &inEvent);
|
||||
EKey ToKey(int inKey) const;
|
||||
|
||||
ApplicationWindowLinux * mWindow = nullptr;
|
||||
bool mKeysPressed[(int)EKey::NumKeys] = { };
|
||||
StaticArray<EKey, 128> mPendingKeyBuffer;
|
||||
StaticArray<EKey, 128> mKeyBuffer;
|
||||
uint mCurrentKey = 0;
|
||||
};
|
||||
76
lib/All/JoltPhysics/TestFramework/Input/Linux/MouseLinux.cpp
Normal file
76
lib/All/JoltPhysics/TestFramework/Input/Linux/MouseLinux.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Input/Linux/MouseLinux.h>
|
||||
#include <Window/ApplicationWindowLinux.h>
|
||||
|
||||
MouseLinux::MouseLinux()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
MouseLinux::~MouseLinux()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
bool MouseLinux::Initialize(ApplicationWindow *inWindow)
|
||||
{
|
||||
ApplicationWindowLinux *window = static_cast<ApplicationWindowLinux *>(inWindow);
|
||||
mDisplay = window->GetDisplay();
|
||||
mWindow = window->GetWindow();
|
||||
|
||||
// Poll once and reset the deltas
|
||||
Poll();
|
||||
mDX = 0;
|
||||
mDY = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MouseLinux::Shutdown()
|
||||
{
|
||||
mWindow = 0;
|
||||
mDisplay = nullptr;
|
||||
}
|
||||
|
||||
void MouseLinux::Reset()
|
||||
{
|
||||
mX = 0;
|
||||
mY = 0;
|
||||
mDX = 0;
|
||||
mDY = 0;
|
||||
mLeftPressed = false;
|
||||
mRightPressed = false;
|
||||
mMiddlePressed = false;
|
||||
}
|
||||
|
||||
void MouseLinux::Poll()
|
||||
{
|
||||
Window root_return, child_return;
|
||||
int root_x, root_y, win_x, win_y;
|
||||
unsigned int mask;
|
||||
if (XQueryPointer(mDisplay, mWindow, &root_return, &child_return, &root_x, &root_y, &win_x, &win_y, &mask))
|
||||
{
|
||||
mDX = win_x - mX;
|
||||
mDY = win_y - mY;
|
||||
mX = win_x;
|
||||
mY = win_y;
|
||||
mLeftPressed = mask & Button1Mask;
|
||||
mRightPressed = mask & Button3Mask;
|
||||
mMiddlePressed = mask & Button2Mask;
|
||||
}
|
||||
else
|
||||
Reset();
|
||||
}
|
||||
|
||||
void MouseLinux::HideCursor()
|
||||
{
|
||||
}
|
||||
|
||||
void MouseLinux::ShowCursor()
|
||||
{
|
||||
}
|
||||
49
lib/All/JoltPhysics/TestFramework/Input/Linux/MouseLinux.h
Normal file
49
lib/All/JoltPhysics/TestFramework/Input/Linux/MouseLinux.h
Normal file
@@ -0,0 +1,49 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Input/Mouse.h>
|
||||
|
||||
/// Mouse interface class, keeps track of the mouse button state and of the absolute and relative movements of the mouse.
|
||||
class MouseLinux : public Mouse
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
MouseLinux();
|
||||
virtual ~MouseLinux() override;
|
||||
|
||||
/// Initialization / shutdown
|
||||
virtual bool Initialize(ApplicationWindow *inWindow) override;
|
||||
virtual void Shutdown() override;
|
||||
|
||||
/// Update the mouse state
|
||||
virtual void Poll() override;
|
||||
|
||||
virtual int GetX() const override { return mX; }
|
||||
virtual int GetY() const override { return mY; }
|
||||
virtual int GetDX() const override { return mDX; }
|
||||
virtual int GetDY() const override { return mDY; }
|
||||
|
||||
virtual bool IsLeftPressed() const override { return mLeftPressed; }
|
||||
virtual bool IsRightPressed() const override { return mRightPressed; }
|
||||
virtual bool IsMiddlePressed() const override { return mMiddlePressed; }
|
||||
|
||||
virtual void HideCursor() override;
|
||||
virtual void ShowCursor() override;
|
||||
|
||||
private:
|
||||
void Reset();
|
||||
|
||||
Display * mDisplay;
|
||||
Window mWindow;
|
||||
|
||||
int mX;
|
||||
int mY;
|
||||
int mDX;
|
||||
int mDY;
|
||||
bool mLeftPressed;
|
||||
bool mRightPressed;
|
||||
bool mMiddlePressed;
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Input/Keyboard.h>
|
||||
|
||||
/// Keyboard interface class which keeps track on the status of all keys and keeps track of the list of keys pressed.
|
||||
class KeyboardMacOS : public Keyboard
|
||||
{
|
||||
public:
|
||||
/// Initialization / shutdown
|
||||
virtual bool Initialize(ApplicationWindow *inWindow) override;
|
||||
virtual void Shutdown() override { }
|
||||
|
||||
/// Update the keyboard state
|
||||
virtual void Poll() override;
|
||||
|
||||
/// Checks if a key is pressed or not
|
||||
virtual bool IsKeyPressed(EKey inKey) const override { return mKeyPressed[(int)inKey]; }
|
||||
|
||||
/// Buffered keyboard input, returns EKey::Invalid for none
|
||||
virtual EKey GetFirstKey() override;
|
||||
virtual EKey GetNextKey() override;
|
||||
|
||||
/// Handle a key press event
|
||||
void OnKeyPressed(EKey inKey, bool inPressed);
|
||||
|
||||
private:
|
||||
bool mKeyPressed[(int)EKey::NumKeys] = { };
|
||||
StaticArray<EKey, 128> mPendingKeyBuffer;
|
||||
StaticArray<EKey, 128> mKeyBuffer;
|
||||
uint mCurrentKey = 0;
|
||||
};
|
||||
143
lib/All/JoltPhysics/TestFramework/Input/MacOS/KeyboardMacOS.mm
Normal file
143
lib/All/JoltPhysics/TestFramework/Input/MacOS/KeyboardMacOS.mm
Normal file
@@ -0,0 +1,143 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Input/MacOS/KeyboardMacOS.h>
|
||||
#import <GameController/GameController.h>
|
||||
|
||||
static EKey sToKey(GCKeyCode inValue)
|
||||
{
|
||||
if (inValue == GCKeyCodeKeyA) return EKey::A;
|
||||
if (inValue == GCKeyCodeKeyB) return EKey::B;
|
||||
if (inValue == GCKeyCodeKeyC) return EKey::C;
|
||||
if (inValue == GCKeyCodeKeyD) return EKey::D;
|
||||
if (inValue == GCKeyCodeKeyE) return EKey::E;
|
||||
if (inValue == GCKeyCodeKeyF) return EKey::F;
|
||||
if (inValue == GCKeyCodeKeyG) return EKey::G;
|
||||
if (inValue == GCKeyCodeKeyH) return EKey::H;
|
||||
if (inValue == GCKeyCodeKeyI) return EKey::I;
|
||||
if (inValue == GCKeyCodeKeyJ) return EKey::J;
|
||||
if (inValue == GCKeyCodeKeyK) return EKey::K;
|
||||
if (inValue == GCKeyCodeKeyL) return EKey::L;
|
||||
if (inValue == GCKeyCodeKeyM) return EKey::M;
|
||||
if (inValue == GCKeyCodeKeyN) return EKey::N;
|
||||
if (inValue == GCKeyCodeKeyO) return EKey::O;
|
||||
if (inValue == GCKeyCodeKeyP) return EKey::P;
|
||||
if (inValue == GCKeyCodeKeyQ) return EKey::Q;
|
||||
if (inValue == GCKeyCodeKeyR) return EKey::R;
|
||||
if (inValue == GCKeyCodeKeyS) return EKey::S;
|
||||
if (inValue == GCKeyCodeKeyT) return EKey::T;
|
||||
if (inValue == GCKeyCodeKeyU) return EKey::U;
|
||||
if (inValue == GCKeyCodeKeyV) return EKey::V;
|
||||
if (inValue == GCKeyCodeKeyW) return EKey::W;
|
||||
if (inValue == GCKeyCodeKeyX) return EKey::X;
|
||||
if (inValue == GCKeyCodeKeyY) return EKey::Y;
|
||||
if (inValue == GCKeyCodeKeyZ) return EKey::Z;
|
||||
if (inValue == GCKeyCodeZero) return EKey::Num0;
|
||||
if (inValue == GCKeyCodeOne) return EKey::Num1;
|
||||
if (inValue == GCKeyCodeTwo) return EKey::Num2;
|
||||
if (inValue == GCKeyCodeThree) return EKey::Num3;
|
||||
if (inValue == GCKeyCodeFour) return EKey::Num4;
|
||||
if (inValue == GCKeyCodeFive) return EKey::Num5;
|
||||
if (inValue == GCKeyCodeSix) return EKey::Num6;
|
||||
if (inValue == GCKeyCodeSeven) return EKey::Num7;
|
||||
if (inValue == GCKeyCodeEight) return EKey::Num8;
|
||||
if (inValue == GCKeyCodeNine) return EKey::Num9;
|
||||
if (inValue == GCKeyCodeSpacebar) return EKey::Space;
|
||||
if (inValue == GCKeyCodeComma) return EKey::Comma;
|
||||
if (inValue == GCKeyCodePeriod) return EKey::Period;
|
||||
if (inValue == GCKeyCodeEscape) return EKey::Escape;
|
||||
if (inValue == GCKeyCodeLeftShift) return EKey::LShift;
|
||||
if (inValue == GCKeyCodeRightShift) return EKey::RShift;
|
||||
if (inValue == GCKeyCodeLeftControl) return EKey::LControl;
|
||||
if (inValue == GCKeyCodeRightControl) return EKey::RControl;
|
||||
if (inValue == GCKeyCodeLeftAlt) return EKey::LAlt;
|
||||
if (inValue == GCKeyCodeRightAlt) return EKey::RAlt;
|
||||
if (inValue == GCKeyCodeLeftArrow) return EKey::Left;
|
||||
if (inValue == GCKeyCodeRightArrow) return EKey::Right;
|
||||
if (inValue == GCKeyCodeUpArrow) return EKey::Up;
|
||||
if (inValue == GCKeyCodeDownArrow) return EKey::Down;
|
||||
if (inValue == GCKeyCodeReturnOrEnter) return EKey::Return;
|
||||
return EKey::Unknown;
|
||||
}
|
||||
|
||||
// This class receives keyboard connect callbacks
|
||||
@interface KeyboardDelegate : NSObject
|
||||
@end
|
||||
|
||||
@implementation KeyboardDelegate
|
||||
{
|
||||
KeyboardMacOS *mKeyboard;
|
||||
}
|
||||
|
||||
- (KeyboardDelegate *)init:(KeyboardMacOS *)Keyboard
|
||||
{
|
||||
mKeyboard = Keyboard;
|
||||
|
||||
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyDown handler:^NSEvent *(NSEvent *event) {
|
||||
// Ignore all keystrokes except Command-Q (Quit).
|
||||
if ((event.modifierFlags & NSEventModifierFlagCommand) && [event.charactersIgnoringModifiers isEqual:@"q"])
|
||||
return event;
|
||||
else
|
||||
return nil;
|
||||
}];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)keyboardDidConnect:(NSNotification *)notification
|
||||
{
|
||||
GCKeyboard *keyboard = (GCKeyboard *)notification.object;
|
||||
if (!keyboard)
|
||||
return;
|
||||
|
||||
__block KeyboardDelegate *weakSelf = self;
|
||||
keyboard.keyboardInput.keyChangedHandler = ^(GCKeyboardInput *keyboard, GCControllerButtonInput *key, GCKeyCode keyCode, BOOL pressed) {
|
||||
KeyboardDelegate *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
EKey ekey = sToKey(keyCode);
|
||||
if (ekey != EKey::Invalid)
|
||||
strongSelf->mKeyboard->OnKeyPressed(ekey, pressed);
|
||||
};
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
bool KeyboardMacOS::Initialize(ApplicationWindow *inWindow)
|
||||
{
|
||||
KeyboardDelegate *delegate = [[KeyboardDelegate alloc] init: this];
|
||||
[NSNotificationCenter.defaultCenter addObserver: delegate selector: @selector(keyboardDidConnect:) name: GCKeyboardDidConnectNotification object: nil];
|
||||
return true;
|
||||
}
|
||||
|
||||
void KeyboardMacOS::Poll()
|
||||
{
|
||||
// Make the pending buffer the active buffer
|
||||
mKeyBuffer = mPendingKeyBuffer;
|
||||
mPendingKeyBuffer.clear();
|
||||
}
|
||||
|
||||
EKey KeyboardMacOS::GetFirstKey()
|
||||
{
|
||||
mCurrentKey = 0;
|
||||
return GetNextKey();
|
||||
}
|
||||
|
||||
EKey KeyboardMacOS::GetNextKey()
|
||||
{
|
||||
if (mCurrentKey < mKeyBuffer.size())
|
||||
return mKeyBuffer[mCurrentKey++];
|
||||
return EKey::Invalid;
|
||||
}
|
||||
|
||||
void KeyboardMacOS::OnKeyPressed(EKey inKey, bool inPressed)
|
||||
{
|
||||
if (inPressed && mPendingKeyBuffer.size() < mPendingKeyBuffer.capacity())
|
||||
mPendingKeyBuffer.push_back(inKey);
|
||||
|
||||
mKeyPressed[(int)inKey] = inPressed;
|
||||
}
|
||||
54
lib/All/JoltPhysics/TestFramework/Input/MacOS/MouseMacOS.h
Normal file
54
lib/All/JoltPhysics/TestFramework/Input/MacOS/MouseMacOS.h
Normal file
@@ -0,0 +1,54 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Input/Mouse.h>
|
||||
|
||||
class ApplicationWindowMacOS;
|
||||
|
||||
/// Mouse interface class, keeps track of the mouse button state and of the absolute and relative movements of the mouse.
|
||||
class MouseMacOS : public Mouse
|
||||
{
|
||||
public:
|
||||
/// Initialization / shutdown
|
||||
virtual bool Initialize(ApplicationWindow *inWindow) override;
|
||||
virtual void Shutdown() override;
|
||||
|
||||
/// Update the mouse state
|
||||
virtual void Poll() override;
|
||||
|
||||
virtual int GetX() const override { return mX; }
|
||||
virtual int GetY() const override { return mY; }
|
||||
virtual int GetDX() const override { return mDeltaX; }
|
||||
virtual int GetDY() const override { return mDeltaY; }
|
||||
|
||||
virtual bool IsLeftPressed() const override { return mLeftPressed; }
|
||||
virtual bool IsRightPressed() const override { return mRightPressed; }
|
||||
virtual bool IsMiddlePressed() const override { return mMiddlePressed; }
|
||||
|
||||
virtual void HideCursor() override { }
|
||||
virtual void ShowCursor() override { }
|
||||
|
||||
/// Internal callbacks
|
||||
void OnMouseMoved(int inX, int inY) { mX = inX; mY = inY; }
|
||||
void OnMouseDelta(int inDX, int inDY) { mDeltaXAcc += inDX; mDeltaYAcc += inDY; }
|
||||
void SetLeftPressed(bool inPressed) { mLeftPressed = inPressed; }
|
||||
void SetRightPressed(bool inPressed) { mRightPressed = inPressed; }
|
||||
void SetMiddlePressed(bool inPressed) { mMiddlePressed = inPressed; }
|
||||
|
||||
private:
|
||||
ApplicationWindowMacOS * mWindow = nullptr;
|
||||
|
||||
int mX = 0;
|
||||
int mY = 0;
|
||||
int mDeltaX = 0;
|
||||
int mDeltaY = 0;
|
||||
int mDeltaXAcc = 0;
|
||||
int mDeltaYAcc = 0;
|
||||
|
||||
bool mLeftPressed = false;
|
||||
bool mRightPressed = false;
|
||||
bool mMiddlePressed = false;
|
||||
};
|
||||
103
lib/All/JoltPhysics/TestFramework/Input/MacOS/MouseMacOS.mm
Normal file
103
lib/All/JoltPhysics/TestFramework/Input/MacOS/MouseMacOS.mm
Normal file
@@ -0,0 +1,103 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Input/MacOS/MouseMacOS.h>
|
||||
#include <Window/ApplicationWindowMacOS.h>
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <GameController/GameController.h>
|
||||
|
||||
// This class receives mouse connect callbacks
|
||||
@interface MouseDelegate : NSObject
|
||||
@end
|
||||
|
||||
@implementation MouseDelegate
|
||||
{
|
||||
MouseMacOS *mMouse;
|
||||
}
|
||||
|
||||
- (MouseDelegate *)init:(MouseMacOS *)mouse
|
||||
{
|
||||
mMouse = mouse;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)mouseDidConnect:(NSNotification *)notification
|
||||
{
|
||||
GCMouse *mouse = (GCMouse *)notification.object;
|
||||
if (mouse == nil)
|
||||
return;
|
||||
|
||||
GCMouseInput *mouseInput = mouse.mouseInput;
|
||||
if (mouseInput == nil)
|
||||
return;
|
||||
|
||||
__block MouseDelegate *weakSelf = self;
|
||||
mouseInput.mouseMovedHandler = ^(GCMouseInput *mouse, float deltaX, float deltaY) {
|
||||
MouseDelegate *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf->mMouse->OnMouseDelta(deltaX, -deltaY);
|
||||
};
|
||||
|
||||
mouseInput.leftButton.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
MouseDelegate *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf->mMouse->SetLeftPressed(pressed);
|
||||
};
|
||||
|
||||
mouseInput.rightButton.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
MouseDelegate *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf->mMouse->SetRightPressed(pressed);
|
||||
};
|
||||
|
||||
mouseInput.middleButton.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
MouseDelegate *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf->mMouse->SetMiddlePressed(pressed);
|
||||
};
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
bool MouseMacOS::Initialize(ApplicationWindow *inWindow)
|
||||
{
|
||||
mWindow = static_cast<ApplicationWindowMacOS *>(inWindow);
|
||||
|
||||
// Install listener for mouse move callbacks
|
||||
mWindow->SetMouseMovedCallback([this](int inX, int inY) { OnMouseMoved(inX, inY); });
|
||||
|
||||
// Install listener for mouse delta callbacks (will work also when mouse is outside the window or at the edge of the screen)
|
||||
MouseDelegate *delegate = [[MouseDelegate alloc] init: this];
|
||||
[NSNotificationCenter.defaultCenter addObserver: delegate selector: @selector(mouseDidConnect:) name: GCMouseDidConnectNotification object:nil];
|
||||
return true;
|
||||
}
|
||||
|
||||
void MouseMacOS::Shutdown()
|
||||
{
|
||||
if (mWindow != nullptr)
|
||||
{
|
||||
mWindow->SetMouseMovedCallback({});
|
||||
mWindow = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void MouseMacOS::Poll()
|
||||
{
|
||||
mDeltaX = mDeltaXAcc;
|
||||
mDeltaY = mDeltaYAcc;
|
||||
|
||||
mDeltaXAcc = 0;
|
||||
mDeltaYAcc = 0;
|
||||
}
|
||||
35
lib/All/JoltPhysics/TestFramework/Input/Mouse.h
Normal file
35
lib/All/JoltPhysics/TestFramework/Input/Mouse.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
class ApplicationWindow;
|
||||
|
||||
/// Mouse interface class, keeps track of the mouse button state and of the absolute and relative movements of the mouse.
|
||||
class Mouse
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
Mouse() = default;
|
||||
virtual ~Mouse() = default;
|
||||
|
||||
/// Initialization / shutdown
|
||||
virtual bool Initialize(ApplicationWindow *inWindow) = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
/// Update the mouse state
|
||||
virtual void Poll() = 0;
|
||||
|
||||
virtual int GetX() const = 0;
|
||||
virtual int GetY() const = 0;
|
||||
virtual int GetDX() const = 0;
|
||||
virtual int GetDY() const = 0;
|
||||
|
||||
virtual bool IsLeftPressed() const = 0;
|
||||
virtual bool IsRightPressed() const = 0;
|
||||
virtual bool IsMiddlePressed() const = 0;
|
||||
|
||||
virtual void HideCursor() = 0;
|
||||
virtual void ShowCursor() = 0;
|
||||
};
|
||||
281
lib/All/JoltPhysics/TestFramework/Input/Win/KeyboardWin.cpp
Normal file
281
lib/All/JoltPhysics/TestFramework/Input/Win/KeyboardWin.cpp
Normal file
@@ -0,0 +1,281 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Input/Win/KeyboardWin.h>
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Window/ApplicationWindowWin.h>
|
||||
#include <Jolt/Core/Profiler.h>
|
||||
|
||||
KeyboardWin::KeyboardWin()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
KeyboardWin::~KeyboardWin()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void KeyboardWin::Reset()
|
||||
{
|
||||
mDI = nullptr;
|
||||
mKeyboard = nullptr;
|
||||
|
||||
ResetKeyboard();
|
||||
}
|
||||
|
||||
void KeyboardWin::ResetKeyboard()
|
||||
{
|
||||
memset(&mKeyPressed, 0, sizeof(mKeyPressed));
|
||||
memset(&mDOD, 0, sizeof(mDOD));
|
||||
mDODLength = 0;
|
||||
mCurrentPosition = 0;
|
||||
}
|
||||
|
||||
bool KeyboardWin::Initialize(ApplicationWindow *inWindow)
|
||||
#ifdef JPH_COMPILER_CLANG
|
||||
// DIPROP_BUFFERSIZE is a pointer to 1 which causes UBSan: runtime error: reference binding to misaligned address 0x000000000001
|
||||
__attribute__((no_sanitize("alignment")))
|
||||
#endif
|
||||
{
|
||||
// Create direct input interface
|
||||
if (FAILED(CoCreateInstance(CLSID_DirectInput8, nullptr, CLSCTX_INPROC_SERVER, IID_IDirectInput8W, (void **)&mDI)))
|
||||
{
|
||||
Trace("Unable to create DirectInput interface, DirectX 8.0 is required");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize direct input interface
|
||||
if (FAILED(mDI->Initialize((HINSTANCE)GetModuleHandle(nullptr), DIRECTINPUT_VERSION)))
|
||||
{
|
||||
Trace("Unable to initialize DirectInput interface, DirectX 8.0 is required");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create keyboard device
|
||||
if (FAILED(mDI->CreateDevice(GUID_SysKeyboard, &mKeyboard, nullptr)))
|
||||
{
|
||||
Trace("Unable to get DirectInputDevice interface, DirectX 8.0 is required");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set cooperative level for keyboard
|
||||
if (FAILED(mKeyboard->SetCooperativeLevel(static_cast<ApplicationWindowWin *>(inWindow)->GetWindowHandle(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND)))
|
||||
{
|
||||
Trace("Unable to set cooperative level for keyboard");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set data format
|
||||
if (FAILED(mKeyboard->SetDataFormat(&c_dfDIKeyboard)))
|
||||
{
|
||||
Trace("Unable to set data format to keyboard");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a keyboard buffer
|
||||
DIPROPDWORD dipdw;
|
||||
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
|
||||
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
||||
dipdw.diph.dwObj = 0;
|
||||
dipdw.diph.dwHow = DIPH_DEVICE;
|
||||
dipdw.dwData = BUFFERSIZE;
|
||||
if (FAILED(mKeyboard->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph)))
|
||||
{
|
||||
Trace("Unable to set keyboard buffer size");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void KeyboardWin::Shutdown()
|
||||
{
|
||||
if (mKeyboard)
|
||||
{
|
||||
mKeyboard->Unacquire();
|
||||
mKeyboard = nullptr;
|
||||
}
|
||||
|
||||
mDI = nullptr;
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
void KeyboardWin::Poll()
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
// Get the state of the keyboard
|
||||
if (FAILED(mKeyboard->GetDeviceState(sizeof(mKeyPressed), mKeyPressed)))
|
||||
{
|
||||
mKeyboard->Acquire();
|
||||
|
||||
if (FAILED(mKeyboard->GetDeviceState(sizeof(mKeyPressed), mKeyPressed)))
|
||||
{
|
||||
ResetKeyboard();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the state in a buffer
|
||||
mDODLength = BUFFERSIZE;
|
||||
mCurrentPosition = 0;
|
||||
if (FAILED(mKeyboard->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), mDOD, &mDODLength, 0)))
|
||||
{
|
||||
mKeyboard->Acquire();
|
||||
|
||||
if (FAILED(mKeyboard->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), mDOD, &mDODLength, 0)))
|
||||
{
|
||||
ResetKeyboard();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EKey KeyboardWin::GetFirstKey()
|
||||
{
|
||||
mCurrentPosition = 0;
|
||||
|
||||
return GetNextKey();
|
||||
}
|
||||
|
||||
EKey KeyboardWin::GetNextKey()
|
||||
{
|
||||
while (mCurrentPosition < mDODLength)
|
||||
{
|
||||
// Get next key
|
||||
const DIDEVICEOBJECTDATA ¤t = mDOD[mCurrentPosition];
|
||||
mCurrentPosition++;
|
||||
|
||||
// Return it
|
||||
if (current.dwData & 0x80)
|
||||
return ToKey(current.dwOfs);
|
||||
}
|
||||
|
||||
return EKey::Invalid;
|
||||
}
|
||||
|
||||
EKey KeyboardWin::ToKey(int inValue) const
|
||||
{
|
||||
switch (inValue)
|
||||
{
|
||||
case DIK_A: return EKey::A;
|
||||
case DIK_B: return EKey::B;
|
||||
case DIK_C: return EKey::C;
|
||||
case DIK_D: return EKey::D;
|
||||
case DIK_E: return EKey::E;
|
||||
case DIK_F: return EKey::F;
|
||||
case DIK_G: return EKey::G;
|
||||
case DIK_H: return EKey::H;
|
||||
case DIK_I: return EKey::I;
|
||||
case DIK_J: return EKey::J;
|
||||
case DIK_K: return EKey::K;
|
||||
case DIK_L: return EKey::L;
|
||||
case DIK_M: return EKey::M;
|
||||
case DIK_N: return EKey::N;
|
||||
case DIK_O: return EKey::O;
|
||||
case DIK_P: return EKey::P;
|
||||
case DIK_Q: return EKey::Q;
|
||||
case DIK_R: return EKey::R;
|
||||
case DIK_S: return EKey::S;
|
||||
case DIK_T: return EKey::T;
|
||||
case DIK_U: return EKey::U;
|
||||
case DIK_V: return EKey::V;
|
||||
case DIK_W: return EKey::W;
|
||||
case DIK_X: return EKey::X;
|
||||
case DIK_Y: return EKey::Y;
|
||||
case DIK_Z: return EKey::Z;
|
||||
case DIK_0: return EKey::Num0;
|
||||
case DIK_1: return EKey::Num1;
|
||||
case DIK_2: return EKey::Num2;
|
||||
case DIK_3: return EKey::Num3;
|
||||
case DIK_4: return EKey::Num4;
|
||||
case DIK_5: return EKey::Num5;
|
||||
case DIK_6: return EKey::Num6;
|
||||
case DIK_7: return EKey::Num7;
|
||||
case DIK_8: return EKey::Num8;
|
||||
case DIK_9: return EKey::Num9;
|
||||
case DIK_SPACE: return EKey::Space;
|
||||
case DIK_COMMA: return EKey::Comma;
|
||||
case DIK_PERIOD: return EKey::Period;
|
||||
case DIK_ESCAPE: return EKey::Escape;
|
||||
case DIK_LSHIFT: return EKey::LShift;
|
||||
case DIK_RSHIFT: return EKey::RShift;
|
||||
case DIK_LCONTROL: return EKey::LControl;
|
||||
case DIK_RCONTROL: return EKey::RControl;
|
||||
case DIK_LALT: return EKey::LAlt;
|
||||
case DIK_RALT: return EKey::RAlt;
|
||||
case DIK_LEFT: return EKey::Left;
|
||||
case DIK_RIGHT: return EKey::Right;
|
||||
case DIK_UP: return EKey::Up;
|
||||
case DIK_DOWN: return EKey::Down;
|
||||
case DIK_RETURN: return EKey::Return;
|
||||
default: return EKey::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
int KeyboardWin::FromKey(EKey inKey) const
|
||||
{
|
||||
switch (inKey)
|
||||
{
|
||||
case EKey::A: return DIK_A;
|
||||
case EKey::B: return DIK_B;
|
||||
case EKey::C: return DIK_C;
|
||||
case EKey::D: return DIK_D;
|
||||
case EKey::E: return DIK_E;
|
||||
case EKey::F: return DIK_F;
|
||||
case EKey::G: return DIK_G;
|
||||
case EKey::H: return DIK_H;
|
||||
case EKey::I: return DIK_I;
|
||||
case EKey::J: return DIK_J;
|
||||
case EKey::K: return DIK_K;
|
||||
case EKey::L: return DIK_L;
|
||||
case EKey::M: return DIK_M;
|
||||
case EKey::N: return DIK_N;
|
||||
case EKey::O: return DIK_O;
|
||||
case EKey::P: return DIK_P;
|
||||
case EKey::Q: return DIK_Q;
|
||||
case EKey::R: return DIK_R;
|
||||
case EKey::S: return DIK_S;
|
||||
case EKey::T: return DIK_T;
|
||||
case EKey::U: return DIK_U;
|
||||
case EKey::V: return DIK_V;
|
||||
case EKey::W: return DIK_W;
|
||||
case EKey::X: return DIK_X;
|
||||
case EKey::Y: return DIK_Y;
|
||||
case EKey::Z: return DIK_Z;
|
||||
case EKey::Num0: return DIK_0;
|
||||
case EKey::Num1: return DIK_1;
|
||||
case EKey::Num2: return DIK_2;
|
||||
case EKey::Num3: return DIK_3;
|
||||
case EKey::Num4: return DIK_4;
|
||||
case EKey::Num5: return DIK_5;
|
||||
case EKey::Num6: return DIK_6;
|
||||
case EKey::Num7: return DIK_7;
|
||||
case EKey::Num8: return DIK_8;
|
||||
case EKey::Num9: return DIK_9;
|
||||
case EKey::Space: return DIK_SPACE;
|
||||
case EKey::Comma: return DIK_COMMA;
|
||||
case EKey::Period: return DIK_PERIOD;
|
||||
case EKey::Escape: return DIK_ESCAPE;
|
||||
case EKey::LShift: return DIK_LSHIFT;
|
||||
case EKey::RShift: return DIK_RSHIFT;
|
||||
case EKey::LControl: return DIK_LCONTROL;
|
||||
case EKey::RControl: return DIK_RCONTROL;
|
||||
case EKey::LAlt: return DIK_LALT;
|
||||
case EKey::RAlt: return DIK_RALT;
|
||||
case EKey::Left: return DIK_LEFT;
|
||||
case EKey::Right: return DIK_RIGHT;
|
||||
case EKey::Up: return DIK_UP;
|
||||
case EKey::Down: return DIK_DOWN;
|
||||
case EKey::Return: return DIK_RETURN;
|
||||
case EKey::Invalid:
|
||||
case EKey::Unknown:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
53
lib/All/JoltPhysics/TestFramework/Input/Win/KeyboardWin.h
Normal file
53
lib/All/JoltPhysics/TestFramework/Input/Win/KeyboardWin.h
Normal file
@@ -0,0 +1,53 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Input/Keyboard.h>
|
||||
|
||||
// We're using DX8's DirectInput API
|
||||
#define DIRECTINPUT_VERSION 0x0800
|
||||
#include <dinput.h>
|
||||
|
||||
/// Keyboard interface class which keeps track on the status of all keys and keeps track of the list of keys pressed.
|
||||
class KeyboardWin : public Keyboard
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
KeyboardWin();
|
||||
virtual ~KeyboardWin() override;
|
||||
|
||||
/// Initialization / shutdown
|
||||
virtual bool Initialize(ApplicationWindow *inWindow) override;
|
||||
virtual void Shutdown() override;
|
||||
|
||||
/// Update the keyboard state
|
||||
virtual void Poll() override;
|
||||
|
||||
/// Checks if a key is pressed or not
|
||||
virtual bool IsKeyPressed(EKey inKey) const override { return mKeyPressed[FromKey(inKey)] != 0; }
|
||||
|
||||
/// Buffered keyboard input, returns EKey::Invalid for none
|
||||
virtual EKey GetFirstKey() override;
|
||||
virtual EKey GetNextKey() override;
|
||||
|
||||
private:
|
||||
void Reset();
|
||||
void ResetKeyboard();
|
||||
EKey ToKey(int inKey) const;
|
||||
int FromKey(EKey inKey) const;
|
||||
|
||||
enum
|
||||
{
|
||||
BUFFERSIZE = 64, ///< Number of keys cached
|
||||
};
|
||||
|
||||
// DirectInput part
|
||||
ComPtr<IDirectInput8> mDI;
|
||||
ComPtr<IDirectInputDevice8> mKeyboard;
|
||||
char mKeyPressed[256];
|
||||
DIDEVICEOBJECTDATA mDOD[BUFFERSIZE];
|
||||
DWORD mDODLength;
|
||||
DWORD mCurrentPosition;
|
||||
};
|
||||
189
lib/All/JoltPhysics/TestFramework/Input/Win/MouseWin.cpp
Normal file
189
lib/All/JoltPhysics/TestFramework/Input/Win/MouseWin.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Input/Win/MouseWin.h>
|
||||
#include <Window/ApplicationWindowWin.h>
|
||||
#include <Jolt/Core/Profiler.h>
|
||||
|
||||
MouseWin::MouseWin()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
MouseWin::~MouseWin()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void MouseWin::Reset()
|
||||
{
|
||||
mDI = nullptr;
|
||||
mMouse = nullptr;
|
||||
mMousePos.x = 0;
|
||||
mMousePos.y = 0;
|
||||
|
||||
ResetMouse();
|
||||
}
|
||||
|
||||
void MouseWin::ResetMouse()
|
||||
{
|
||||
memset(&mMouseState, 0, sizeof(mMouseState));
|
||||
mMousePosInitialized = false;
|
||||
}
|
||||
|
||||
void MouseWin::DetectParsecRunning()
|
||||
{
|
||||
mIsParsecRunning = false;
|
||||
|
||||
if (SC_HANDLE manager = OpenSCManager(nullptr, nullptr, SC_MANAGER_CONNECT))
|
||||
{
|
||||
if (SC_HANDLE service = OpenServiceA(manager, "Parsec", SERVICE_QUERY_STATUS))
|
||||
{
|
||||
SERVICE_STATUS status;
|
||||
if (QueryServiceStatus(service, &status))
|
||||
{
|
||||
mIsParsecRunning = status.dwCurrentState == SERVICE_RUNNING;
|
||||
}
|
||||
CloseServiceHandle(service);
|
||||
}
|
||||
CloseServiceHandle(manager);
|
||||
}
|
||||
}
|
||||
|
||||
bool MouseWin::Initialize(ApplicationWindow *inWindow)
|
||||
#ifdef JPH_COMPILER_CLANG
|
||||
// DIPROP_BUFFERSIZE is a pointer to 1 which causes UBSan: runtime error: reference binding to misaligned address 0x000000000001
|
||||
__attribute__((no_sanitize("alignment")))
|
||||
#endif
|
||||
{
|
||||
// Store window
|
||||
mWindow = static_cast<ApplicationWindowWin *>(inWindow);
|
||||
|
||||
// Create direct input interface
|
||||
if (FAILED(CoCreateInstance(CLSID_DirectInput8, nullptr, CLSCTX_INPROC_SERVER, IID_IDirectInput8W, (void **)&mDI)))
|
||||
{
|
||||
Trace("Unable to create DirectInput interface, DirectX 8.0 is required");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize direct input interface
|
||||
if (FAILED(mDI->Initialize((HINSTANCE)GetModuleHandle(nullptr), DIRECTINPUT_VERSION)))
|
||||
{
|
||||
Trace("Unable to initialize DirectInput interface, DirectX 8.0 is required");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create Mouse device
|
||||
if (FAILED(mDI->CreateDevice(GUID_SysMouse, &mMouse, nullptr)))
|
||||
{
|
||||
Trace("Unable to get DirectInputDevice interface, DirectX 8.0 is required");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set cooperative level for Mouse
|
||||
if (FAILED(mMouse->SetCooperativeLevel(mWindow->GetWindowHandle(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND)))
|
||||
Trace("Failed to set cooperative level for mouse");
|
||||
|
||||
// Set data format
|
||||
if (FAILED(mMouse->SetDataFormat(&c_dfDIMouse)))
|
||||
{
|
||||
Trace("Unable to set data format to mouse");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a mouse buffer
|
||||
DIPROPDWORD dipdw;
|
||||
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
|
||||
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
||||
dipdw.diph.dwObj = 0;
|
||||
dipdw.diph.dwHow = DIPH_DEVICE;
|
||||
dipdw.dwData = BUFFERSIZE;
|
||||
if (FAILED(mMouse->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph)))
|
||||
{
|
||||
Trace("Unable to set mouse buffer size");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the parsec service is running
|
||||
DetectParsecRunning();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MouseWin::Shutdown()
|
||||
{
|
||||
if (mMouse)
|
||||
{
|
||||
mMouse->Unacquire();
|
||||
mMouse = nullptr;
|
||||
}
|
||||
|
||||
mDI = nullptr;
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
void MouseWin::Poll()
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
// Remember last position
|
||||
POINT old_mouse_pos = mMousePos;
|
||||
|
||||
// Get mouse position using the standard window call
|
||||
if (!GetCursorPos(&mMousePos))
|
||||
{
|
||||
ResetMouse();
|
||||
return;
|
||||
}
|
||||
|
||||
// If we lost mouse before, we need to reset the old mouse pos to the current one
|
||||
if (!mMousePosInitialized)
|
||||
{
|
||||
old_mouse_pos = mMousePos;
|
||||
mMousePosInitialized = true;
|
||||
}
|
||||
|
||||
// Convert to window space
|
||||
if (!ScreenToClient(mWindow->GetWindowHandle(), &mMousePos))
|
||||
{
|
||||
ResetMouse();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get relative movement
|
||||
if (FAILED(mMouse->GetDeviceState(sizeof(mMouseState), &mMouseState)))
|
||||
{
|
||||
// Mouse input was lost, reacquire
|
||||
mMouse->Acquire();
|
||||
|
||||
if (FAILED(mMouse->GetDeviceState(sizeof(mMouseState), &mMouseState)))
|
||||
{
|
||||
ResetMouse();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're connected through remote desktop or Parsec then GetDeviceState returns faulty data for lX and lY so we need to use a fallback
|
||||
if (GetSystemMetrics(SM_REMOTESESSION) || mIsParsecRunning)
|
||||
{
|
||||
// Just use the delta between the current and last mouse position.
|
||||
// Note that this has the disadvantage that you can no longer rotate any further if you're at the edge of the screen,
|
||||
// but unfortunately a RDP session doesn't allow capturing the mouse so there doesn't seem to be a workaround for this.
|
||||
mMouseState.lX = mMousePos.x - old_mouse_pos.x;
|
||||
mMouseState.lY = mMousePos.y - old_mouse_pos.y;
|
||||
}
|
||||
}
|
||||
|
||||
void MouseWin::HideCursor()
|
||||
{
|
||||
::ShowCursor(false);
|
||||
}
|
||||
|
||||
void MouseWin::ShowCursor()
|
||||
{
|
||||
::ShowCursor(true);
|
||||
}
|
||||
59
lib/All/JoltPhysics/TestFramework/Input/Win/MouseWin.h
Normal file
59
lib/All/JoltPhysics/TestFramework/Input/Win/MouseWin.h
Normal file
@@ -0,0 +1,59 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Input/Mouse.h>
|
||||
|
||||
// We're using DX8's DirectInput API
|
||||
#define DIRECTINPUT_VERSION 0x0800
|
||||
#include <dinput.h>
|
||||
|
||||
class ApplicationWindowWin;
|
||||
|
||||
/// Mouse interface class, keeps track of the mouse button state and of the absolute and relative movements of the mouse.
|
||||
class MouseWin : public Mouse
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
MouseWin();
|
||||
virtual ~MouseWin() override;
|
||||
|
||||
/// Initialization / shutdown
|
||||
virtual bool Initialize(ApplicationWindow *inWindow) override;
|
||||
virtual void Shutdown() override;
|
||||
|
||||
/// Update the mouse state
|
||||
virtual void Poll() override;
|
||||
|
||||
virtual int GetX() const override { return mMousePos.x; }
|
||||
virtual int GetY() const override { return mMousePos.y; }
|
||||
virtual int GetDX() const override { return mMouseState.lX; }
|
||||
virtual int GetDY() const override { return mMouseState.lY; }
|
||||
|
||||
virtual bool IsLeftPressed() const override { return (mMouseState.rgbButtons[0] & 0x80) != 0; }
|
||||
virtual bool IsRightPressed() const override { return (mMouseState.rgbButtons[1] & 0x80) != 0; }
|
||||
virtual bool IsMiddlePressed() const override { return (mMouseState.rgbButtons[2] & 0x80) != 0; }
|
||||
|
||||
virtual void HideCursor() override;
|
||||
virtual void ShowCursor() override;
|
||||
|
||||
private:
|
||||
void DetectParsecRunning();
|
||||
void Reset();
|
||||
void ResetMouse();
|
||||
|
||||
enum
|
||||
{
|
||||
BUFFERSIZE = 64, ///< Number of keys cached
|
||||
};
|
||||
|
||||
ApplicationWindowWin * mWindow;
|
||||
ComPtr<IDirectInput8> mDI;
|
||||
ComPtr<IDirectInputDevice8> mMouse;
|
||||
bool mIsParsecRunning; ///< If the Parsec remote desktop solution is running, if so we can't trust the mouse movement information from DX and it will make the mouse too sensitive
|
||||
DIMOUSESTATE mMouseState;
|
||||
bool mMousePosInitialized = false;
|
||||
POINT mMousePos;
|
||||
};
|
||||
@@ -0,0 +1,117 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/DX12/FatalErrorIfFailedDX12.h>
|
||||
|
||||
/// Holds a number of DirectX operations with logic to wait for completion
|
||||
class CommandQueueDX12
|
||||
{
|
||||
public:
|
||||
/// Destructor
|
||||
~CommandQueueDX12()
|
||||
{
|
||||
WaitUntilFinished();
|
||||
|
||||
if (mFenceEvent != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(mFenceEvent);
|
||||
}
|
||||
|
||||
/// Initialize the queue
|
||||
void Initialize(ID3D12Device *inDevice)
|
||||
{
|
||||
D3D12_COMMAND_QUEUE_DESC queue_desc = {};
|
||||
queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
||||
queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
||||
FatalErrorIfFailed(inDevice->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&mCommandQueue)));
|
||||
|
||||
FatalErrorIfFailed(inDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&mCommandAllocator)));
|
||||
|
||||
// Create the command list
|
||||
FatalErrorIfFailed(inDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, mCommandAllocator.Get(), nullptr, IID_PPV_ARGS(&mCommandList)));
|
||||
|
||||
// Command lists are created in the recording state, but there is nothing to record yet. The main loop expects it to be closed, so close it now
|
||||
FatalErrorIfFailed(mCommandList->Close());
|
||||
|
||||
// Create synchronization object
|
||||
FatalErrorIfFailed(inDevice->CreateFence(mFenceValue, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence)));
|
||||
|
||||
// Increment fence value so we don't skip waiting the first time a command list is executed
|
||||
mFenceValue++;
|
||||
|
||||
// Create an event handle to use for frame synchronization
|
||||
mFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
if (mFenceEvent == nullptr)
|
||||
FatalErrorIfFailed(HRESULT_FROM_WIN32(GetLastError()));
|
||||
}
|
||||
|
||||
/// Start the command list (requires waiting until the previous one is finished)
|
||||
ID3D12GraphicsCommandList * Start()
|
||||
{
|
||||
// Reset the allocator
|
||||
FatalErrorIfFailed(mCommandAllocator->Reset());
|
||||
|
||||
// Reset the command list
|
||||
FatalErrorIfFailed(mCommandList->Reset(mCommandAllocator.Get(), nullptr));
|
||||
|
||||
return mCommandList.Get();
|
||||
}
|
||||
|
||||
/// Execute accumulated command list
|
||||
void Execute()
|
||||
{
|
||||
JPH_ASSERT(!mIsExecuting);
|
||||
|
||||
// Close the command list
|
||||
FatalErrorIfFailed(mCommandList->Close());
|
||||
|
||||
// Execute the command list
|
||||
ID3D12CommandList* ppCommandLists[] = { mCommandList.Get() };
|
||||
mCommandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
|
||||
|
||||
// Schedule a Signal command in the queue
|
||||
FatalErrorIfFailed(mCommandQueue->Signal(mFence.Get(), mFenceValue));
|
||||
|
||||
// Mark that we're executing
|
||||
mIsExecuting = true;
|
||||
}
|
||||
|
||||
/// After executing, this waits until execution is done
|
||||
void WaitUntilFinished()
|
||||
{
|
||||
// Check if we've been started
|
||||
if (mIsExecuting)
|
||||
{
|
||||
if (mFence->GetCompletedValue() < mFenceValue)
|
||||
{
|
||||
// Wait until the fence has been processed
|
||||
FatalErrorIfFailed(mFence->SetEventOnCompletion(mFenceValue, mFenceEvent));
|
||||
WaitForSingleObjectEx(mFenceEvent, INFINITE, FALSE);
|
||||
}
|
||||
|
||||
// Increment the fence value
|
||||
mFenceValue++;
|
||||
|
||||
// Done executing
|
||||
mIsExecuting = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute and wait for the command list to finish
|
||||
void ExecuteAndWait()
|
||||
{
|
||||
Execute();
|
||||
WaitUntilFinished();
|
||||
}
|
||||
|
||||
private:
|
||||
ComPtr<ID3D12CommandQueue> mCommandQueue; ///< The command queue that will hold command lists
|
||||
ComPtr<ID3D12CommandAllocator> mCommandAllocator; ///< Allocator that holds the memory for the commands
|
||||
ComPtr<ID3D12GraphicsCommandList> mCommandList; ///< The command list that will hold the render commands / state changes
|
||||
HANDLE mFenceEvent = INVALID_HANDLE_VALUE; ///< Fence event, used to wait for rendering to complete
|
||||
ComPtr<ID3D12Fence> mFence; ///< Fence object, used to signal the fence event
|
||||
UINT64 mFenceValue = 0; ///< Current fence value, each time we need to wait we will signal the fence with this value, wait for it and then increase the value
|
||||
bool mIsExecuting = false; ///< If a commandlist is currently executing on the queue
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/DX12/ConstantBufferDX12.h>
|
||||
#include <Renderer/DX12/RendererDX12.h>
|
||||
#include <Renderer/DX12/FatalErrorIfFailedDX12.h>
|
||||
|
||||
ConstantBufferDX12::ConstantBufferDX12(RendererDX12 *inRenderer, uint64 inBufferSize) :
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
mBuffer = mRenderer->CreateD3DResourceOnUploadHeap(inBufferSize);
|
||||
mBufferSize = inBufferSize;
|
||||
}
|
||||
|
||||
ConstantBufferDX12::~ConstantBufferDX12()
|
||||
{
|
||||
if (mBuffer != nullptr)
|
||||
mRenderer->RecycleD3DResourceOnUploadHeap(mBuffer.Get(), mBufferSize);
|
||||
}
|
||||
|
||||
void *ConstantBufferDX12::MapInternal()
|
||||
{
|
||||
void *mapped_resource;
|
||||
D3D12_RANGE range = { 0, 0 }; // We're not going to read
|
||||
FatalErrorIfFailed(mBuffer->Map(0, &range, &mapped_resource));
|
||||
return mapped_resource;
|
||||
}
|
||||
|
||||
void ConstantBufferDX12::Unmap()
|
||||
{
|
||||
mBuffer->Unmap(0, nullptr);
|
||||
}
|
||||
|
||||
void ConstantBufferDX12::Bind(int inSlot)
|
||||
{
|
||||
mRenderer->GetCommandList()->SetGraphicsRootConstantBufferView(inSlot, mBuffer->GetGPUVirtualAddress());
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
class RendererDX12;
|
||||
|
||||
/// A binary blob that can be used to pass constants to a shader
|
||||
class ConstantBufferDX12
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
ConstantBufferDX12(RendererDX12 *inRenderer, uint64 inBufferSize);
|
||||
~ConstantBufferDX12();
|
||||
|
||||
/// Map / unmap buffer (get pointer to data). This will discard all data in the buffer.
|
||||
template <typename T> T * Map() { return reinterpret_cast<T *>(MapInternal()); }
|
||||
void Unmap();
|
||||
|
||||
// Bind the constant buffer to a slot
|
||||
void Bind(int inSlot);
|
||||
|
||||
private:
|
||||
friend class RendererDX12;
|
||||
|
||||
void * MapInternal();
|
||||
|
||||
RendererDX12 * mRenderer;
|
||||
ComPtr<ID3D12Resource> mBuffer;
|
||||
uint64 mBufferSize;
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
/// DirectX descriptor heap, used to allocate handles for resources to bind them to shaders
|
||||
class DescriptorHeapDX12
|
||||
{
|
||||
public:
|
||||
/// Initialize the heap
|
||||
/// @param inDevice The DirectX device
|
||||
/// @param inType Type of heap
|
||||
/// @param inFlags Flags for the heap
|
||||
/// @param inNumber Number of handles to reserve
|
||||
void Init(ID3D12Device *inDevice, D3D12_DESCRIPTOR_HEAP_TYPE inType, D3D12_DESCRIPTOR_HEAP_FLAGS inFlags, uint inNumber)
|
||||
{
|
||||
// Create the heap
|
||||
D3D12_DESCRIPTOR_HEAP_DESC heap_desc = {};
|
||||
heap_desc.NumDescriptors = inNumber;
|
||||
heap_desc.Type = inType;
|
||||
heap_desc.Flags = inFlags;
|
||||
FatalErrorIfFailed(inDevice->CreateDescriptorHeap(&heap_desc, IID_PPV_ARGS(&mHeap)));
|
||||
|
||||
// Delta between descriptor elements
|
||||
mDescriptorSize = inDevice->GetDescriptorHandleIncrementSize(heap_desc.Type);
|
||||
|
||||
// Delta between the CPU and GPU heap
|
||||
if (inFlags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)
|
||||
mGPUOffset = mHeap->GetGPUDescriptorHandleForHeapStart().ptr - mHeap->GetCPUDescriptorHandleForHeapStart().ptr;
|
||||
|
||||
// Populate the freelist
|
||||
mFreeList.reserve(inNumber);
|
||||
for (uint i = 0; i < inNumber; ++i)
|
||||
mFreeList.push_back(i);
|
||||
}
|
||||
|
||||
/// Allocate and return a new handle
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE Allocate()
|
||||
{
|
||||
JPH_ASSERT(!mFreeList.empty());
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE handle = mHeap->GetCPUDescriptorHandleForHeapStart();
|
||||
|
||||
uint index = mFreeList.back();
|
||||
mFreeList.pop_back();
|
||||
|
||||
handle.ptr += index * mDescriptorSize;
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// Free a handle and return it to the freelist
|
||||
void Free(D3D12_CPU_DESCRIPTOR_HANDLE inHandle)
|
||||
{
|
||||
uint index = uint((inHandle.ptr - mHeap->GetCPUDescriptorHandleForHeapStart().ptr) / mDescriptorSize);
|
||||
|
||||
mFreeList.push_back(index);
|
||||
}
|
||||
|
||||
/// Convert from a CPU to a GPU handle
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE ConvertToGPUHandle(D3D12_CPU_DESCRIPTOR_HANDLE inHandle)
|
||||
{
|
||||
JPH_ASSERT(mGPUOffset != -1);
|
||||
return { UINT64(inHandle.ptr) + mGPUOffset };
|
||||
}
|
||||
|
||||
/// Access to the underlying DirectX structure
|
||||
ID3D12DescriptorHeap * Get()
|
||||
{
|
||||
return mHeap.Get();
|
||||
}
|
||||
|
||||
private:
|
||||
ComPtr<ID3D12DescriptorHeap> mHeap;
|
||||
uint mDescriptorSize; ///< The size (in bytes) of a single heap descriptor
|
||||
Array<uint> mFreeList; ///< List of indices in the heap that are still free
|
||||
INT64 mGPUOffset = -1; ///< Offset between CPU and GPU handles
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <system_error>
|
||||
|
||||
#include <Renderer/DX12/FatalErrorIfFailedDX12.h>
|
||||
#include <Jolt/Core/StringTools.h>
|
||||
#include <Utils/Log.h>
|
||||
|
||||
void FatalErrorIfFailed(HRESULT inHResult)
|
||||
{
|
||||
if (FAILED(inHResult))
|
||||
{
|
||||
string message = system_category().message(inHResult);
|
||||
FatalError("DirectX error returned: %s (%s)", ConvertToString(inHResult).c_str(), message.c_str());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
/// Convert DirectX error to readable text and alert
|
||||
void FatalErrorIfFailed(HRESULT inHResult);
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/DX12/PipelineStateDX12.h>
|
||||
#include <Renderer/DX12/RendererDX12.h>
|
||||
#include <Renderer/DX12/VertexShaderDX12.h>
|
||||
#include <Renderer/DX12/PixelShaderDX12.h>
|
||||
#include <Renderer/DX12/FatalErrorIfFailedDX12.h>
|
||||
|
||||
PipelineStateDX12::PipelineStateDX12(RendererDX12 *inRenderer, const VertexShaderDX12 *inVertexShader, const EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShaderDX12 *inPixelShader, EDrawPass inDrawPass, EFillMode inFillMode, ETopology inTopology, EDepthTest inDepthTest, EBlendMode inBlendMode, ECullMode inCullMode) :
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
D3D12_PRIMITIVE_TOPOLOGY_TYPE topology = inTopology == ETopology::Triangle? D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE : D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE;
|
||||
|
||||
Array<D3D12_INPUT_ELEMENT_DESC> input_description;
|
||||
uint vertex_offset = 0, instance_offset = 0;
|
||||
for (uint i = 0; i < inInputDescriptionCount; ++i)
|
||||
switch (inInputDescription[i])
|
||||
{
|
||||
case EInputDescription::Position:
|
||||
input_description.push_back({ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, vertex_offset, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 });
|
||||
vertex_offset += 3 * sizeof(float);
|
||||
break;
|
||||
|
||||
case EInputDescription::Color:
|
||||
input_description.push_back({ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, vertex_offset, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 });
|
||||
vertex_offset += 4 * sizeof(uint8);
|
||||
break;
|
||||
|
||||
case EInputDescription::Normal:
|
||||
input_description.push_back({ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, vertex_offset, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 });
|
||||
vertex_offset += 3 * sizeof(float);
|
||||
break;
|
||||
|
||||
case EInputDescription::TexCoord:
|
||||
input_description.push_back({ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, vertex_offset, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 });
|
||||
vertex_offset += 2 * sizeof(float);
|
||||
break;
|
||||
|
||||
case EInputDescription::InstanceColor:
|
||||
input_description.push_back({ "INSTANCE_COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 1, instance_offset, D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1 });
|
||||
instance_offset += 4 * sizeof(uint8);
|
||||
break;
|
||||
|
||||
case EInputDescription::InstanceTransform:
|
||||
{
|
||||
for (uint j = 0; j < 4; ++j)
|
||||
{
|
||||
input_description.push_back({ "INSTANCE_TRANSFORM", j, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, instance_offset, D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1 });
|
||||
instance_offset += 4 * sizeof(float);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case EInputDescription::InstanceInvTransform:
|
||||
{
|
||||
for (uint j = 0; j < 4; ++j)
|
||||
{
|
||||
input_description.push_back({ "INSTANCE_INV_TRANSFORM", j, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, instance_offset, D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1 });
|
||||
instance_offset += 4 * sizeof(float);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc = {};
|
||||
pso_desc.InputLayout = { input_description.data(), (UINT)input_description.size() };
|
||||
pso_desc.pRootSignature = mRenderer->GetRootSignature();
|
||||
pso_desc.VS = { inVertexShader->mShader->GetBufferPointer(), inVertexShader->mShader->GetBufferSize() };
|
||||
pso_desc.PS = { inPixelShader->mShader->GetBufferPointer(), inPixelShader->mShader->GetBufferSize() };
|
||||
|
||||
pso_desc.RasterizerState.FillMode = inFillMode == EFillMode::Solid? D3D12_FILL_MODE_SOLID : D3D12_FILL_MODE_WIREFRAME;
|
||||
pso_desc.RasterizerState.CullMode = inCullMode == ECullMode::Backface? D3D12_CULL_MODE_FRONT : D3D12_CULL_MODE_BACK; // DX uses left handed system so we reverse the options
|
||||
pso_desc.RasterizerState.FrontCounterClockwise = FALSE;
|
||||
pso_desc.RasterizerState.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
|
||||
pso_desc.RasterizerState.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
|
||||
pso_desc.RasterizerState.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
|
||||
pso_desc.RasterizerState.DepthClipEnable = TRUE;
|
||||
pso_desc.RasterizerState.MultisampleEnable = FALSE;
|
||||
pso_desc.RasterizerState.AntialiasedLineEnable = FALSE;
|
||||
pso_desc.RasterizerState.ForcedSampleCount = 0;
|
||||
pso_desc.RasterizerState.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
|
||||
|
||||
pso_desc.BlendState.AlphaToCoverageEnable = FALSE;
|
||||
pso_desc.BlendState.IndependentBlendEnable = FALSE;
|
||||
|
||||
D3D12_RENDER_TARGET_BLEND_DESC &blend_desc = pso_desc.BlendState.RenderTarget[0];
|
||||
blend_desc.LogicOpEnable = FALSE;
|
||||
blend_desc.LogicOp = D3D12_LOGIC_OP_NOOP;
|
||||
blend_desc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
|
||||
switch (inBlendMode)
|
||||
{
|
||||
case EBlendMode::Write:
|
||||
blend_desc.BlendEnable = FALSE;
|
||||
break;
|
||||
|
||||
case EBlendMode::AlphaBlend:
|
||||
blend_desc.BlendEnable = TRUE;
|
||||
blend_desc.SrcBlend = D3D12_BLEND_SRC_ALPHA;
|
||||
blend_desc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
|
||||
blend_desc.BlendOp = D3D12_BLEND_OP_ADD;
|
||||
blend_desc.SrcBlendAlpha = D3D12_BLEND_ZERO;
|
||||
blend_desc.DestBlendAlpha = D3D12_BLEND_ZERO;
|
||||
blend_desc.BlendOpAlpha = D3D12_BLEND_OP_ADD;
|
||||
break;
|
||||
}
|
||||
|
||||
pso_desc.DepthStencilState.DepthEnable = inDepthTest == EDepthTest::On? TRUE : FALSE;
|
||||
pso_desc.DepthStencilState.DepthWriteMask = inDepthTest == EDepthTest::On? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO;
|
||||
pso_desc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_GREATER_EQUAL;
|
||||
pso_desc.DepthStencilState.StencilEnable = FALSE;
|
||||
|
||||
pso_desc.SampleMask = UINT_MAX;
|
||||
pso_desc.PrimitiveTopologyType = topology;
|
||||
pso_desc.NumRenderTargets = 1;
|
||||
pso_desc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
pso_desc.DSVFormat = DXGI_FORMAT_D32_FLOAT;
|
||||
pso_desc.SampleDesc.Count = 1;
|
||||
|
||||
FatalErrorIfFailed(mRenderer->GetDevice()->CreateGraphicsPipelineState(&pso_desc, IID_PPV_ARGS(&mPSO)));
|
||||
}
|
||||
|
||||
PipelineStateDX12::~PipelineStateDX12()
|
||||
{
|
||||
if (mPSO != nullptr)
|
||||
mRenderer->RecycleD3DObject(mPSO.Get());
|
||||
}
|
||||
|
||||
void PipelineStateDX12::Activate()
|
||||
{
|
||||
mRenderer->GetCommandList()->SetPipelineState(mPSO.Get());
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/PipelineState.h>
|
||||
|
||||
class RendererDX12;
|
||||
class VertexShaderDX12;
|
||||
class PixelShaderDX12;
|
||||
|
||||
/// DirectX 12 pipeline state object
|
||||
class PipelineStateDX12 : public PipelineState
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
PipelineStateDX12(RendererDX12 *inRenderer, const VertexShaderDX12 *inVertexShader, const EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShaderDX12 *inPixelShader, EDrawPass inDrawPass, EFillMode inFillMode, ETopology inTopology, EDepthTest inDepthTest, EBlendMode inBlendMode, ECullMode inCullMode);
|
||||
virtual ~PipelineStateDX12() override;
|
||||
|
||||
/// Make this pipeline state active (any primitives rendered after this will use this state)
|
||||
virtual void Activate() override;
|
||||
|
||||
private:
|
||||
RendererDX12 * mRenderer;
|
||||
ComPtr<ID3D12PipelineState> mPSO;
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/PixelShader.h>
|
||||
|
||||
/// Pixel shader handle for DirectX
|
||||
class PixelShaderDX12 : public PixelShader
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
PixelShaderDX12(ComPtr<ID3DBlob> inShader) : mShader(inShader) { }
|
||||
|
||||
ComPtr<ID3DBlob> mShader; ///< The compiled shader
|
||||
};
|
||||
@@ -0,0 +1,97 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/DX12/RenderInstancesDX12.h>
|
||||
#include <Renderer/DX12/RenderPrimitiveDX12.h>
|
||||
#include <Renderer/DX12/FatalErrorIfFailedDX12.h>
|
||||
|
||||
void RenderInstancesDX12::Clear()
|
||||
{
|
||||
if (mInstanceBuffer != nullptr)
|
||||
mRenderer->RecycleD3DResourceOnUploadHeap(mInstanceBuffer.Get(), mInstanceBufferSize);
|
||||
|
||||
mInstanceBuffer = nullptr;
|
||||
mInstanceBufferSize = 0;
|
||||
mInstanceSize = 0;
|
||||
}
|
||||
|
||||
void RenderInstancesDX12::CreateBuffer(int inNumInstances, int inInstanceSize)
|
||||
{
|
||||
uint new_size = uint(inNumInstances) * inInstanceSize;
|
||||
if (mInstanceBuffer == nullptr || mInstanceBufferSize < new_size)
|
||||
{
|
||||
// Delete the old buffer
|
||||
Clear();
|
||||
|
||||
// Calculate size
|
||||
mInstanceBufferSize = new_size;
|
||||
|
||||
// Create buffer
|
||||
mInstanceBuffer = mRenderer->CreateD3DResourceOnUploadHeap(mInstanceBufferSize);
|
||||
JPH_IF_DEBUG(mInstanceBuffer->SetName(L"Instance Buffer");)
|
||||
}
|
||||
|
||||
// Update parameters
|
||||
mInstanceSize = inInstanceSize;
|
||||
}
|
||||
|
||||
void *RenderInstancesDX12::Lock()
|
||||
{
|
||||
uint32 *mapped_resource;
|
||||
D3D12_RANGE range = { 0, 0 };
|
||||
mInstanceBuffer->Map(0, &range, (void **)&mapped_resource);
|
||||
return mapped_resource;
|
||||
}
|
||||
|
||||
void RenderInstancesDX12::Unlock()
|
||||
{
|
||||
mInstanceBuffer->Unmap(0, nullptr);
|
||||
}
|
||||
|
||||
void RenderInstancesDX12::Draw(RenderPrimitive *inPrimitive, int inStartInstance, int inNumInstances) const
|
||||
{
|
||||
if (inNumInstances <= 0)
|
||||
return;
|
||||
|
||||
RenderPrimitiveDX12 *primitive = static_cast<RenderPrimitiveDX12 *>(inPrimitive);
|
||||
|
||||
ID3D12GraphicsCommandList *command_list = mRenderer->GetCommandList();
|
||||
|
||||
// Set topology
|
||||
command_list->IASetPrimitiveTopology(primitive->mType == PipelineState::ETopology::Triangle? D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST : D3D_PRIMITIVE_TOPOLOGY_LINELIST);
|
||||
|
||||
D3D12_VERTEX_BUFFER_VIEW vb_view[2];
|
||||
|
||||
// Vertex buffer
|
||||
vb_view[0].BufferLocation = primitive->mVtxBuffer->GetGPUVirtualAddress();
|
||||
vb_view[0].StrideInBytes = primitive->mVtxSize;
|
||||
vb_view[0].SizeInBytes = primitive->mNumVtxToDraw * primitive->mVtxSize;
|
||||
|
||||
// Instances buffer
|
||||
vb_view[1].BufferLocation = mInstanceBuffer->GetGPUVirtualAddress();
|
||||
vb_view[1].StrideInBytes = mInstanceSize;
|
||||
vb_view[1].SizeInBytes = mInstanceBufferSize;
|
||||
|
||||
command_list->IASetVertexBuffers(0, 2, &vb_view[0]);
|
||||
|
||||
if (primitive->mIdxBuffer == nullptr)
|
||||
{
|
||||
// Draw instanced primitive
|
||||
command_list->DrawInstanced(primitive->mNumVtxToDraw, inNumInstances, 0, inStartInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set index buffer
|
||||
D3D12_INDEX_BUFFER_VIEW ib_view;
|
||||
ib_view.BufferLocation = primitive->mIdxBuffer->GetGPUVirtualAddress();
|
||||
ib_view.SizeInBytes = primitive->mNumIdxToDraw * sizeof(uint32);
|
||||
ib_view.Format = DXGI_FORMAT_R32_UINT;
|
||||
command_list->IASetIndexBuffer(&ib_view);
|
||||
|
||||
// Draw instanced primitive
|
||||
command_list->DrawIndexedInstanced(primitive->mNumIdxToDraw, inNumInstances, 0, 0, inStartInstance);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/DX12/RendererDX12.h>
|
||||
#include <Renderer/RenderInstances.h>
|
||||
|
||||
class RenderPrimitive;
|
||||
|
||||
/// DirectX 12 implementation of a render instances object
|
||||
class RenderInstancesDX12 : public RenderInstances
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
RenderInstancesDX12(RendererDX12 *inRenderer) : mRenderer(inRenderer) { }
|
||||
virtual ~RenderInstancesDX12() override { Clear(); }
|
||||
|
||||
/// Erase all instance data
|
||||
virtual void Clear() override;
|
||||
|
||||
/// Instance buffer management functions
|
||||
virtual void CreateBuffer(int inNumInstances, int inInstanceSize) override;
|
||||
virtual void * Lock() override;
|
||||
virtual void Unlock() override;
|
||||
|
||||
/// Draw the instances when context has been set by Renderer::BindShader
|
||||
virtual void Draw(RenderPrimitive *inPrimitive, int inStartInstance, int inNumInstances) const override;
|
||||
|
||||
private:
|
||||
RendererDX12 * mRenderer;
|
||||
|
||||
ComPtr<ID3D12Resource> mInstanceBuffer;
|
||||
uint mInstanceBufferSize = 0;
|
||||
int mInstanceSize = 0;
|
||||
};
|
||||
@@ -0,0 +1,148 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/DX12/RenderPrimitiveDX12.h>
|
||||
#include <Renderer/DX12/FatalErrorIfFailedDX12.h>
|
||||
|
||||
void RenderPrimitiveDX12::ReleaseVertexBuffer()
|
||||
{
|
||||
if (mVtxBuffer != nullptr)
|
||||
{
|
||||
if (mVtxBufferInUploadHeap)
|
||||
mRenderer->RecycleD3DResourceOnUploadHeap(mVtxBuffer.Get(), mNumVtx * mVtxSize);
|
||||
else
|
||||
mRenderer->RecycleD3DObject(mVtxBuffer.Get());
|
||||
mVtxBuffer = nullptr;
|
||||
}
|
||||
|
||||
mVtxBufferInUploadHeap = false;
|
||||
RenderPrimitive::ReleaseVertexBuffer();
|
||||
}
|
||||
|
||||
void RenderPrimitiveDX12::ReleaseIndexBuffer()
|
||||
{
|
||||
if (mIdxBuffer != nullptr)
|
||||
{
|
||||
if (mIdxBufferInUploadHeap)
|
||||
mRenderer->RecycleD3DResourceOnUploadHeap(mIdxBuffer.Get(), mNumIdx * sizeof(uint32));
|
||||
else
|
||||
mRenderer->RecycleD3DObject(mIdxBuffer.Get());
|
||||
mIdxBuffer = nullptr;
|
||||
}
|
||||
|
||||
mIdxBufferInUploadHeap = false;
|
||||
RenderPrimitive::ReleaseIndexBuffer();
|
||||
}
|
||||
|
||||
void RenderPrimitiveDX12::CreateVertexBuffer(int inNumVtx, int inVtxSize, const void *inData)
|
||||
{
|
||||
RenderPrimitive::CreateVertexBuffer(inNumVtx, inVtxSize, inData);
|
||||
|
||||
uint64 size = uint64(inNumVtx) * inVtxSize;
|
||||
|
||||
if (inData != nullptr)
|
||||
{
|
||||
// Data provided, assume the buffer is static so allocate it on the GPU
|
||||
mVtxBuffer = mRenderer->CreateD3DResourceOnDefaultHeap(inData, size);
|
||||
mVtxBufferInUploadHeap = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No data provided, create a buffer that will be uploaded to the GPU every time it is used
|
||||
mVtxBuffer = mRenderer->CreateD3DResourceOnUploadHeap(size);
|
||||
mVtxBufferInUploadHeap = true;
|
||||
}
|
||||
|
||||
JPH_IF_DEBUG(mVtxBuffer->SetName(L"Vertex Buffer");)
|
||||
}
|
||||
|
||||
void *RenderPrimitiveDX12::LockVertexBuffer()
|
||||
{
|
||||
void *mapped_resource;
|
||||
D3D12_RANGE range = { 0, 0 };
|
||||
FatalErrorIfFailed(mVtxBuffer->Map(0, &range, &mapped_resource));
|
||||
return mapped_resource;
|
||||
}
|
||||
|
||||
void RenderPrimitiveDX12::UnlockVertexBuffer()
|
||||
{
|
||||
mVtxBuffer->Unmap(0, nullptr);
|
||||
}
|
||||
|
||||
void RenderPrimitiveDX12::CreateIndexBuffer(int inNumIdx, const uint32 *inData)
|
||||
{
|
||||
RenderPrimitive::CreateIndexBuffer(inNumIdx, inData);
|
||||
|
||||
uint64 size = uint64(inNumIdx) * sizeof(uint32);
|
||||
|
||||
if (inData != nullptr)
|
||||
{
|
||||
// Data provided, assume the buffer is static so allocate it on the GPU
|
||||
mIdxBuffer = mRenderer->CreateD3DResourceOnDefaultHeap(inData, size);
|
||||
mIdxBufferInUploadHeap = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No data provided, create a buffer that will be uploaded to the GPU every time it is used
|
||||
mIdxBuffer = mRenderer->CreateD3DResourceOnUploadHeap(size);
|
||||
mIdxBufferInUploadHeap = true;
|
||||
}
|
||||
|
||||
JPH_IF_DEBUG(mIdxBuffer->SetName(L"Index Buffer");)
|
||||
}
|
||||
|
||||
uint32 *RenderPrimitiveDX12::LockIndexBuffer()
|
||||
{
|
||||
uint32 *mapped_resource;
|
||||
D3D12_RANGE range = { 0, 0 };
|
||||
FatalErrorIfFailed(mIdxBuffer->Map(0, &range, (void **)&mapped_resource));
|
||||
return mapped_resource;
|
||||
}
|
||||
|
||||
void RenderPrimitiveDX12::UnlockIndexBuffer()
|
||||
{
|
||||
mIdxBuffer->Unmap(0, nullptr);
|
||||
}
|
||||
|
||||
void RenderPrimitiveDX12::Draw() const
|
||||
{
|
||||
ID3D12GraphicsCommandList *command_list = mRenderer->GetCommandList();
|
||||
|
||||
// Set topology
|
||||
command_list->IASetPrimitiveTopology(mType == PipelineState::ETopology::Triangle? D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST : D3D_PRIMITIVE_TOPOLOGY_LINELIST);
|
||||
|
||||
if (mIdxBuffer == nullptr)
|
||||
{
|
||||
// Set vertex buffer
|
||||
D3D12_VERTEX_BUFFER_VIEW vb_view;
|
||||
vb_view.BufferLocation = mVtxBuffer->GetGPUVirtualAddress();
|
||||
vb_view.StrideInBytes = mVtxSize;
|
||||
vb_view.SizeInBytes = mNumVtxToDraw * mVtxSize;
|
||||
command_list->IASetVertexBuffers(0, 1, &vb_view);
|
||||
|
||||
// Draw the non indexed primitive
|
||||
command_list->DrawInstanced(mNumVtxToDraw, 1, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set vertex buffer
|
||||
D3D12_VERTEX_BUFFER_VIEW vb_view;
|
||||
vb_view.BufferLocation = mVtxBuffer->GetGPUVirtualAddress();
|
||||
vb_view.StrideInBytes = mVtxSize;
|
||||
vb_view.SizeInBytes = mNumVtx * mVtxSize;
|
||||
command_list->IASetVertexBuffers(0, 1, &vb_view);
|
||||
|
||||
// Set index buffer
|
||||
D3D12_INDEX_BUFFER_VIEW ib_view;
|
||||
ib_view.BufferLocation = mIdxBuffer->GetGPUVirtualAddress();
|
||||
ib_view.SizeInBytes = mNumIdxToDraw * sizeof(uint32);
|
||||
ib_view.Format = DXGI_FORMAT_R32_UINT;
|
||||
command_list->IASetIndexBuffer(&ib_view);
|
||||
|
||||
// Draw indexed primitive
|
||||
command_list->DrawIndexedInstanced(mNumIdxToDraw, 1, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/RenderPrimitive.h>
|
||||
#include <Renderer/DX12/RendererDX12.h>
|
||||
|
||||
/// DirectX 12 implementation of a render primitive
|
||||
class RenderPrimitiveDX12 : public RenderPrimitive
|
||||
{
|
||||
public:
|
||||
JPH_OVERRIDE_NEW_DELETE
|
||||
|
||||
/// Constructor
|
||||
RenderPrimitiveDX12(RendererDX12 *inRenderer, PipelineState::ETopology inType) : mRenderer(inRenderer), mType(inType) { }
|
||||
virtual ~RenderPrimitiveDX12() override { Clear(); }
|
||||
|
||||
/// Vertex buffer management functions
|
||||
virtual void CreateVertexBuffer(int inNumVtx, int inVtxSize, const void *inData = nullptr) override;
|
||||
virtual void ReleaseVertexBuffer() override;
|
||||
virtual void * LockVertexBuffer() override;
|
||||
virtual void UnlockVertexBuffer() override;
|
||||
|
||||
/// Index buffer management functions
|
||||
virtual void CreateIndexBuffer(int inNumIdx, const uint32 *inData = nullptr) override;
|
||||
virtual void ReleaseIndexBuffer() override;
|
||||
virtual uint32 * LockIndexBuffer() override;
|
||||
virtual void UnlockIndexBuffer() override;
|
||||
|
||||
/// Draw the primitive
|
||||
virtual void Draw() const override;
|
||||
|
||||
private:
|
||||
friend class RenderInstancesDX12;
|
||||
|
||||
RendererDX12 * mRenderer;
|
||||
|
||||
PipelineState::ETopology mType;
|
||||
|
||||
ComPtr<ID3D12Resource> mVtxBuffer;
|
||||
bool mVtxBufferInUploadHeap = false;
|
||||
|
||||
ComPtr<ID3D12Resource> mIdxBuffer;
|
||||
bool mIdxBufferInUploadHeap = false;
|
||||
};
|
||||
719
lib/All/JoltPhysics/TestFramework/Renderer/DX12/RendererDX12.cpp
Normal file
719
lib/All/JoltPhysics/TestFramework/Renderer/DX12/RendererDX12.cpp
Normal file
@@ -0,0 +1,719 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/DX12/RendererDX12.h>
|
||||
#include <Renderer/DX12/RenderPrimitiveDX12.h>
|
||||
#include <Renderer/DX12/PipelineStateDX12.h>
|
||||
#include <Renderer/DX12/VertexShaderDX12.h>
|
||||
#include <Renderer/DX12/PixelShaderDX12.h>
|
||||
#include <Renderer/DX12/TextureDX12.h>
|
||||
#include <Renderer/DX12/RenderInstancesDX12.h>
|
||||
#include <Renderer/DX12/FatalErrorIfFailedDX12.h>
|
||||
#include <Window/ApplicationWindowWin.h>
|
||||
#include <Jolt/Core/Profiler.h>
|
||||
#include <Utils/ReadData.h>
|
||||
#include <Utils/Log.h>
|
||||
#include <Utils/AssetStream.h>
|
||||
|
||||
#include <d3dcompiler.h>
|
||||
#ifdef JPH_DEBUG
|
||||
#include <d3d12sdklayers.h>
|
||||
#endif
|
||||
|
||||
RendererDX12::~RendererDX12()
|
||||
{
|
||||
// Ensure that the GPU is no longer referencing resources that are about to be cleaned up by the destructor.
|
||||
WaitForGpu();
|
||||
|
||||
// Don't add more stuff to the delay reference list
|
||||
mIsExiting = true;
|
||||
|
||||
CloseHandle(mFenceEvent);
|
||||
}
|
||||
|
||||
void RendererDX12::WaitForGpu()
|
||||
{
|
||||
// Schedule a Signal command in the queue
|
||||
UINT64 current_fence_value = mFenceValues[mFrameIndex];
|
||||
FatalErrorIfFailed(mCommandQueue->Signal(mFence.Get(), current_fence_value));
|
||||
|
||||
// Wait until the fence has been processed
|
||||
FatalErrorIfFailed(mFence->SetEventOnCompletion(current_fence_value, mFenceEvent));
|
||||
WaitForSingleObjectEx(mFenceEvent, INFINITE, FALSE);
|
||||
|
||||
// Increment the fence value for all frames
|
||||
for (uint n = 0; n < cFrameCount; ++n)
|
||||
mFenceValues[n] = current_fence_value + 1;
|
||||
|
||||
// Release all used resources
|
||||
for (Array<ComPtr<ID3D12Object>> &list : mDelayReleased)
|
||||
list.clear();
|
||||
|
||||
// Anything that's not used yet can be removed, delayed objects are now available
|
||||
mResourceCache.clear();
|
||||
mDelayCached[mFrameIndex].swap(mResourceCache);
|
||||
}
|
||||
|
||||
void RendererDX12::CreateRenderTargets()
|
||||
{
|
||||
// Create render targets and views
|
||||
for (uint n = 0; n < cFrameCount; ++n)
|
||||
{
|
||||
mRenderTargetViews[n] = mRTVHeap.Allocate();
|
||||
|
||||
FatalErrorIfFailed(mSwapChain->GetBuffer(n, IID_PPV_ARGS(&mRenderTargets[n])));
|
||||
mDevice->CreateRenderTargetView(mRenderTargets[n].Get(), nullptr, mRenderTargetViews[n]);
|
||||
}
|
||||
}
|
||||
|
||||
void RendererDX12::CreateDepthBuffer()
|
||||
{
|
||||
// Free any previous depth stencil view
|
||||
if (mDepthStencilView.ptr != 0)
|
||||
mDSVHeap.Free(mDepthStencilView);
|
||||
|
||||
// Free any previous depth stencil buffer
|
||||
mDepthStencilBuffer.Reset();
|
||||
|
||||
// Allocate depth stencil buffer
|
||||
D3D12_CLEAR_VALUE clear_value = {};
|
||||
clear_value.Format = DXGI_FORMAT_D32_FLOAT;
|
||||
clear_value.DepthStencil.Depth = 0.0f;
|
||||
clear_value.DepthStencil.Stencil = 0;
|
||||
|
||||
D3D12_HEAP_PROPERTIES heap_properties = {};
|
||||
heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
heap_properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
heap_properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
heap_properties.CreationNodeMask = 1;
|
||||
heap_properties.VisibleNodeMask = 1;
|
||||
|
||||
D3D12_RESOURCE_DESC depth_stencil_desc = {};
|
||||
depth_stencil_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
||||
depth_stencil_desc.Alignment = 0;
|
||||
depth_stencil_desc.Width = mWindow->GetWindowWidth();
|
||||
depth_stencil_desc.Height = mWindow->GetWindowHeight();
|
||||
depth_stencil_desc.DepthOrArraySize = 1;
|
||||
depth_stencil_desc.MipLevels = 1;
|
||||
depth_stencil_desc.Format = DXGI_FORMAT_D32_FLOAT;
|
||||
depth_stencil_desc.SampleDesc.Count = 1;
|
||||
depth_stencil_desc.SampleDesc.Quality = 0;
|
||||
depth_stencil_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
||||
depth_stencil_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
|
||||
|
||||
FatalErrorIfFailed(mDevice->CreateCommittedResource(&heap_properties, D3D12_HEAP_FLAG_NONE, &depth_stencil_desc, D3D12_RESOURCE_STATE_DEPTH_WRITE, &clear_value, IID_PPV_ARGS(&mDepthStencilBuffer)));
|
||||
|
||||
// Allocate depth stencil view
|
||||
D3D12_DEPTH_STENCIL_VIEW_DESC depth_stencil_view_desc = {};
|
||||
depth_stencil_view_desc.Format = DXGI_FORMAT_D32_FLOAT;
|
||||
depth_stencil_view_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
|
||||
depth_stencil_view_desc.Flags = D3D12_DSV_FLAG_NONE;
|
||||
|
||||
mDepthStencilView = mDSVHeap.Allocate();
|
||||
mDevice->CreateDepthStencilView(mDepthStencilBuffer.Get(), &depth_stencil_view_desc, mDepthStencilView);
|
||||
}
|
||||
|
||||
void RendererDX12::Initialize(ApplicationWindow *inWindow)
|
||||
{
|
||||
Renderer::Initialize(inWindow);
|
||||
|
||||
#if defined(JPH_DEBUG)
|
||||
// Enable the D3D12 debug layer
|
||||
ComPtr<ID3D12Debug> debug_controller;
|
||||
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug_controller))))
|
||||
debug_controller->EnableDebugLayer();
|
||||
#endif
|
||||
|
||||
// Create DXGI factory
|
||||
FatalErrorIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(&mDXGIFactory)));
|
||||
|
||||
// Find adapter
|
||||
ComPtr<IDXGIAdapter1> adapter;
|
||||
|
||||
HRESULT result = E_FAIL;
|
||||
|
||||
// First check if we have the Windows 1803 IDXGIFactory6 interface
|
||||
ComPtr<IDXGIFactory6> factory6;
|
||||
if (SUCCEEDED(mDXGIFactory->QueryInterface(IID_PPV_ARGS(&factory6))))
|
||||
{
|
||||
for (UINT index = 0; DXGI_ERROR_NOT_FOUND != factory6->EnumAdapterByGpuPreference(index, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(&adapter)); ++index)
|
||||
{
|
||||
DXGI_ADAPTER_DESC1 desc;
|
||||
adapter->GetDesc1(&desc);
|
||||
|
||||
// We don't want software renderers
|
||||
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
|
||||
continue;
|
||||
|
||||
// Check to see whether the adapter supports Direct3D 12
|
||||
result = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&mDevice));
|
||||
if (SUCCEEDED(result))
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fall back to the older method that may not get the fastest GPU
|
||||
for (UINT index = 0; DXGI_ERROR_NOT_FOUND != mDXGIFactory->EnumAdapters1(index, &adapter); ++index)
|
||||
{
|
||||
DXGI_ADAPTER_DESC1 desc;
|
||||
adapter->GetDesc1(&desc);
|
||||
|
||||
// We don't want software renderers
|
||||
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
|
||||
continue;
|
||||
|
||||
// Check to see whether the adapter supports Direct3D 12
|
||||
result = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&mDevice));
|
||||
if (SUCCEEDED(result))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we managed to obtain a device
|
||||
FatalErrorIfFailed(result);
|
||||
|
||||
#ifdef JPH_DEBUG
|
||||
// Enable breaking on errors
|
||||
ComPtr<ID3D12InfoQueue> info_queue;
|
||||
if (SUCCEEDED(mDevice.As(&info_queue)))
|
||||
{
|
||||
info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, TRUE);
|
||||
info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, TRUE);
|
||||
info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, TRUE);
|
||||
|
||||
// Disable an error that triggers on Windows 11 with a hybrid graphic system
|
||||
// See: https://stackoverflow.com/questions/69805245/directx-12-application-is-crashing-in-windows-11
|
||||
D3D12_MESSAGE_ID hide[] =
|
||||
{
|
||||
D3D12_MESSAGE_ID_RESOURCE_BARRIER_MISMATCHING_COMMAND_LIST_TYPE,
|
||||
};
|
||||
D3D12_INFO_QUEUE_FILTER filter = { };
|
||||
filter.DenyList.NumIDs = static_cast<UINT>( std::size( hide ) );
|
||||
filter.DenyList.pIDList = hide;
|
||||
info_queue->AddStorageFilterEntries( &filter );
|
||||
}
|
||||
#endif // JPH_DEBUG
|
||||
|
||||
// Disable full screen transitions
|
||||
FatalErrorIfFailed(mDXGIFactory->MakeWindowAssociation(static_cast<ApplicationWindowWin *>(mWindow)->GetWindowHandle(), DXGI_MWA_NO_ALT_ENTER));
|
||||
|
||||
// Create heaps
|
||||
mRTVHeap.Init(mDevice.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 2);
|
||||
mDSVHeap.Init(mDevice.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 4);
|
||||
mSRVHeap.Init(mDevice.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 128);
|
||||
|
||||
// Create a command queue
|
||||
D3D12_COMMAND_QUEUE_DESC queue_desc = {};
|
||||
queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
||||
queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
||||
FatalErrorIfFailed(mDevice->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&mCommandQueue)));
|
||||
|
||||
// Create a command allocator for each frame
|
||||
for (uint n = 0; n < cFrameCount; n++)
|
||||
FatalErrorIfFailed(mDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&mCommandAllocators[n])));
|
||||
|
||||
// Describe and create the swap chain
|
||||
DXGI_SWAP_CHAIN_DESC swap_chain_desc = {};
|
||||
swap_chain_desc.BufferCount = cFrameCount;
|
||||
swap_chain_desc.BufferDesc.Width = mWindow->GetWindowWidth();
|
||||
swap_chain_desc.BufferDesc.Height = mWindow->GetWindowHeight();
|
||||
swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||
swap_chain_desc.OutputWindow = static_cast<ApplicationWindowWin *>(mWindow)->GetWindowHandle();
|
||||
swap_chain_desc.SampleDesc.Count = 1;
|
||||
swap_chain_desc.Windowed = TRUE;
|
||||
|
||||
ComPtr<IDXGISwapChain> swap_chain;
|
||||
FatalErrorIfFailed(mDXGIFactory->CreateSwapChain(mCommandQueue.Get(), &swap_chain_desc, &swap_chain));
|
||||
FatalErrorIfFailed(swap_chain.As(&mSwapChain));
|
||||
mFrameIndex = mSwapChain->GetCurrentBackBufferIndex();
|
||||
|
||||
CreateRenderTargets();
|
||||
|
||||
CreateDepthBuffer();
|
||||
|
||||
// Create a root signature suitable for all our shaders
|
||||
D3D12_ROOT_PARAMETER params[3] = {};
|
||||
|
||||
// Mapping a constant buffer to slot 0 for the vertex shader
|
||||
params[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
|
||||
params[0].Descriptor.ShaderRegister = 0;
|
||||
params[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
|
||||
|
||||
// Mapping a constant buffer to slot 1 in the pixel shader
|
||||
params[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
|
||||
params[1].Descriptor.ShaderRegister = 1;
|
||||
params[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
||||
|
||||
// Mapping a texture to slot 2 in the pixel shader
|
||||
D3D12_DESCRIPTOR_RANGE range = {};
|
||||
range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
||||
range.BaseShaderRegister = 2;
|
||||
range.NumDescriptors = 1;
|
||||
params[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
||||
params[2].DescriptorTable.NumDescriptorRanges = 1;
|
||||
params[2].DescriptorTable.pDescriptorRanges = ⦥
|
||||
params[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
||||
|
||||
D3D12_STATIC_SAMPLER_DESC samplers[3] = {};
|
||||
|
||||
// Sampler 0: Non-wrapping linear filtering
|
||||
samplers[0].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
samplers[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
samplers[0].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
samplers[0].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
samplers[0].MipLODBias = 0.0f;
|
||||
samplers[0].MaxAnisotropy = 1;
|
||||
samplers[0].ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
|
||||
samplers[0].BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
|
||||
samplers[0].MinLOD = 0.0f;
|
||||
samplers[0].MaxLOD = D3D12_FLOAT32_MAX;
|
||||
samplers[0].ShaderRegister = 0;
|
||||
samplers[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
||||
|
||||
// Sampler 1: Wrapping and linear filtering
|
||||
samplers[1] = samplers[0];
|
||||
samplers[1].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
samplers[1].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
samplers[1].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
samplers[1].ShaderRegister = 1;
|
||||
|
||||
// Sampler 2: Point filtering, using SampleCmp mode to compare if sampled value >= reference value (for shadows)
|
||||
samplers[2] = samplers[0];
|
||||
samplers[2].Filter = D3D12_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT;
|
||||
samplers[2].ComparisonFunc = D3D12_COMPARISON_FUNC_GREATER_EQUAL;
|
||||
samplers[2].ShaderRegister = 2;
|
||||
|
||||
D3D12_ROOT_SIGNATURE_DESC root_signature_desc = {};
|
||||
root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
|
||||
root_signature_desc.NumParameters = ARRAYSIZE(params);
|
||||
root_signature_desc.pParameters = params;
|
||||
root_signature_desc.NumStaticSamplers = ARRAYSIZE(samplers);
|
||||
root_signature_desc.pStaticSamplers = samplers;
|
||||
|
||||
ComPtr<ID3DBlob> signature;
|
||||
ComPtr<ID3DBlob> error;
|
||||
FatalErrorIfFailed(D3D12SerializeRootSignature(&root_signature_desc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error));
|
||||
FatalErrorIfFailed(mDevice->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&mRootSignature)));
|
||||
|
||||
// Create the command list
|
||||
FatalErrorIfFailed(mDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, mCommandAllocators[mFrameIndex].Get(), nullptr, IID_PPV_ARGS(&mCommandList)));
|
||||
|
||||
// Command lists are created in the recording state, but there is nothing to record yet. The main loop expects it to be closed, so close it now
|
||||
FatalErrorIfFailed(mCommandList->Close());
|
||||
|
||||
// Create synchronization object
|
||||
FatalErrorIfFailed(mDevice->CreateFence(mFenceValues[mFrameIndex], D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence)));
|
||||
|
||||
// Increment fence value so we don't skip waiting the first time a command list is executed
|
||||
mFenceValues[mFrameIndex]++;
|
||||
|
||||
// Create an event handle to use for frame synchronization
|
||||
mFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
if (mFenceEvent == nullptr)
|
||||
FatalErrorIfFailed(HRESULT_FROM_WIN32(GetLastError()));
|
||||
|
||||
// Initialize the queue used to upload resources to the GPU
|
||||
mUploadQueue.Initialize(mDevice.Get());
|
||||
|
||||
// Create constant buffer. One per frame to avoid overwriting the constant buffer while the GPU is still using it.
|
||||
for (uint n = 0; n < cFrameCount; ++n)
|
||||
{
|
||||
mVertexShaderConstantBufferProjection[n] = CreateConstantBuffer(sizeof(VertexShaderConstantBuffer));
|
||||
mVertexShaderConstantBufferOrtho[n] = CreateConstantBuffer(sizeof(VertexShaderConstantBuffer));
|
||||
mPixelShaderConstantBuffer[n] = CreateConstantBuffer(sizeof(PixelShaderConstantBuffer));
|
||||
}
|
||||
|
||||
// Create depth only texture (no color buffer, as seen from light)
|
||||
mShadowMap = new TextureDX12(this, cShadowMapSize, cShadowMapSize);
|
||||
}
|
||||
|
||||
void RendererDX12::OnWindowResize()
|
||||
{
|
||||
// Wait for the previous frame to be rendered
|
||||
WaitForGpu();
|
||||
|
||||
// Free the render targets and views to allow resizing the swap chain
|
||||
for (uint n = 0; n < cFrameCount; ++n)
|
||||
{
|
||||
mRTVHeap.Free(mRenderTargetViews[n]);
|
||||
mRenderTargets[n].Reset();
|
||||
}
|
||||
|
||||
// Resize the swap chain buffers
|
||||
FatalErrorIfFailed(mSwapChain->ResizeBuffers(cFrameCount, mWindow->GetWindowWidth(), mWindow->GetWindowHeight(), DXGI_FORMAT_R8G8B8A8_UNORM, 0));
|
||||
|
||||
// Back buffer index may have changed after the resize (it always seems to go to 0 again)
|
||||
mFrameIndex = mSwapChain->GetCurrentBackBufferIndex();
|
||||
|
||||
// Since we may have switched frame index and we know everything is done, we need to update the fence value for our other frame as completed
|
||||
for (uint n = 0; n < cFrameCount; ++n)
|
||||
if (mFrameIndex != n)
|
||||
mFenceValues[n] = mFence->GetCompletedValue();
|
||||
|
||||
// Recreate render targets
|
||||
CreateRenderTargets();
|
||||
|
||||
// Recreate depth buffer
|
||||
CreateDepthBuffer();
|
||||
}
|
||||
|
||||
bool RendererDX12::BeginFrame(const CameraState &inCamera, float inWorldScale)
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
Renderer::BeginFrame(inCamera, inWorldScale);
|
||||
|
||||
// Reset command allocator
|
||||
FatalErrorIfFailed(mCommandAllocators[mFrameIndex]->Reset());
|
||||
|
||||
// Reset command list
|
||||
FatalErrorIfFailed(mCommandList->Reset(mCommandAllocators[mFrameIndex].Get(), nullptr));
|
||||
|
||||
// Set root signature
|
||||
mCommandList->SetGraphicsRootSignature(mRootSignature.Get());
|
||||
|
||||
// Set SRV heap
|
||||
ID3D12DescriptorHeap *heaps[] = { mSRVHeap.Get() };
|
||||
mCommandList->SetDescriptorHeaps(_countof(heaps), heaps);
|
||||
|
||||
// Indicate that the back buffer will be used as a render target.
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = mRenderTargets[mFrameIndex].Get();
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
mCommandList->ResourceBarrier(1, &barrier);
|
||||
|
||||
// Clear the back buffer.
|
||||
const float blue[] = { 0.098f, 0.098f, 0.439f, 1.000f };
|
||||
mCommandList->ClearRenderTargetView(mRenderTargetViews[mFrameIndex], blue, 0, nullptr);
|
||||
mCommandList->ClearDepthStencilView(mDepthStencilView, D3D12_CLEAR_FLAG_DEPTH, 0.0f, 0, 0, nullptr);
|
||||
|
||||
// Set constants for vertex shader in projection mode
|
||||
VertexShaderConstantBuffer *vs = mVertexShaderConstantBufferProjection[mFrameIndex]->Map<VertexShaderConstantBuffer>();
|
||||
*vs = mVSBuffer;
|
||||
mVertexShaderConstantBufferProjection[mFrameIndex]->Unmap();
|
||||
|
||||
// Set constants for vertex shader in ortho mode
|
||||
vs = mVertexShaderConstantBufferOrtho[mFrameIndex]->Map<VertexShaderConstantBuffer>();
|
||||
*vs = mVSBufferOrtho;
|
||||
mVertexShaderConstantBufferOrtho[mFrameIndex]->Unmap();
|
||||
|
||||
// Switch to 3d projection mode
|
||||
SetProjectionMode();
|
||||
|
||||
// Set constants for pixel shader
|
||||
PixelShaderConstantBuffer *ps = mPixelShaderConstantBuffer[mFrameIndex]->Map<PixelShaderConstantBuffer>();
|
||||
*ps = mPSBuffer;
|
||||
mPixelShaderConstantBuffer[mFrameIndex]->Unmap();
|
||||
|
||||
// Set the pixel shader constant buffer data.
|
||||
mPixelShaderConstantBuffer[mFrameIndex]->Bind(1);
|
||||
|
||||
// Start drawing the shadow pass
|
||||
mShadowMap->SetAsRenderTarget(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RendererDX12::EndShadowPass()
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
// Finish drawing the shadow pass
|
||||
mShadowMap->SetAsRenderTarget(false);
|
||||
|
||||
// Set the main back buffer as render target
|
||||
mCommandList->OMSetRenderTargets(1, &mRenderTargetViews[mFrameIndex], FALSE, &mDepthStencilView);
|
||||
|
||||
// Set viewport
|
||||
D3D12_VIEWPORT viewport = { 0.0f, 0.0f, static_cast<float>(mWindow->GetWindowWidth()), static_cast<float>(mWindow->GetWindowHeight()), 0.0f, 1.0f };
|
||||
mCommandList->RSSetViewports(1, &viewport);
|
||||
|
||||
// Set scissor rect
|
||||
D3D12_RECT scissor_rect = { 0, 0, static_cast<LONG>(mWindow->GetWindowWidth()), static_cast<LONG>(mWindow->GetWindowHeight()) };
|
||||
mCommandList->RSSetScissorRects(1, &scissor_rect);
|
||||
}
|
||||
|
||||
void RendererDX12::EndFrame()
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
Renderer::EndFrame();
|
||||
|
||||
// Indicate that the back buffer will now be used to present.
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = mRenderTargets[mFrameIndex].Get();
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
mCommandList->ResourceBarrier(1, &barrier);
|
||||
|
||||
// Close the command list
|
||||
FatalErrorIfFailed(mCommandList->Close());
|
||||
|
||||
// Execute the command list
|
||||
ID3D12CommandList* command_lists[] = { mCommandList.Get() };
|
||||
mCommandQueue->ExecuteCommandLists(_countof(command_lists), command_lists);
|
||||
|
||||
// Present the frame
|
||||
FatalErrorIfFailed(mSwapChain->Present(1, 0));
|
||||
|
||||
// Schedule a Signal command in the queue
|
||||
UINT64 current_fence_value = mFenceValues[mFrameIndex];
|
||||
FatalErrorIfFailed(mCommandQueue->Signal(mFence.Get(), current_fence_value));
|
||||
|
||||
// Update the frame index
|
||||
mFrameIndex = mSwapChain->GetCurrentBackBufferIndex();
|
||||
|
||||
// If the next frame is not ready to be rendered yet, wait until it is ready
|
||||
UINT64 completed_value = mFence->GetCompletedValue();
|
||||
if (completed_value < mFenceValues[mFrameIndex])
|
||||
{
|
||||
FatalErrorIfFailed(mFence->SetEventOnCompletion(mFenceValues[mFrameIndex], mFenceEvent));
|
||||
WaitForSingleObjectEx(mFenceEvent, INFINITE, FALSE);
|
||||
}
|
||||
|
||||
// Release all used resources
|
||||
mDelayReleased[mFrameIndex].clear();
|
||||
|
||||
// Anything that's not used yet can be removed, delayed objects are now available
|
||||
mResourceCache.clear();
|
||||
mDelayCached[mFrameIndex].swap(mResourceCache);
|
||||
|
||||
// Set the fence value for the next frame.
|
||||
mFenceValues[mFrameIndex] = current_fence_value + 1;
|
||||
}
|
||||
|
||||
|
||||
void RendererDX12::SetProjectionMode()
|
||||
{
|
||||
JPH_ASSERT(mInFrame);
|
||||
|
||||
mVertexShaderConstantBufferProjection[mFrameIndex]->Bind(0);
|
||||
}
|
||||
|
||||
void RendererDX12::SetOrthoMode()
|
||||
{
|
||||
JPH_ASSERT(mInFrame);
|
||||
|
||||
mVertexShaderConstantBufferOrtho[mFrameIndex]->Bind(0);
|
||||
}
|
||||
|
||||
Ref<Texture> RendererDX12::CreateTexture(const Surface *inSurface)
|
||||
{
|
||||
return new TextureDX12(this, inSurface);
|
||||
}
|
||||
|
||||
Ref<VertexShader> RendererDX12::CreateVertexShader(const char *inName)
|
||||
{
|
||||
UINT flags = D3DCOMPILE_ENABLE_STRICTNESS;
|
||||
#ifdef JPH_DEBUG
|
||||
flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
|
||||
#endif
|
||||
|
||||
const D3D_SHADER_MACRO defines[] =
|
||||
{
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
// Read shader source file
|
||||
String file_name = String("Shaders/DX/") + inName + ".hlsl";
|
||||
Array<uint8> data = ReadData(file_name.c_str());
|
||||
|
||||
// Compile source
|
||||
ComPtr<ID3DBlob> shader_blob, error_blob;
|
||||
HRESULT hr = D3DCompile(&data[0],
|
||||
(uint)data.size(),
|
||||
(AssetStream::sGetAssetsBasePath() + file_name).c_str(),
|
||||
defines,
|
||||
D3D_COMPILE_STANDARD_FILE_INCLUDE,
|
||||
"main",
|
||||
"vs_5_0",
|
||||
flags,
|
||||
0,
|
||||
shader_blob.GetAddressOf(),
|
||||
error_blob.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// Throw error if compilation failed
|
||||
if (error_blob)
|
||||
OutputDebugStringA((const char *)error_blob->GetBufferPointer());
|
||||
FatalError("Failed to compile vertex shader");
|
||||
}
|
||||
|
||||
return new VertexShaderDX12(shader_blob);
|
||||
}
|
||||
|
||||
Ref<PixelShader> RendererDX12::CreatePixelShader(const char *inName)
|
||||
{
|
||||
UINT flags = D3DCOMPILE_ENABLE_STRICTNESS;
|
||||
#ifdef JPH_DEBUG
|
||||
flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
|
||||
#endif
|
||||
|
||||
const D3D_SHADER_MACRO defines[] =
|
||||
{
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
// Read shader source file
|
||||
String file_name = String("Shaders/DX/") + inName + ".hlsl";
|
||||
Array<uint8> data = ReadData(file_name.c_str());
|
||||
|
||||
// Compile source
|
||||
ComPtr<ID3DBlob> shader_blob, error_blob;
|
||||
HRESULT hr = D3DCompile(&data[0],
|
||||
(uint)data.size(),
|
||||
(AssetStream::sGetAssetsBasePath() + file_name).c_str(),
|
||||
defines,
|
||||
D3D_COMPILE_STANDARD_FILE_INCLUDE,
|
||||
"main",
|
||||
"ps_5_0",
|
||||
flags,
|
||||
0,
|
||||
shader_blob.GetAddressOf(),
|
||||
error_blob.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// Throw error if compilation failed
|
||||
if (error_blob)
|
||||
OutputDebugStringA((const char *)error_blob->GetBufferPointer());
|
||||
FatalError("Failed to compile pixel shader");
|
||||
}
|
||||
|
||||
return new PixelShaderDX12(shader_blob);
|
||||
}
|
||||
|
||||
unique_ptr<ConstantBufferDX12> RendererDX12::CreateConstantBuffer(uint inBufferSize)
|
||||
{
|
||||
return make_unique<ConstantBufferDX12>(this, inBufferSize);
|
||||
}
|
||||
|
||||
unique_ptr<PipelineState> RendererDX12::CreatePipelineState(const VertexShader *inVertexShader, const PipelineState::EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShader *inPixelShader, PipelineState::EDrawPass inDrawPass, PipelineState::EFillMode inFillMode, PipelineState::ETopology inTopology, PipelineState::EDepthTest inDepthTest, PipelineState::EBlendMode inBlendMode, PipelineState::ECullMode inCullMode)
|
||||
{
|
||||
return make_unique<PipelineStateDX12>(this, static_cast<const VertexShaderDX12 *>(inVertexShader), inInputDescription, inInputDescriptionCount, static_cast<const PixelShaderDX12 *>(inPixelShader), inDrawPass, inFillMode, inTopology, inDepthTest, inBlendMode, inCullMode);
|
||||
}
|
||||
|
||||
RenderPrimitive *RendererDX12::CreateRenderPrimitive(PipelineState::ETopology inType)
|
||||
{
|
||||
return new RenderPrimitiveDX12(this, inType);
|
||||
}
|
||||
|
||||
RenderInstances *RendererDX12::CreateRenderInstances()
|
||||
{
|
||||
return new RenderInstancesDX12(this);
|
||||
}
|
||||
|
||||
ComPtr<ID3D12Resource> RendererDX12::CreateD3DResource(D3D12_HEAP_TYPE inHeapType, D3D12_RESOURCE_STATES inResourceState, uint64 inSize)
|
||||
{
|
||||
// Create a new resource
|
||||
D3D12_RESOURCE_DESC desc;
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
desc.Alignment = 0;
|
||||
desc.Width = inSize;
|
||||
desc.Height = 1;
|
||||
desc.DepthOrArraySize = 1;
|
||||
desc.MipLevels = 1;
|
||||
desc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.SampleDesc.Quality = 0;
|
||||
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
|
||||
D3D12_HEAP_PROPERTIES heap_properties = {};
|
||||
heap_properties.Type = inHeapType;
|
||||
heap_properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
heap_properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
heap_properties.CreationNodeMask = 1;
|
||||
heap_properties.VisibleNodeMask = 1;
|
||||
|
||||
ComPtr<ID3D12Resource> resource;
|
||||
FatalErrorIfFailed(mDevice->CreateCommittedResource(&heap_properties, D3D12_HEAP_FLAG_NONE, &desc, inResourceState, nullptr, IID_PPV_ARGS(&resource)));
|
||||
return resource;
|
||||
}
|
||||
|
||||
void RendererDX12::CopyD3DResource(ID3D12Resource *inDest, const void *inSrc, uint64 inSize)
|
||||
{
|
||||
// Copy data to destination buffer
|
||||
void *data;
|
||||
D3D12_RANGE range = { 0, 0 }; // We're not going to read
|
||||
FatalErrorIfFailed(inDest->Map(0, &range, &data));
|
||||
memcpy(data, inSrc, size_t(inSize));
|
||||
inDest->Unmap(0, nullptr);
|
||||
}
|
||||
|
||||
void RendererDX12::CopyD3DResource(ID3D12Resource *inDest, ID3D12Resource *inSrc, uint64 inSize)
|
||||
{
|
||||
// Start a commandlist for the upload
|
||||
ID3D12GraphicsCommandList *list = mUploadQueue.Start();
|
||||
|
||||
// Copy the data to the GPU
|
||||
list->CopyBufferRegion(inDest, 0, inSrc, 0, inSize);
|
||||
|
||||
// Change the state of the resource to generic read
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = inDest;
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_GENERIC_READ;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
list->ResourceBarrier(1, &barrier);
|
||||
|
||||
// Wait for copying to finish
|
||||
mUploadQueue.ExecuteAndWait();
|
||||
}
|
||||
|
||||
ComPtr<ID3D12Resource> RendererDX12::CreateD3DResourceOnDefaultHeap(const void *inData, uint64 inSize)
|
||||
{
|
||||
ComPtr<ID3D12Resource> upload = CreateD3DResourceOnUploadHeap(inSize);
|
||||
ComPtr<ID3D12Resource> resource = CreateD3DResource(D3D12_HEAP_TYPE_DEFAULT, D3D12_RESOURCE_STATE_COMMON, inSize);
|
||||
CopyD3DResource(upload.Get(), inData, inSize);
|
||||
CopyD3DResource(resource.Get(), upload.Get(), inSize);
|
||||
RecycleD3DResourceOnUploadHeap(upload.Get(), inSize);
|
||||
return resource;
|
||||
}
|
||||
|
||||
ComPtr<ID3D12Resource> RendererDX12::CreateD3DResourceOnUploadHeap(uint64 inSize)
|
||||
{
|
||||
// Try cache first
|
||||
ResourceCache::iterator i = mResourceCache.find(inSize);
|
||||
if (i != mResourceCache.end() && !i->second.empty())
|
||||
{
|
||||
ComPtr<ID3D12Resource> resource = i->second.back();
|
||||
i->second.pop_back();
|
||||
return resource;
|
||||
}
|
||||
|
||||
return CreateD3DResource(D3D12_HEAP_TYPE_UPLOAD, D3D12_RESOURCE_STATE_GENERIC_READ, inSize);
|
||||
}
|
||||
|
||||
void RendererDX12::RecycleD3DResourceOnUploadHeap(ID3D12Resource *inResource, uint64 inSize)
|
||||
{
|
||||
if (!mIsExiting)
|
||||
mDelayCached[mFrameIndex][inSize].push_back(inResource);
|
||||
}
|
||||
|
||||
void RendererDX12::RecycleD3DObject(ID3D12Object *inResource)
|
||||
{
|
||||
if (!mIsExiting)
|
||||
mDelayReleased[mFrameIndex].push_back(inResource);
|
||||
}
|
||||
|
||||
#ifndef JPH_ENABLE_VULKAN
|
||||
Renderer *Renderer::sCreate()
|
||||
{
|
||||
return new RendererDX12;
|
||||
}
|
||||
#endif
|
||||
111
lib/All/JoltPhysics/TestFramework/Renderer/DX12/RendererDX12.h
Normal file
111
lib/All/JoltPhysics/TestFramework/Renderer/DX12/RendererDX12.h
Normal file
@@ -0,0 +1,111 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/UnorderedMap.h>
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Renderer/DX12/CommandQueueDX12.h>
|
||||
#include <Renderer/DX12/DescriptorHeapDX12.h>
|
||||
#include <Renderer/DX12/ConstantBufferDX12.h>
|
||||
#include <Renderer/DX12/TextureDX12.h>
|
||||
|
||||
/// DirectX 12 renderer
|
||||
class RendererDX12 : public Renderer
|
||||
{
|
||||
public:
|
||||
/// Destructor
|
||||
virtual ~RendererDX12() override;
|
||||
|
||||
// See: Renderer
|
||||
virtual void Initialize(ApplicationWindow *inWindow) override;
|
||||
virtual bool BeginFrame(const CameraState &inCamera, float inWorldScale) override;
|
||||
virtual void EndShadowPass() override;
|
||||
virtual void EndFrame() override;
|
||||
virtual void SetProjectionMode() override;
|
||||
virtual void SetOrthoMode() override;
|
||||
virtual Ref<Texture> CreateTexture(const Surface *inSurface) override;
|
||||
virtual Ref<VertexShader> CreateVertexShader(const char *inName) override;
|
||||
virtual Ref<PixelShader> CreatePixelShader(const char *inName) override;
|
||||
virtual unique_ptr<PipelineState> CreatePipelineState(const VertexShader *inVertexShader, const PipelineState::EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShader *inPixelShader, PipelineState::EDrawPass inDrawPass, PipelineState::EFillMode inFillMode, PipelineState::ETopology inTopology, PipelineState::EDepthTest inDepthTest, PipelineState::EBlendMode inBlendMode, PipelineState::ECullMode inCullMode) override;
|
||||
virtual RenderPrimitive * CreateRenderPrimitive(PipelineState::ETopology inType) override;
|
||||
virtual RenderInstances * CreateRenderInstances() override;
|
||||
virtual Texture * GetShadowMap() const override { return mShadowMap.GetPtr(); }
|
||||
virtual void OnWindowResize() override;
|
||||
|
||||
/// Create a constant buffer
|
||||
unique_ptr<ConstantBufferDX12> CreateConstantBuffer(uint inBufferSize);
|
||||
|
||||
/// Create a buffer on the default heap (usable for permanent buffers)
|
||||
ComPtr<ID3D12Resource> CreateD3DResourceOnDefaultHeap(const void *inData, uint64 inSize);
|
||||
|
||||
/// Create buffer on the upload heap (usable for temporary buffers).
|
||||
ComPtr<ID3D12Resource> CreateD3DResourceOnUploadHeap(uint64 inSize);
|
||||
|
||||
/// Recycle a buffer on the upload heap. This puts it back in a cache and will reuse it when it is certain the GPU is no longer referencing it.
|
||||
void RecycleD3DResourceOnUploadHeap(ID3D12Resource *inResource, uint64 inSize);
|
||||
|
||||
/// Keeps a reference to the resource until the current frame has finished
|
||||
void RecycleD3DObject(ID3D12Object *inResource);
|
||||
|
||||
/// Access to the most important DirectX structures
|
||||
ID3D12Device * GetDevice() { return mDevice.Get(); }
|
||||
ID3D12RootSignature * GetRootSignature() { return mRootSignature.Get(); }
|
||||
ID3D12GraphicsCommandList * GetCommandList() { JPH_ASSERT(mInFrame); return mCommandList.Get(); }
|
||||
CommandQueueDX12 & GetUploadQueue() { return mUploadQueue; }
|
||||
DescriptorHeapDX12 & GetDSVHeap() { return mDSVHeap; }
|
||||
DescriptorHeapDX12 & GetSRVHeap() { return mSRVHeap; }
|
||||
|
||||
private:
|
||||
// Wait for pending GPU work to complete
|
||||
void WaitForGpu();
|
||||
|
||||
// Create render targets and their views
|
||||
void CreateRenderTargets();
|
||||
|
||||
// Create a depth buffer for the back buffer
|
||||
void CreateDepthBuffer();
|
||||
|
||||
// Function to create a ID3D12Resource on specified heap with specified state
|
||||
ComPtr<ID3D12Resource> CreateD3DResource(D3D12_HEAP_TYPE inHeapType, D3D12_RESOURCE_STATES inResourceState, uint64 inSize);
|
||||
|
||||
// Copy CPU memory into a ID3D12Resource
|
||||
void CopyD3DResource(ID3D12Resource *inDest, const void *inSrc, uint64 inSize);
|
||||
|
||||
// Copy a CPU resource to a GPU resource
|
||||
void CopyD3DResource(ID3D12Resource *inDest, ID3D12Resource *inSrc, uint64 inSize);
|
||||
|
||||
// DirectX interfaces
|
||||
ComPtr<IDXGIFactory4> mDXGIFactory;
|
||||
ComPtr<ID3D12Device> mDevice;
|
||||
DescriptorHeapDX12 mRTVHeap; ///< Render target view heap
|
||||
DescriptorHeapDX12 mDSVHeap; ///< Depth stencil view heap
|
||||
DescriptorHeapDX12 mSRVHeap; ///< Shader resource view heap
|
||||
ComPtr<IDXGISwapChain3> mSwapChain;
|
||||
ComPtr<ID3D12Resource> mRenderTargets[cFrameCount]; ///< Two render targets (we're double buffering in order for the CPU to continue while the GPU is rendering)
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE mRenderTargetViews[cFrameCount]; ///< The two render views corresponding to the render targets
|
||||
ComPtr<ID3D12Resource> mDepthStencilBuffer; ///< The main depth buffer
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE mDepthStencilView { 0 }; ///< A view for binding the depth buffer
|
||||
ComPtr<ID3D12CommandAllocator> mCommandAllocators[cFrameCount]; ///< Two command allocator lists (one per frame)
|
||||
ComPtr<ID3D12CommandQueue> mCommandQueue; ///< The command queue that will execute commands (there's only 1 since we want to finish rendering 1 frame before moving onto the next)
|
||||
ComPtr<ID3D12GraphicsCommandList> mCommandList; ///< The command list
|
||||
ComPtr<ID3D12RootSignature> mRootSignature; ///< The root signature, we have a simple application so we only need 1, which is suitable for all our shaders
|
||||
Ref<TextureDX12> mShadowMap; ///< Used to render shadow maps
|
||||
CommandQueueDX12 mUploadQueue; ///< Queue used to upload resources to GPU memory
|
||||
unique_ptr<ConstantBufferDX12> mVertexShaderConstantBufferProjection[cFrameCount];
|
||||
unique_ptr<ConstantBufferDX12> mVertexShaderConstantBufferOrtho[cFrameCount];
|
||||
unique_ptr<ConstantBufferDX12> mPixelShaderConstantBuffer[cFrameCount];
|
||||
|
||||
// Synchronization objects used to finish rendering and swapping before reusing a command queue
|
||||
HANDLE mFenceEvent; ///< Fence event to wait for the previous frame rendering to complete (in order to free 1 of the buffers)
|
||||
ComPtr<ID3D12Fence> mFence; ///< Fence object, used to signal the end of a frame
|
||||
UINT64 mFenceValues[cFrameCount] = {}; ///< Values that were used to signal completion of one of the two frames
|
||||
|
||||
using ResourceCache = UnorderedMap<uint64, Array<ComPtr<ID3D12Resource>>>;
|
||||
|
||||
ResourceCache mResourceCache; ///< Cache items ready to be reused
|
||||
ResourceCache mDelayCached[cFrameCount]; ///< List of reusable ID3D12Resources that are potentially referenced by the GPU so can be used only when the GPU finishes
|
||||
Array<ComPtr<ID3D12Object>> mDelayReleased[cFrameCount]; ///< Objects that are potentially referenced by the GPU so can only be freed when the GPU finishes
|
||||
bool mIsExiting = false; ///< When exiting we don't want to add references too buffers
|
||||
};
|
||||
236
lib/All/JoltPhysics/TestFramework/Renderer/DX12/TextureDX12.cpp
Normal file
236
lib/All/JoltPhysics/TestFramework/Renderer/DX12/TextureDX12.cpp
Normal file
@@ -0,0 +1,236 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/DX12/TextureDX12.h>
|
||||
#include <Renderer/DX12/RendererDX12.h>
|
||||
#include <Renderer/DX12/FatalErrorIfFailedDX12.h>
|
||||
#include <Image/BlitSurface.h>
|
||||
|
||||
TextureDX12::TextureDX12(RendererDX12 *inRenderer, const Surface *inSurface) :
|
||||
Texture(inSurface->GetWidth(), inSurface->GetHeight()),
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
// Create description
|
||||
D3D12_RESOURCE_DESC desc = {};
|
||||
desc.MipLevels = 1;
|
||||
ESurfaceFormat format = inSurface->GetFormat();
|
||||
switch (format)
|
||||
{
|
||||
case ESurfaceFormat::A4L4: desc.Format = DXGI_FORMAT_R8G8_UNORM; format = ESurfaceFormat::A8L8; break;
|
||||
case ESurfaceFormat::L8: desc.Format = DXGI_FORMAT_R8_UNORM; break;
|
||||
case ESurfaceFormat::A8: desc.Format = DXGI_FORMAT_A8_UNORM; break;
|
||||
case ESurfaceFormat::A8L8: desc.Format = DXGI_FORMAT_R8G8_UNORM; break;
|
||||
case ESurfaceFormat::R5G6B5: desc.Format = DXGI_FORMAT_B5G6R5_UNORM; break;
|
||||
case ESurfaceFormat::X1R5G5B5: desc.Format = DXGI_FORMAT_B5G5R5A1_UNORM; format = ESurfaceFormat::A1R5G5B5; break;
|
||||
case ESurfaceFormat::X4R4G4B4: desc.Format = DXGI_FORMAT_B4G4R4A4_UNORM; format = ESurfaceFormat::A4R4G4B4; break;
|
||||
case ESurfaceFormat::A1R5G5B5: desc.Format = DXGI_FORMAT_B5G5R5A1_UNORM; break;
|
||||
case ESurfaceFormat::A4R4G4B4: desc.Format = DXGI_FORMAT_B4G4R4A4_UNORM; break;
|
||||
case ESurfaceFormat::R8G8B8: desc.Format = DXGI_FORMAT_B8G8R8X8_UNORM; format = ESurfaceFormat::X8R8G8B8; break;
|
||||
case ESurfaceFormat::B8G8R8: desc.Format = DXGI_FORMAT_B8G8R8X8_UNORM; format = ESurfaceFormat::X8R8G8B8; break;
|
||||
case ESurfaceFormat::X8R8G8B8: desc.Format = DXGI_FORMAT_B8G8R8X8_UNORM; break;
|
||||
case ESurfaceFormat::X8B8G8R8: desc.Format = DXGI_FORMAT_B8G8R8X8_UNORM; format = ESurfaceFormat::X8R8G8B8; break;
|
||||
case ESurfaceFormat::A8R8G8B8: desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; break;
|
||||
case ESurfaceFormat::A8B8G8R8: desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; format = ESurfaceFormat::A8R8G8B8; break;
|
||||
case ESurfaceFormat::Invalid:
|
||||
default: JPH_ASSERT(false); break;
|
||||
}
|
||||
desc.Width = mWidth;
|
||||
desc.Height = mHeight;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
desc.DepthOrArraySize = 1;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.SampleDesc.Quality = 0;
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
||||
|
||||
// Blit the surface to another temporary surface if the format changed
|
||||
const Surface *surface = inSurface;
|
||||
Ref<Surface> tmp;
|
||||
if (format != inSurface->GetFormat())
|
||||
{
|
||||
tmp = new SoftwareSurface(mWidth, mHeight, format);
|
||||
BlitSurface(inSurface, tmp);
|
||||
surface = tmp;
|
||||
}
|
||||
|
||||
// Create texture in default heap
|
||||
D3D12_HEAP_PROPERTIES heap_properties = {};
|
||||
heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
heap_properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
heap_properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
heap_properties.CreationNodeMask = 1;
|
||||
heap_properties.VisibleNodeMask = 1;
|
||||
FatalErrorIfFailed(inRenderer->GetDevice()->CreateCommittedResource(&heap_properties, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&mTexture)));
|
||||
JPH_IF_DEBUG(mTexture->SetName(L"Texture");)
|
||||
|
||||
// Determine required size of data to copy
|
||||
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint;
|
||||
UINT64 row_size_in_bytes;
|
||||
UINT64 required_size = 0;
|
||||
inRenderer->GetDevice()->GetCopyableFootprints(&desc, 0, 1, 0, &footprint, nullptr, &row_size_in_bytes, &required_size);
|
||||
|
||||
// Create the GPU upload buffer
|
||||
ComPtr<ID3D12Resource> upload_resource = mRenderer->CreateD3DResourceOnUploadHeap(required_size);
|
||||
JPH_IF_DEBUG(upload_resource->SetName(L"Texture Upload");)
|
||||
|
||||
// Copy data to upload texture
|
||||
surface->Lock(ESurfaceLockMode::Read);
|
||||
uint8 *upload_data;
|
||||
D3D12_RANGE range = { 0, 0 }; // We're not going to read
|
||||
FatalErrorIfFailed(upload_resource->Map(0, &range, (void **)&upload_data));
|
||||
for (int y = 0; y < mHeight; ++y)
|
||||
memcpy(upload_data + y * row_size_in_bytes, surface->GetData() + y * surface->GetStride(), surface->GetBytesPerPixel() * mWidth);
|
||||
upload_resource->Unmap(0, nullptr);
|
||||
surface->UnLock();
|
||||
|
||||
// Start a commandlist for the upload
|
||||
ID3D12GraphicsCommandList *list = inRenderer->GetUploadQueue().Start();
|
||||
|
||||
// Copy the texture from our upload buffer to our final texture
|
||||
D3D12_TEXTURE_COPY_LOCATION copy_dst;
|
||||
copy_dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||
copy_dst.pResource = mTexture.Get();
|
||||
copy_dst.SubresourceIndex = 0;
|
||||
D3D12_TEXTURE_COPY_LOCATION copy_src;
|
||||
copy_src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||
copy_src.pResource = upload_resource.Get();
|
||||
copy_src.PlacedFootprint = footprint;
|
||||
list->CopyTextureRegion(©_dst, 0, 0, 0, ©_src, nullptr);
|
||||
|
||||
// Indicate that the texture is now ready to be used by a pixel shader
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = mTexture.Get();
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
list->ResourceBarrier(1, &barrier);
|
||||
|
||||
// Create a SRV for the texture
|
||||
mSRV = inRenderer->GetSRVHeap().Allocate();
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = {};
|
||||
srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
srv_desc.Format = desc.Format;
|
||||
srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
srv_desc.Texture2D.MipLevels = 1;
|
||||
inRenderer->GetDevice()->CreateShaderResourceView(mTexture.Get(), &srv_desc, mSRV);
|
||||
|
||||
// Wait for copying to finish so we can destroy the upload texture
|
||||
inRenderer->GetUploadQueue().ExecuteAndWait();
|
||||
|
||||
// Recycle the upload buffer
|
||||
inRenderer->RecycleD3DResourceOnUploadHeap(upload_resource.Get(), required_size);
|
||||
}
|
||||
|
||||
TextureDX12::TextureDX12(RendererDX12 *inRenderer, int inWidth, int inHeight) :
|
||||
Texture(inWidth, inHeight),
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
// Allocate depth stencil buffer
|
||||
D3D12_CLEAR_VALUE clear_value = {};
|
||||
clear_value.Format = DXGI_FORMAT_D32_FLOAT;
|
||||
clear_value.DepthStencil.Depth = 0;
|
||||
clear_value.DepthStencil.Stencil = 0;
|
||||
|
||||
D3D12_HEAP_PROPERTIES heap_properties = {};
|
||||
heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
heap_properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
heap_properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
heap_properties.CreationNodeMask = 1;
|
||||
heap_properties.VisibleNodeMask = 1;
|
||||
|
||||
D3D12_RESOURCE_DESC depth_stencil_desc = {};
|
||||
depth_stencil_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
||||
depth_stencil_desc.Alignment = 0;
|
||||
depth_stencil_desc.Width = mWidth;
|
||||
depth_stencil_desc.Height = mHeight;
|
||||
depth_stencil_desc.DepthOrArraySize = 1;
|
||||
depth_stencil_desc.MipLevels = 1;
|
||||
depth_stencil_desc.Format = DXGI_FORMAT_D32_FLOAT;
|
||||
depth_stencil_desc.SampleDesc.Count = 1;
|
||||
depth_stencil_desc.SampleDesc.Quality = 0;
|
||||
depth_stencil_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
||||
depth_stencil_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
|
||||
|
||||
FatalErrorIfFailed(inRenderer->GetDevice()->CreateCommittedResource(&heap_properties, D3D12_HEAP_FLAG_NONE, &depth_stencil_desc, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, &clear_value, IID_PPV_ARGS(&mTexture)));
|
||||
JPH_IF_DEBUG(mTexture->SetName(L"Render Target Texture");)
|
||||
|
||||
// Create DSV for the texture
|
||||
mDSV = inRenderer->GetDSVHeap().Allocate();
|
||||
D3D12_DEPTH_STENCIL_VIEW_DESC dsv_desc = {};
|
||||
dsv_desc.Format = DXGI_FORMAT_D32_FLOAT;
|
||||
dsv_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
|
||||
dsv_desc.Flags = D3D12_DSV_FLAG_NONE;
|
||||
inRenderer->GetDevice()->CreateDepthStencilView(mTexture.Get(), &dsv_desc, mDSV);
|
||||
|
||||
// Create a SRV for the texture
|
||||
mSRV = inRenderer->GetSRVHeap().Allocate();
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = {};
|
||||
srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
srv_desc.Format = DXGI_FORMAT_R32_FLOAT;
|
||||
srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
srv_desc.Texture2D.MipLevels = 1;
|
||||
inRenderer->GetDevice()->CreateShaderResourceView(mTexture.Get(), &srv_desc, mSRV);
|
||||
}
|
||||
|
||||
TextureDX12::~TextureDX12()
|
||||
{
|
||||
if (mSRV.ptr != 0)
|
||||
mRenderer->GetSRVHeap().Free(mSRV);
|
||||
|
||||
if (mDSV.ptr != 0)
|
||||
mRenderer->GetDSVHeap().Free(mDSV);
|
||||
|
||||
if (mTexture != nullptr)
|
||||
mRenderer->RecycleD3DObject(mTexture.Get());
|
||||
}
|
||||
|
||||
void TextureDX12::Bind() const
|
||||
{
|
||||
mRenderer->GetCommandList()->SetGraphicsRootDescriptorTable(2 /* All shaders use slot 2 to bind their texture */, mRenderer->GetSRVHeap().ConvertToGPUHandle(mSRV));
|
||||
}
|
||||
|
||||
void TextureDX12::SetAsRenderTarget(bool inSet) const
|
||||
{
|
||||
if (inSet)
|
||||
{
|
||||
// Indicate make the texture ready for rendering to
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = mTexture.Get();
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_DEPTH_WRITE;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
mRenderer->GetCommandList()->ResourceBarrier(1, &barrier);
|
||||
|
||||
// Set as render target
|
||||
mRenderer->GetCommandList()->OMSetRenderTargets(0, nullptr, FALSE, &mDSV);
|
||||
|
||||
// Set view port
|
||||
D3D12_VIEWPORT viewport = { 0.0f, 0.0f, static_cast<float>(mWidth), static_cast<float>(mHeight), 0.0f, 1.0f };
|
||||
mRenderer->GetCommandList()->RSSetViewports(1, &viewport);
|
||||
|
||||
// Set scissor rect
|
||||
D3D12_RECT scissor_rect = { 0, 0, static_cast<LONG>(mWidth), static_cast<LONG>(mHeight) };
|
||||
mRenderer->GetCommandList()->RSSetScissorRects(1, &scissor_rect);
|
||||
|
||||
// Clear the render target
|
||||
mRenderer->GetCommandList()->ClearDepthStencilView(mDSV, D3D12_CLEAR_FLAG_DEPTH, 0, 0, 0, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Indicate that the texture is now ready to be used by a pixel shader
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = mTexture.Get();
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_DEPTH_WRITE;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
mRenderer->GetCommandList()->ResourceBarrier(1, &barrier);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/Texture.h>
|
||||
|
||||
class RendererDX12;
|
||||
|
||||
class TextureDX12 : public Texture
|
||||
{
|
||||
public:
|
||||
/// Constructor, called by Renderer::CreateTextureDX12
|
||||
TextureDX12(RendererDX12 *inRenderer, const Surface *inSurface); // Create a normal TextureDX12
|
||||
TextureDX12(RendererDX12 *inRenderer, int inWidth, int inHeight); // Create a render target (depth only)
|
||||
virtual ~TextureDX12() override;
|
||||
|
||||
/// Bind texture to the pixel shader
|
||||
virtual void Bind() const override;
|
||||
|
||||
/// Activate this texture as the current render target, used by RendererDX12::BeginFrame, EndShadowPass
|
||||
void SetAsRenderTarget(bool inSet) const;
|
||||
|
||||
private:
|
||||
RendererDX12 * mRenderer;
|
||||
|
||||
ComPtr<ID3D12Resource> mTexture; ///< The texture data
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE mSRV { 0 }; ///< Shader resource view to bind as texture
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE mDSV { 0 }; ///< Depth shader view to bind as render target
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/VertexShader.h>
|
||||
|
||||
/// Vertex shader handle for DirectX
|
||||
class VertexShaderDX12 : public VertexShader
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
VertexShaderDX12(ComPtr<ID3DBlob> inShader) : mShader(inShader) { }
|
||||
|
||||
ComPtr<ID3DBlob> mShader; ///< The compiled shader
|
||||
};
|
||||
511
lib/All/JoltPhysics/TestFramework/Renderer/DebugRendererImp.cpp
Normal file
511
lib/All/JoltPhysics/TestFramework/Renderer/DebugRendererImp.cpp
Normal file
@@ -0,0 +1,511 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/DebugRendererImp.h>
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Renderer/Font.h>
|
||||
|
||||
#ifndef JPH_DEBUG_RENDERER
|
||||
// Hack to still compile DebugRenderer inside the test framework when Jolt is compiled without
|
||||
#define JPH_DEBUG_RENDERER
|
||||
#include <Jolt/Renderer/DebugRenderer.cpp>
|
||||
#undef JPH_DEBUG_RENDERER
|
||||
#endif // !JPH_DEBUG_RENDERER
|
||||
|
||||
DebugRendererImp::DebugRendererImp(Renderer *inRenderer, const Font *inFont) :
|
||||
mRenderer(inRenderer),
|
||||
mFont(inFont)
|
||||
{
|
||||
// Create input layout for lines
|
||||
const PipelineState::EInputDescription line_vertex_desc[] =
|
||||
{
|
||||
PipelineState::EInputDescription::Position,
|
||||
PipelineState::EInputDescription::Color
|
||||
};
|
||||
|
||||
// Lines
|
||||
Ref<VertexShader> vtx_line = mRenderer->CreateVertexShader("LineVertexShader");
|
||||
Ref<PixelShader> pix_line = mRenderer->CreatePixelShader("LinePixelShader");
|
||||
mLineState = mRenderer->CreatePipelineState(vtx_line, line_vertex_desc, std::size(line_vertex_desc), pix_line, PipelineState::EDrawPass::Normal, PipelineState::EFillMode::Solid, PipelineState::ETopology::Line, PipelineState::EDepthTest::On, PipelineState::EBlendMode::AlphaBlend, PipelineState::ECullMode::Backface);
|
||||
|
||||
// Create input layout for triangles
|
||||
const PipelineState::EInputDescription triangles_vertex_desc[] =
|
||||
{
|
||||
PipelineState::EInputDescription::Position,
|
||||
PipelineState::EInputDescription::Normal,
|
||||
PipelineState::EInputDescription::TexCoord,
|
||||
PipelineState::EInputDescription::Color,
|
||||
PipelineState::EInputDescription::InstanceTransform,
|
||||
PipelineState::EInputDescription::InstanceInvTransform,
|
||||
PipelineState::EInputDescription::InstanceColor
|
||||
};
|
||||
|
||||
// Triangles
|
||||
Ref<VertexShader> vtx_triangle = mRenderer->CreateVertexShader("TriangleVertexShader");
|
||||
Ref<PixelShader> pix_triangle = mRenderer->CreatePixelShader("TrianglePixelShader");
|
||||
mTriangleStateBF = mRenderer->CreatePipelineState(vtx_triangle, triangles_vertex_desc, std::size(triangles_vertex_desc), pix_triangle, PipelineState::EDrawPass::Normal, PipelineState::EFillMode::Solid, PipelineState::ETopology::Triangle, PipelineState::EDepthTest::On, PipelineState::EBlendMode::AlphaBlend, PipelineState::ECullMode::Backface);
|
||||
mTriangleStateFF = mRenderer->CreatePipelineState(vtx_triangle, triangles_vertex_desc, std::size(triangles_vertex_desc), pix_triangle, PipelineState::EDrawPass::Normal, PipelineState::EFillMode::Solid, PipelineState::ETopology::Triangle, PipelineState::EDepthTest::On, PipelineState::EBlendMode::AlphaBlend, PipelineState::ECullMode::FrontFace);
|
||||
mTriangleStateWire = mRenderer->CreatePipelineState(vtx_triangle, triangles_vertex_desc, std::size(triangles_vertex_desc), pix_triangle, PipelineState::EDrawPass::Normal, PipelineState::EFillMode::Wireframe, PipelineState::ETopology::Triangle, PipelineState::EDepthTest::On, PipelineState::EBlendMode::AlphaBlend, PipelineState::ECullMode::Backface);
|
||||
|
||||
// Shadow pass
|
||||
Ref<VertexShader> vtx_shadow = mRenderer->CreateVertexShader("TriangleDepthVertexShader");
|
||||
Ref<PixelShader> pix_shadow = mRenderer->CreatePixelShader("TriangleDepthPixelShader");
|
||||
mShadowStateBF = mRenderer->CreatePipelineState(vtx_shadow, triangles_vertex_desc, std::size(triangles_vertex_desc), pix_shadow, PipelineState::EDrawPass::Shadow, PipelineState::EFillMode::Solid, PipelineState::ETopology::Triangle, PipelineState::EDepthTest::On, PipelineState::EBlendMode::AlphaBlend, PipelineState::ECullMode::Backface);
|
||||
mShadowStateFF = mRenderer->CreatePipelineState(vtx_shadow, triangles_vertex_desc, std::size(triangles_vertex_desc), pix_shadow, PipelineState::EDrawPass::Shadow, PipelineState::EFillMode::Solid, PipelineState::ETopology::Triangle, PipelineState::EDepthTest::On, PipelineState::EBlendMode::AlphaBlend, PipelineState::ECullMode::FrontFace);
|
||||
mShadowStateWire = mRenderer->CreatePipelineState(vtx_shadow, triangles_vertex_desc, std::size(triangles_vertex_desc), pix_shadow, PipelineState::EDrawPass::Shadow, PipelineState::EFillMode::Wireframe, PipelineState::ETopology::Triangle, PipelineState::EDepthTest::On, PipelineState::EBlendMode::AlphaBlend, PipelineState::ECullMode::Backface);
|
||||
|
||||
// Create instances buffer
|
||||
for (uint n = 0; n < Renderer::cFrameCount; ++n)
|
||||
mInstancesBuffer[n] = mRenderer->CreateRenderInstances();
|
||||
|
||||
// Create empty batch
|
||||
Vertex empty_vertex { Float3(0, 0, 0), Float3(1, 0, 0), Float2(0, 0), Color::sWhite };
|
||||
uint32 empty_indices[] = { 0, 0, 0 };
|
||||
mEmptyBatch = CreateTriangleBatch(&empty_vertex, 1, empty_indices, 3);
|
||||
|
||||
// Initialize base class
|
||||
DebugRenderer::Initialize();
|
||||
}
|
||||
|
||||
void DebugRendererImp::DrawLine(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor)
|
||||
{
|
||||
RVec3 offset = mRenderer->GetBaseOffset();
|
||||
|
||||
Line line;
|
||||
Vec3(inFrom - offset).StoreFloat3(&line.mFrom);
|
||||
line.mFromColor = inColor;
|
||||
Vec3(inTo - offset).StoreFloat3(&line.mTo);
|
||||
line.mToColor = inColor;
|
||||
|
||||
lock_guard lock(mLinesLock);
|
||||
mLines.push_back(line);
|
||||
}
|
||||
|
||||
DebugRenderer::Batch DebugRendererImp::CreateTriangleBatch(const Triangle *inTriangles, int inTriangleCount)
|
||||
{
|
||||
if (inTriangles == nullptr || inTriangleCount == 0)
|
||||
return mEmptyBatch;
|
||||
|
||||
RenderPrimitive *primitive = mRenderer->CreateRenderPrimitive(PipelineState::ETopology::Triangle);
|
||||
primitive->CreateVertexBuffer(3 * inTriangleCount, sizeof(Vertex), inTriangles);
|
||||
|
||||
return primitive;
|
||||
}
|
||||
|
||||
DebugRenderer::Batch DebugRendererImp::CreateTriangleBatch(const Vertex *inVertices, int inVertexCount, const uint32 *inIndices, int inIndexCount)
|
||||
{
|
||||
if (inVertices == nullptr || inVertexCount == 0 || inIndices == nullptr || inIndexCount == 0)
|
||||
return mEmptyBatch;
|
||||
|
||||
RenderPrimitive *primitive = mRenderer->CreateRenderPrimitive(PipelineState::ETopology::Triangle);
|
||||
primitive->CreateVertexBuffer(inVertexCount, sizeof(Vertex), inVertices);
|
||||
primitive->CreateIndexBuffer(inIndexCount, inIndices);
|
||||
|
||||
return primitive;
|
||||
}
|
||||
|
||||
void DebugRendererImp::DrawGeometry(RMat44Arg inModelMatrix, const AABox &inWorldSpaceBounds, float inLODScaleSq, ColorArg inModelColor, const GeometryRef &inGeometry, ECullMode inCullMode, ECastShadow inCastShadow, EDrawMode inDrawMode)
|
||||
{
|
||||
lock_guard lock(mPrimitivesLock);
|
||||
|
||||
RVec3 offset = mRenderer->GetBaseOffset();
|
||||
|
||||
Mat44 model_matrix = inModelMatrix.PostTranslated(-offset).ToMat44();
|
||||
AABox world_space_bounds = inWorldSpaceBounds;
|
||||
world_space_bounds.Translate(Vec3(-offset));
|
||||
|
||||
// Our pixel shader uses alpha only to turn on/off shadows
|
||||
Color color = inCastShadow == ECastShadow::On? Color(inModelColor, 255) : Color(inModelColor, 0);
|
||||
|
||||
if (inDrawMode == EDrawMode::Wireframe)
|
||||
{
|
||||
mWireframePrimitives[inGeometry].mInstances.push_back({ model_matrix, model_matrix.GetDirectionPreservingMatrix(), color, world_space_bounds, inLODScaleSq });
|
||||
++mNumInstances;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (inCullMode != ECullMode::CullFrontFace)
|
||||
{
|
||||
mPrimitives[inGeometry].mInstances.push_back({ model_matrix, model_matrix.GetDirectionPreservingMatrix(), color, world_space_bounds, inLODScaleSq });
|
||||
++mNumInstances;
|
||||
}
|
||||
|
||||
if (inCullMode != ECullMode::CullBackFace)
|
||||
{
|
||||
mPrimitivesBackFacing[inGeometry].mInstances.push_back({ model_matrix, model_matrix.GetDirectionPreservingMatrix(), color, world_space_bounds, inLODScaleSq });
|
||||
++mNumInstances;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebugRendererImp::FinalizePrimitive()
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
if (mLockedPrimitive != nullptr)
|
||||
{
|
||||
// Unlock the primitive
|
||||
mLockedPrimitive->UnlockVertexBuffer();
|
||||
|
||||
// Set number of indices to draw
|
||||
mLockedPrimitive->SetNumVtxToDraw(int(mLockedVertices - mLockedVerticesStart));
|
||||
|
||||
// Add to draw list
|
||||
mTempPrimitives[new Geometry(mLockedPrimitive.GetPtr(), mLockedPrimitiveBounds)].mInstances.push_back({ Mat44::sIdentity(), Mat44::sIdentity(), Color::sWhite, mLockedPrimitiveBounds, 1.0f });
|
||||
++mNumInstances;
|
||||
|
||||
// Clear pointers
|
||||
mLockedPrimitive = nullptr;
|
||||
mLockedVerticesStart = nullptr;
|
||||
mLockedVertices = nullptr;
|
||||
mLockedVerticesEnd = nullptr;
|
||||
mLockedPrimitiveBounds = AABox();
|
||||
}
|
||||
}
|
||||
|
||||
void DebugRendererImp::EnsurePrimitiveSpace(int inVtxSize)
|
||||
{
|
||||
const int cVertexBufferSize = 10240;
|
||||
|
||||
if (mLockedPrimitive == nullptr
|
||||
|| mLockedVerticesEnd - mLockedVertices < inVtxSize)
|
||||
{
|
||||
FinalizePrimitive();
|
||||
|
||||
// Create new
|
||||
mLockedPrimitive = mRenderer->CreateRenderPrimitive(PipelineState::ETopology::Triangle);
|
||||
mLockedPrimitive->CreateVertexBuffer(cVertexBufferSize, sizeof(Vertex));
|
||||
|
||||
// Lock buffers
|
||||
mLockedVerticesStart = mLockedVertices = (Vertex *)mLockedPrimitive->LockVertexBuffer();
|
||||
mLockedVerticesEnd = mLockedVertices + cVertexBufferSize;
|
||||
}
|
||||
}
|
||||
|
||||
void DebugRendererImp::DrawTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor, ECastShadow inCastShadow)
|
||||
{
|
||||
RVec3 offset = mRenderer->GetBaseOffset();
|
||||
|
||||
Vec3 v1(inV1 - offset);
|
||||
Vec3 v2(inV2 - offset);
|
||||
Vec3 v3(inV3 - offset);
|
||||
|
||||
lock_guard lock(mPrimitivesLock);
|
||||
|
||||
EnsurePrimitiveSpace(3);
|
||||
|
||||
// Set alpha to zero if we don't want to cast shadows to notify the pixel shader
|
||||
Color color(inColor, inCastShadow == ECastShadow::Off? 0 : 0xff);
|
||||
|
||||
// Construct triangle in separate buffer and then copy it to the target memory block (may be uncached memory)
|
||||
Triangle triangle(v1, v2, v3, color);
|
||||
*(Triangle *)mLockedVertices = triangle;
|
||||
mLockedVertices += 3;
|
||||
|
||||
// Update bounding box
|
||||
mLockedPrimitiveBounds.Encapsulate(v1);
|
||||
mLockedPrimitiveBounds.Encapsulate(v2);
|
||||
mLockedPrimitiveBounds.Encapsulate(v3);
|
||||
}
|
||||
|
||||
void DebugRendererImp::DrawInstances(const Geometry *inGeometry, const Array<int> &inStartIdx)
|
||||
{
|
||||
RenderInstances *instances_buffer = mInstancesBuffer[mRenderer->GetCurrentFrameIndex()];
|
||||
|
||||
if (!inStartIdx.empty())
|
||||
{
|
||||
// Get LODs
|
||||
const Array<LOD> &geometry_lods = inGeometry->mLODs;
|
||||
|
||||
// Write instances for all LODS
|
||||
int next_start_idx = inStartIdx.front();
|
||||
for (size_t lod = 0; lod < geometry_lods.size(); ++lod)
|
||||
{
|
||||
int start_idx = next_start_idx;
|
||||
next_start_idx = inStartIdx[lod + 1];
|
||||
int num_instances = next_start_idx - start_idx;
|
||||
instances_buffer->Draw(static_cast<RenderPrimitive *>(geometry_lods[lod].mTriangleBatch.GetPtr()), start_idx, num_instances);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebugRendererImp::DrawText3D(RVec3Arg inPosition, const string_view &inString, ColorArg inColor, float inHeight)
|
||||
{
|
||||
RVec3 offset = mRenderer->GetBaseOffset();
|
||||
|
||||
Vec3 pos(inPosition - offset);
|
||||
|
||||
lock_guard lock(mTextsLock);
|
||||
mTexts.emplace_back(pos, inString, inColor, inHeight);
|
||||
}
|
||||
|
||||
void DebugRendererImp::DrawLines()
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
lock_guard lock(mLinesLock);
|
||||
|
||||
// Draw the lines
|
||||
if (!mLines.empty())
|
||||
{
|
||||
Ref<RenderPrimitive> primitive = mRenderer->CreateRenderPrimitive(PipelineState::ETopology::Line);
|
||||
primitive->CreateVertexBuffer((int)mLines.size() * 2, sizeof(Line) / 2);
|
||||
void *data = primitive->LockVertexBuffer();
|
||||
memcpy(data, &mLines[0], mLines.size() * sizeof(Line));
|
||||
primitive->UnlockVertexBuffer();
|
||||
mLineState->Activate();
|
||||
primitive->Draw();
|
||||
}
|
||||
}
|
||||
|
||||
void DebugRendererImp::DrawShadowPass()
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
lock_guard lock(mPrimitivesLock);
|
||||
|
||||
// Finish the last primitive
|
||||
FinalizePrimitive();
|
||||
|
||||
// Get the camera and light frustum for culling
|
||||
Vec3 camera_pos(mRenderer->GetCameraState().mPos - mRenderer->GetBaseOffset());
|
||||
const Frustum &camera_frustum = mRenderer->GetCameraFrustum();
|
||||
const Frustum &light_frustum = mRenderer->GetLightFrustum();
|
||||
|
||||
// Resize instances buffer and copy all visible instance data into it
|
||||
if (mNumInstances > 0)
|
||||
{
|
||||
// Create instances buffer
|
||||
RenderInstances *instances_buffer = mInstancesBuffer[mRenderer->GetCurrentFrameIndex()];
|
||||
instances_buffer->CreateBuffer(2 * mNumInstances, sizeof(Instance));
|
||||
Instance *dst_instance = reinterpret_cast<Instance *>(instances_buffer->Lock());
|
||||
|
||||
// Next write index
|
||||
int dst_index = 0;
|
||||
|
||||
// This keeps track of which instances use which lod, first array: 0 = light pass, 1 = geometry pass
|
||||
Array<Array<int>> lod_indices[2];
|
||||
|
||||
for (InstanceMap *primitive_map : { &mPrimitives, &mTempPrimitives, &mPrimitivesBackFacing, &mWireframePrimitives })
|
||||
for (InstanceMap::value_type &v : *primitive_map)
|
||||
{
|
||||
// Get LODs
|
||||
const Array<LOD> &geometry_lods = v.first->mLODs;
|
||||
size_t num_lods = geometry_lods.size();
|
||||
JPH_ASSERT(num_lods > 0);
|
||||
|
||||
// Ensure that our lod index array is big enough (to avoid reallocating memory too often)
|
||||
if (lod_indices[0].size() < num_lods)
|
||||
lod_indices[0].resize(num_lods);
|
||||
if (lod_indices[1].size() < num_lods)
|
||||
lod_indices[1].resize(num_lods);
|
||||
|
||||
// Iterate over all instances
|
||||
const Array<InstanceWithLODInfo> &instances = v.second.mInstances;
|
||||
for (size_t i = 0; i < instances.size(); ++i)
|
||||
{
|
||||
const InstanceWithLODInfo &src_instance = instances[i];
|
||||
|
||||
// Check if it overlaps with the light or camera frustum
|
||||
bool light_overlaps = light_frustum.Overlaps(src_instance.mWorldSpaceBounds);
|
||||
bool camera_overlaps = camera_frustum.Overlaps(src_instance.mWorldSpaceBounds);
|
||||
if (light_overlaps || camera_overlaps)
|
||||
{
|
||||
// Figure out which LOD to use
|
||||
const LOD &lod = v.first->GetLOD(camera_pos, src_instance.mWorldSpaceBounds, src_instance.mLODScaleSq);
|
||||
size_t lod_index = &lod - geometry_lods.data();
|
||||
|
||||
// Store which index goes in which LOD
|
||||
if (light_overlaps)
|
||||
lod_indices[0][lod_index].push_back((int)i);
|
||||
if (camera_overlaps)
|
||||
lod_indices[1][lod_index].push_back((int)i);
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over both passes: 0 = light, 1 = geometry
|
||||
Array<int> *start_idx[] = { &v.second.mLightStartIdx, &v.second.mGeometryStartIdx };
|
||||
for (int type = 0; type < 2; ++type)
|
||||
{
|
||||
// Reserve space for instance indices
|
||||
Array<int> &type_start_idx = *start_idx[type];
|
||||
type_start_idx.resize(num_lods + 1);
|
||||
|
||||
// Write out geometry pass instances
|
||||
for (size_t lod = 0; lod < num_lods; ++lod)
|
||||
{
|
||||
// Write start index for this LOD
|
||||
type_start_idx[lod] = dst_index;
|
||||
|
||||
// Copy instances
|
||||
Array<int> &this_lod_indices = lod_indices[type][lod];
|
||||
for (int i : this_lod_indices)
|
||||
{
|
||||
const Instance &src_instance = instances[i];
|
||||
dst_instance[dst_index++] = src_instance;
|
||||
}
|
||||
|
||||
// Prepare for next iteration (will preserve memory)
|
||||
this_lod_indices.clear();
|
||||
}
|
||||
|
||||
// Write out end of last LOD
|
||||
type_start_idx.back() = dst_index;
|
||||
}
|
||||
}
|
||||
|
||||
instances_buffer->Unlock();
|
||||
}
|
||||
|
||||
if (!mPrimitives.empty() || !mTempPrimitives.empty())
|
||||
{
|
||||
// Front face culling, we want to render the back side of the geometry for casting shadows
|
||||
mShadowStateFF->Activate();
|
||||
|
||||
// Draw all primitives as seen from the light
|
||||
if (mNumInstances > 0)
|
||||
for (InstanceMap::value_type &v : mPrimitives)
|
||||
DrawInstances(v.first, v.second.mLightStartIdx);
|
||||
for (InstanceMap::value_type &v : mTempPrimitives)
|
||||
DrawInstances(v.first, v.second.mLightStartIdx);
|
||||
}
|
||||
|
||||
if (!mPrimitivesBackFacing.empty())
|
||||
{
|
||||
// Back face culling, we want to render the front side of back facing geometry
|
||||
mShadowStateBF->Activate();
|
||||
|
||||
// Draw all primitives as seen from the light
|
||||
for (InstanceMap::value_type &v : mPrimitivesBackFacing)
|
||||
DrawInstances(v.first, v.second.mLightStartIdx);
|
||||
}
|
||||
|
||||
if (!mWireframePrimitives.empty())
|
||||
{
|
||||
// Switch to wireframe mode
|
||||
mShadowStateWire->Activate();
|
||||
|
||||
// Draw all wireframe primitives as seen from the light
|
||||
for (InstanceMap::value_type &v : mWireframePrimitives)
|
||||
DrawInstances(v.first, v.second.mLightStartIdx);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugRendererImp::DrawTriangles()
|
||||
{
|
||||
// Bind the shadow map texture
|
||||
mRenderer->GetShadowMap()->Bind();
|
||||
|
||||
if (!mPrimitives.empty() || !mTempPrimitives.empty())
|
||||
{
|
||||
// Bind the normal shader, back face culling
|
||||
mTriangleStateBF->Activate();
|
||||
|
||||
// Draw all primitives
|
||||
if (mNumInstances > 0)
|
||||
for (InstanceMap::value_type &v : mPrimitives)
|
||||
DrawInstances(v.first, v.second.mGeometryStartIdx);
|
||||
for (InstanceMap::value_type &v : mTempPrimitives)
|
||||
DrawInstances(v.first, v.second.mGeometryStartIdx);
|
||||
}
|
||||
|
||||
if (!mPrimitivesBackFacing.empty())
|
||||
{
|
||||
// Front face culling, the next batch needs to render inside out
|
||||
mTriangleStateFF->Activate();
|
||||
|
||||
// Draw all back primitives
|
||||
for (InstanceMap::value_type &v : mPrimitivesBackFacing)
|
||||
DrawInstances(v.first, v.second.mGeometryStartIdx);
|
||||
}
|
||||
|
||||
if (!mWireframePrimitives.empty())
|
||||
{
|
||||
// Wire frame mode
|
||||
mTriangleStateWire->Activate();
|
||||
|
||||
// Draw all wireframe primitives
|
||||
for (InstanceMap::value_type &v : mWireframePrimitives)
|
||||
DrawInstances(v.first, v.second.mGeometryStartIdx);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugRendererImp::DrawTexts()
|
||||
{
|
||||
lock_guard lock(mTextsLock);
|
||||
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
const CameraState &camera_state = mRenderer->GetCameraState();
|
||||
|
||||
for (const Text &t : mTexts)
|
||||
{
|
||||
Vec3 forward = camera_state.mForward;
|
||||
Vec3 right = forward.Cross(camera_state.mUp).Normalized();
|
||||
Vec3 up = right.Cross(forward).Normalized();
|
||||
Mat44 transform(Vec4(right, 0), Vec4(up, 0), Vec4(forward, 0), Vec4(t.mPosition, 1));
|
||||
|
||||
mFont->DrawText3D(transform * Mat44::sScale(t.mHeight), t.mText, t.mColor);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugRendererImp::Draw()
|
||||
{
|
||||
DrawLines();
|
||||
DrawTriangles();
|
||||
DrawTexts();
|
||||
}
|
||||
|
||||
void DebugRendererImp::ClearLines()
|
||||
{
|
||||
lock_guard lock(mLinesLock);
|
||||
mLines.clear();
|
||||
}
|
||||
|
||||
void DebugRendererImp::ClearMap(InstanceMap &ioInstances)
|
||||
{
|
||||
Array<GeometryRef> to_delete;
|
||||
|
||||
for (InstanceMap::value_type &kv : ioInstances)
|
||||
{
|
||||
if (kv.second.mInstances.empty())
|
||||
to_delete.push_back(kv.first);
|
||||
else
|
||||
kv.second.mInstances.clear();
|
||||
}
|
||||
|
||||
for (GeometryRef &b : to_delete)
|
||||
ioInstances.erase(b);
|
||||
}
|
||||
|
||||
void DebugRendererImp::ClearTriangles()
|
||||
{
|
||||
lock_guard lock(mPrimitivesLock);
|
||||
|
||||
// Close any primitive that's being built
|
||||
FinalizePrimitive();
|
||||
|
||||
// Move primitives to draw back to the free list
|
||||
ClearMap(mWireframePrimitives);
|
||||
ClearMap(mPrimitives);
|
||||
mTempPrimitives.clear(); // These are created by FinalizePrimitive() and need to be cleared every frame
|
||||
ClearMap(mPrimitivesBackFacing);
|
||||
mNumInstances = 0;
|
||||
}
|
||||
|
||||
void DebugRendererImp::ClearTexts()
|
||||
{
|
||||
lock_guard lock(mTextsLock);
|
||||
mTexts.clear();
|
||||
}
|
||||
|
||||
void DebugRendererImp::Clear()
|
||||
{
|
||||
ClearLines();
|
||||
ClearTriangles();
|
||||
ClearTexts();
|
||||
NextFrame();
|
||||
}
|
||||
178
lib/All/JoltPhysics/TestFramework/Renderer/DebugRendererImp.h
Normal file
178
lib/All/JoltPhysics/TestFramework/Renderer/DebugRendererImp.h
Normal file
@@ -0,0 +1,178 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef JPH_DEBUG_RENDERER
|
||||
#include <Jolt/Renderer/DebugRenderer.h>
|
||||
#else
|
||||
// Hack to still compile DebugRenderer inside the test framework when Jolt is compiled without
|
||||
#define JPH_DEBUG_RENDERER
|
||||
// Make sure the debug renderer symbols don't get imported or exported
|
||||
#define JPH_DEBUG_RENDERER_EXPORT
|
||||
#include <Jolt/Renderer/DebugRenderer.h>
|
||||
#undef JPH_DEBUG_RENDERER
|
||||
#undef JPH_DEBUG_RENDERER_EXPORT
|
||||
#endif
|
||||
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Jolt/Core/Mutex.h>
|
||||
#include <Jolt/Core/UnorderedMap.h>
|
||||
|
||||
class Renderer;
|
||||
class Font;
|
||||
|
||||
/// Implementation of DebugRenderer
|
||||
class DebugRendererImp final : public DebugRenderer
|
||||
{
|
||||
public:
|
||||
JPH_OVERRIDE_NEW_DELETE
|
||||
|
||||
/// Constructor
|
||||
DebugRendererImp(Renderer *inRenderer, const Font *inFont);
|
||||
|
||||
/// Implementation of DebugRenderer interface
|
||||
virtual void DrawLine(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor) override;
|
||||
virtual void DrawTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::Off) override;
|
||||
virtual Batch CreateTriangleBatch(const Triangle *inTriangles, int inTriangleCount) override;
|
||||
virtual Batch CreateTriangleBatch(const Vertex *inVertices, int inVertexCount, const uint32 *inIndices, int inIndexCount) override;
|
||||
virtual void DrawGeometry(RMat44Arg inModelMatrix, const AABox &inWorldSpaceBounds, float inLODScaleSq, ColorArg inModelColor, const GeometryRef &inGeometry, ECullMode inCullMode, ECastShadow inCastShadow, EDrawMode inDrawMode) override;
|
||||
virtual void DrawText3D(RVec3Arg inPosition, const string_view &inString, ColorArg inColor, float inHeight) override;
|
||||
|
||||
/// Draw all primitives from the light source
|
||||
void DrawShadowPass();
|
||||
|
||||
/// Draw all primitives that were added
|
||||
void Draw();
|
||||
|
||||
/// Clear all primitives (to be called after drawing)
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
/// Helper functions to draw sub parts
|
||||
void DrawLines();
|
||||
void DrawTriangles();
|
||||
void DrawTexts();
|
||||
|
||||
/// Helper functions to clear sub parts
|
||||
void ClearLines();
|
||||
void ClearTriangles();
|
||||
void ClearTexts();
|
||||
|
||||
/// Finalize the current locked primitive and add it to the primitives to draw
|
||||
void FinalizePrimitive();
|
||||
|
||||
/// Ensure that the current locked primitive has space for a primitive consisting inVtxSize vertices
|
||||
void EnsurePrimitiveSpace(int inVtxSize);
|
||||
|
||||
Renderer * mRenderer;
|
||||
|
||||
/// Shaders for triangles
|
||||
unique_ptr<PipelineState> mTriangleStateBF;
|
||||
unique_ptr<PipelineState> mTriangleStateFF;
|
||||
unique_ptr<PipelineState> mTriangleStateWire;
|
||||
|
||||
/// Shaders for shadow pass for triangles
|
||||
unique_ptr<PipelineState> mShadowStateBF;
|
||||
unique_ptr<PipelineState> mShadowStateFF;
|
||||
unique_ptr<PipelineState> mShadowStateWire;
|
||||
|
||||
/// Lock that protects the triangle batches from being accessed from multiple threads
|
||||
Mutex mPrimitivesLock;
|
||||
|
||||
Batch mEmptyBatch;
|
||||
|
||||
/// Properties for a single rendered instance
|
||||
struct Instance
|
||||
{
|
||||
/// Constructor
|
||||
Instance(Mat44Arg inModelMatrix, Mat44Arg inModelMatrixInvTrans, ColorArg inModelColor) : mModelMatrix(inModelMatrix), mModelMatrixInvTrans(inModelMatrixInvTrans), mModelColor(inModelColor) { }
|
||||
|
||||
Mat44 mModelMatrix;
|
||||
Mat44 mModelMatrixInvTrans;
|
||||
Color mModelColor;
|
||||
};
|
||||
|
||||
/// Rendered instance with added information for lodding
|
||||
struct InstanceWithLODInfo : public Instance
|
||||
{
|
||||
/// Constructor
|
||||
InstanceWithLODInfo(Mat44Arg inModelMatrix, Mat44Arg inModelMatrixInvTrans, ColorArg inModelColor, const AABox &inWorldSpaceBounds, float inLODScaleSq) : Instance(inModelMatrix, inModelMatrixInvTrans, inModelColor), mWorldSpaceBounds(inWorldSpaceBounds), mLODScaleSq(inLODScaleSq) { }
|
||||
|
||||
/// Bounding box for culling
|
||||
AABox mWorldSpaceBounds;
|
||||
|
||||
/// Square of scale factor for LODding (1 = original, > 1 = lod out further, < 1 = lod out earlier)
|
||||
float mLODScaleSq;
|
||||
};
|
||||
|
||||
/// Properties for a batch of instances that have the same primitive
|
||||
struct Instances
|
||||
{
|
||||
Array<InstanceWithLODInfo> mInstances;
|
||||
|
||||
/// Start index in mInstancesBuffer for each of the LOD in the geometry pass. Length is one longer than the number of LODs to indicate how many instances the last lod has.
|
||||
Array<int> mGeometryStartIdx;
|
||||
|
||||
/// Start index in mInstancesBuffer for each of the LOD in the light pass. Length is one longer than the number of LODs to indicate how many instances the last lod has.
|
||||
Array<int> mLightStartIdx;
|
||||
};
|
||||
|
||||
using InstanceMap = UnorderedMap<GeometryRef, Instances>;
|
||||
|
||||
/// Clear map of instances and make it ready for the next frame
|
||||
void ClearMap(InstanceMap &ioInstances);
|
||||
|
||||
/// Helper function to draw instances
|
||||
inline void DrawInstances(const Geometry *inGeometry, const Array<int> &inStartIdx);
|
||||
|
||||
/// List of primitives that are finished and ready for drawing
|
||||
InstanceMap mWireframePrimitives;
|
||||
InstanceMap mPrimitives;
|
||||
InstanceMap mTempPrimitives;
|
||||
InstanceMap mPrimitivesBackFacing;
|
||||
int mNumInstances = 0;
|
||||
Ref<RenderInstances> mInstancesBuffer[Renderer::cFrameCount];
|
||||
|
||||
/// Primitive that is being built + its properties
|
||||
Ref<RenderPrimitive> mLockedPrimitive;
|
||||
Vertex * mLockedVerticesStart = nullptr;
|
||||
Vertex * mLockedVertices = nullptr;
|
||||
Vertex * mLockedVerticesEnd = nullptr;
|
||||
AABox mLockedPrimitiveBounds;
|
||||
|
||||
/// A single text string
|
||||
struct Text
|
||||
{
|
||||
Text(Vec3Arg inPosition, const string_view &inText, ColorArg inColor, float inHeight) : mPosition(inPosition), mText(inText), mColor(inColor), mHeight(inHeight) { }
|
||||
|
||||
Vec3 mPosition;
|
||||
String mText;
|
||||
Color mColor;
|
||||
float mHeight;
|
||||
};
|
||||
|
||||
/// All text strings that are to be drawn on screen
|
||||
Array<Text> mTexts;
|
||||
Mutex mTextsLock;
|
||||
|
||||
/// Font with which to draw the texts
|
||||
RefConst<Font> mFont;
|
||||
|
||||
/// A single line segment
|
||||
struct Line
|
||||
{
|
||||
Float3 mFrom;
|
||||
Color mFromColor;
|
||||
Float3 mTo;
|
||||
Color mToColor;
|
||||
};
|
||||
|
||||
/// The list of line segments
|
||||
Array<Line> mLines;
|
||||
Mutex mLinesLock;
|
||||
|
||||
/// The shaders for the line segments
|
||||
unique_ptr<PipelineState> mLineState;
|
||||
};
|
||||
349
lib/All/JoltPhysics/TestFramework/Renderer/Font.cpp
Normal file
349
lib/All/JoltPhysics/TestFramework/Renderer/Font.cpp
Normal file
@@ -0,0 +1,349 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/Font.h>
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Image/Surface.h>
|
||||
#include <Utils/ReadData.h>
|
||||
#include <Jolt/Core/Profiler.h>
|
||||
#include <Jolt/Core/ScopeExit.h>
|
||||
|
||||
JPH_SUPPRESS_WARNINGS_STD_BEGIN
|
||||
JPH_CLANG_SUPPRESS_WARNING("-Wreserved-identifier")
|
||||
JPH_CLANG_SUPPRESS_WARNING("-Wzero-as-null-pointer-constant")
|
||||
JPH_CLANG_SUPPRESS_WARNING("-Wcast-qual")
|
||||
JPH_CLANG_SUPPRESS_WARNING("-Wimplicit-fallthrough")
|
||||
JPH_CLANG_SUPPRESS_WARNING("-Wcomma")
|
||||
JPH_CLANG_SUPPRESS_WARNING("-Wdouble-promotion")
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#include <External/stb_truetype.h>
|
||||
JPH_SUPPRESS_WARNINGS_STD_END
|
||||
|
||||
Font::Font(Renderer *inRenderer) :
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
}
|
||||
|
||||
bool Font::Create(const char *inFontName, int inCharHeight)
|
||||
{
|
||||
JPH_PROFILE("Create");
|
||||
|
||||
// Initialize
|
||||
mFontName = inFontName;
|
||||
mCharHeight = inCharHeight;
|
||||
mHorizontalTexels = 64;
|
||||
mVerticalTexels = 64;
|
||||
|
||||
constexpr int cSpacingH = 2; // Number of pixels to put horizontally between characters
|
||||
constexpr int cSpacingV = 2; // Number of pixels to put vertically between characters
|
||||
|
||||
// Read font data
|
||||
Array<uint8> font_data = ReadData((String("Fonts/") + inFontName + ".ttf").c_str());
|
||||
|
||||
// Construct a font info
|
||||
stbtt_fontinfo font;
|
||||
if (!stbtt_InitFont(&font, font_data.data(), stbtt_GetFontOffsetForIndex(font_data.data(), 0)))
|
||||
return false;
|
||||
|
||||
// Get the base line for the font
|
||||
float scale = stbtt_ScaleForPixelHeight(&font, float(mCharHeight));
|
||||
int ascent;
|
||||
stbtt_GetFontVMetrics(&font, &ascent, nullptr, nullptr);
|
||||
int baseline = int(ascent * scale);
|
||||
|
||||
// Create surface for characters
|
||||
Ref<SoftwareSurface> surface = new SoftwareSurface(mHorizontalTexels, mVerticalTexels, ESurfaceFormat::L8);
|
||||
surface->Clear();
|
||||
surface->Lock(ESurfaceLockMode::Write);
|
||||
|
||||
// Draw all printable characters
|
||||
try_again:;
|
||||
int x = 0, y = 0;
|
||||
static_assert(cBeginChar == ' ', "We skip space in the for loop below");
|
||||
for (int c = cBeginChar + 1; c < cEndChar; ++c)
|
||||
{
|
||||
// Get index in the arrays
|
||||
int idx = c - cBeginChar;
|
||||
|
||||
int w, h, xoff, yoff;
|
||||
unsigned char *bitmap = stbtt_GetCodepointBitmap(&font, 0, scale, c, &w, &h, &xoff, &yoff);
|
||||
JPH_SCOPE_EXIT([bitmap]{ STBTT_free(bitmap, nullptr); });
|
||||
yoff = baseline + yoff;
|
||||
|
||||
// Check if there is room on this line
|
||||
if (int(x + xoff + w + cSpacingH) > mHorizontalTexels)
|
||||
{
|
||||
// Next line
|
||||
x = 0;
|
||||
y += mCharHeight + cSpacingV;
|
||||
|
||||
// Check if character fits
|
||||
if (y + mCharHeight + cSpacingV > mVerticalTexels)
|
||||
{
|
||||
// Character doesn't fit, enlarge surface
|
||||
if (mHorizontalTexels < 2 * mVerticalTexels)
|
||||
mHorizontalTexels <<= 1;
|
||||
else
|
||||
mVerticalTexels <<= 1;
|
||||
|
||||
// Create new surface
|
||||
surface->UnLock();
|
||||
surface = new SoftwareSurface(mHorizontalTexels, mVerticalTexels, ESurfaceFormat::L8);
|
||||
surface->Clear();
|
||||
surface->Lock(ESurfaceLockMode::Write);
|
||||
|
||||
// Try again with the larger texture
|
||||
goto try_again;
|
||||
}
|
||||
}
|
||||
|
||||
// Get location of character in font surface
|
||||
JPH_ASSERT(x >= 0 && x <= 0xffff);
|
||||
JPH_ASSERT(y >= 0 && y <= 0xffff);
|
||||
JPH_ASSERT(w <= 0xff);
|
||||
mStartU[idx] = uint16(x);
|
||||
mStartV[idx] = uint16(y);
|
||||
mWidth[idx] = uint8(w + 1);
|
||||
|
||||
// Copy the character data
|
||||
for (int y2 = 0; y2 < h; ++y2)
|
||||
{
|
||||
uint8 *src = bitmap + y2 * w;
|
||||
uint8 *dst = surface->GetScanLine(y + yoff + y2) + x + xoff;
|
||||
memcpy(dst, src, w);
|
||||
}
|
||||
|
||||
// Go to the next character
|
||||
x += w + cSpacingH;
|
||||
}
|
||||
|
||||
// Calculate spacing between characters
|
||||
for (int idx1 = 0; idx1 < cNumChars; ++idx1)
|
||||
for (int idx2 = 0; idx2 < cNumChars; ++idx2)
|
||||
{
|
||||
int c1 = cBeginChar + idx1;
|
||||
int c2 = cBeginChar + idx2;
|
||||
|
||||
int advance;
|
||||
stbtt_GetCodepointHMetrics(&font, c1, &advance, nullptr);
|
||||
int spacing = Clamp(int(scale * (advance + stbtt_GetCodepointKernAdvance(&font, c1, c2))), 0, 0xff);
|
||||
mSpacing[idx1][idx2] = (uint8)spacing;
|
||||
}
|
||||
|
||||
// Unlock surface
|
||||
surface->UnLock();
|
||||
|
||||
// Create input layout
|
||||
const PipelineState::EInputDescription vertex_desc[] =
|
||||
{
|
||||
PipelineState::EInputDescription::Position,
|
||||
PipelineState::EInputDescription::TexCoord,
|
||||
PipelineState::EInputDescription::Color
|
||||
};
|
||||
|
||||
// Load vertex shader
|
||||
Ref<VertexShader> vtx = mRenderer->CreateVertexShader("FontVertexShader");
|
||||
|
||||
// Load pixel shader
|
||||
Ref<PixelShader> pix = mRenderer->CreatePixelShader("FontPixelShader");
|
||||
|
||||
mPipelineState = mRenderer->CreatePipelineState(vtx, vertex_desc, std::size(vertex_desc), pix, PipelineState::EDrawPass::Normal, PipelineState::EFillMode::Solid, PipelineState::ETopology::Triangle, PipelineState::EDepthTest::Off, PipelineState::EBlendMode::AlphaBlend, PipelineState::ECullMode::Backface);
|
||||
|
||||
// Create texture
|
||||
mTexture = mRenderer->CreateTexture(surface);
|
||||
|
||||
// Trace success
|
||||
Trace("Created font \"%s\" with height %d in a %dx%d surface", mFontName.c_str(), mCharHeight, mHorizontalTexels, mVerticalTexels);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Float2 Font::MeasureText(const string_view &inText) const
|
||||
{
|
||||
JPH_PROFILE("MeasureText");
|
||||
|
||||
Float2 extents(0, 1.0f);
|
||||
|
||||
// Current raster position
|
||||
float x = 0;
|
||||
|
||||
// Loop through string
|
||||
for (uint i = 0; i < inText.size(); ++i)
|
||||
{
|
||||
// Get character
|
||||
int ch = inText[i];
|
||||
|
||||
// Create character if it is printable
|
||||
static_assert(cBeginChar == ' ', "We skip space in the for loop below");
|
||||
if (ch > cBeginChar && ch < cEndChar)
|
||||
{
|
||||
// Update extents
|
||||
int c1 = ch - cBeginChar;
|
||||
extents.x = max(extents.x, x + float(mWidth[c1]) / mCharHeight);
|
||||
}
|
||||
|
||||
// Go to next (x, y) location
|
||||
if (ch == '\n')
|
||||
{
|
||||
// Next line
|
||||
x = 0;
|
||||
extents.y += 1.0f;
|
||||
}
|
||||
else if (i + 1 < inText.size())
|
||||
{
|
||||
// Do spacing between the two characters
|
||||
int c1 = ch - cBeginChar;
|
||||
int c2 = inText[i + 1] - cBeginChar;
|
||||
|
||||
if (c1 >= 0 && c1 < cNumChars && c2 >= 0 && c2 < cNumChars)
|
||||
x += float(mSpacing[c1][c2]) / mCharHeight;
|
||||
}
|
||||
}
|
||||
|
||||
return extents;
|
||||
}
|
||||
|
||||
bool Font::CreateString(Mat44Arg inTransform, const string_view &inText, ColorArg inColor, RenderPrimitive &ioPrimitive) const
|
||||
{
|
||||
JPH_PROFILE("CreateString");
|
||||
|
||||
// Reset primitive
|
||||
ioPrimitive.Clear();
|
||||
|
||||
// Count the number of printable chars
|
||||
int printable = 0;
|
||||
for (uint i = 0; i < inText.size(); ++i)
|
||||
{
|
||||
int ch = inText[i];
|
||||
static_assert(cBeginChar == ' ', "We skip space in the for loop below");
|
||||
if (ch > cBeginChar && ch < cEndChar) // Space is not printable
|
||||
printable++;
|
||||
}
|
||||
if (printable == 0)
|
||||
return false;
|
||||
|
||||
// Get correction factor for texture size
|
||||
float texel_to_u = 1.0f / mHorizontalTexels;
|
||||
float texel_to_v = 1.0f / mVerticalTexels;
|
||||
|
||||
int vtx_size = printable * 4;
|
||||
int idx_size = printable * 6;
|
||||
ioPrimitive.CreateVertexBuffer(vtx_size, sizeof(FontVertex));
|
||||
ioPrimitive.CreateIndexBuffer(idx_size);
|
||||
|
||||
// Current vertex
|
||||
uint32 vtx = 0;
|
||||
|
||||
// Lock buffers
|
||||
FontVertex *font_vtx = (FontVertex *)ioPrimitive.LockVertexBuffer();
|
||||
uint32 *idx_start = ioPrimitive.LockIndexBuffer();
|
||||
uint32 *idx = idx_start;
|
||||
|
||||
// Current raster position
|
||||
float x = 0, y = -1.0f;
|
||||
|
||||
// Loop through string
|
||||
for (uint i = 0; i < inText.size(); ++i)
|
||||
{
|
||||
// Get character
|
||||
int ch = inText[i];
|
||||
|
||||
// Create character if it is printable
|
||||
static_assert(cBeginChar == ' ', "We skip space in the for loop below");
|
||||
if (ch > cBeginChar && ch < cEndChar)
|
||||
{
|
||||
// Get index for character
|
||||
int c1 = ch - cBeginChar;
|
||||
|
||||
// Create indices
|
||||
*idx = vtx;
|
||||
++idx;
|
||||
*idx = vtx + 3;
|
||||
++idx;
|
||||
*idx = vtx + 1;
|
||||
++idx;
|
||||
*idx = vtx;
|
||||
++idx;
|
||||
*idx = vtx + 2;
|
||||
++idx;
|
||||
*idx = vtx + 3;
|
||||
++idx;
|
||||
vtx += 4;
|
||||
|
||||
// Get properties of this character
|
||||
Float2 uv_start(texel_to_u * mStartU[c1], texel_to_v * mStartV[c1]);
|
||||
Float2 uv_end(texel_to_u * (mStartU[c1] + mWidth[c1]), texel_to_v * (mStartV[c1] + mCharHeight));
|
||||
Float2 xy_end(x + float(mWidth[c1]) / mCharHeight, y + 1.0f);
|
||||
|
||||
// Create vertices
|
||||
(inTransform * Vec3(x, y, 0)).StoreFloat3(&font_vtx->mPosition);
|
||||
font_vtx->mColor = inColor;
|
||||
font_vtx->mTexCoord = Float2(uv_start.x, uv_end.y);
|
||||
++font_vtx;
|
||||
|
||||
(inTransform * Vec3(x, xy_end.y, 0)).StoreFloat3(&font_vtx->mPosition);
|
||||
font_vtx->mColor = inColor;
|
||||
font_vtx->mTexCoord = uv_start;
|
||||
++font_vtx;
|
||||
|
||||
(inTransform * Vec3(xy_end.x, y, 0)).StoreFloat3(&font_vtx->mPosition);
|
||||
font_vtx->mColor = inColor;
|
||||
font_vtx->mTexCoord = uv_end;
|
||||
++font_vtx;
|
||||
|
||||
(inTransform * Vec3(xy_end.x, xy_end.y, 0)).StoreFloat3(&font_vtx->mPosition);
|
||||
font_vtx->mColor = inColor;
|
||||
font_vtx->mTexCoord = Float2(uv_end.x, uv_start.y);
|
||||
++font_vtx;
|
||||
}
|
||||
|
||||
// Go to next (x, y) location
|
||||
if (ch == '\n')
|
||||
{
|
||||
// Next line
|
||||
x = 0.0f;
|
||||
y -= 1.0f;
|
||||
}
|
||||
else if (i + 1 < inText.size())
|
||||
{
|
||||
// Do spacing between the two characters
|
||||
int c1 = ch - cBeginChar;
|
||||
int c2 = inText[i + 1] - cBeginChar;
|
||||
|
||||
if (c1 >= 0 && c1 < cNumChars && c2 >= 0 && c2 < cNumChars)
|
||||
x += float(mSpacing[c1][c2]) / mCharHeight;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that we completely filled the output buffer
|
||||
JPH_ASSERT(vtx == (uint32)vtx_size);
|
||||
JPH_ASSERT(idx == idx_start + idx_size);
|
||||
|
||||
// Unlock buffers
|
||||
ioPrimitive.UnlockVertexBuffer();
|
||||
ioPrimitive.UnlockIndexBuffer();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Font::DrawText3D(Mat44Arg inTransform, const string_view &inText, ColorArg inColor) const
|
||||
{
|
||||
JPH_PROFILE("DrawText3D");
|
||||
|
||||
// Early out
|
||||
if (inText.empty())
|
||||
return;
|
||||
|
||||
Ref<RenderPrimitive> primitive = mRenderer->CreateRenderPrimitive(PipelineState::ETopology::Triangle);
|
||||
if (CreateString(inTransform, inText, inColor, *primitive))
|
||||
{
|
||||
mTexture->Bind();
|
||||
|
||||
mPipelineState->Activate();
|
||||
|
||||
primitive->Draw();
|
||||
}
|
||||
}
|
||||
70
lib/All/JoltPhysics/TestFramework/Renderer/Font.h
Normal file
70
lib/All/JoltPhysics/TestFramework/Renderer/Font.h
Normal file
@@ -0,0 +1,70 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
#include <Jolt/Core/Color.h>
|
||||
#include <Jolt/Math/Float2.h>
|
||||
#include <Renderer/RenderPrimitive.h>
|
||||
#include <Renderer/Texture.h>
|
||||
#include <Renderer/PipelineState.h>
|
||||
#include <memory>
|
||||
|
||||
class Renderer;
|
||||
class Texture;
|
||||
|
||||
/// Font class, used to display text in 3D mode. Does variable width fonts with kerning. Font names are identical to the Windows font names.
|
||||
class Font : public RefTarget<Font>
|
||||
{
|
||||
public:
|
||||
/// Constants
|
||||
static const int cBeginChar = ' '; ///< First character that is drawable in the character set
|
||||
static const int cEndChar = 256; ///< Last character + 1 that is drawable in the character set
|
||||
static const int cNumChars = cEndChar - cBeginChar; ///< Number of drawable characters in the character set
|
||||
|
||||
/// Constructor
|
||||
Font(Renderer *inRenderer);
|
||||
|
||||
/// Create a font
|
||||
bool Create(const char *inFontName, int inCharHeight);
|
||||
|
||||
/// Properties
|
||||
const String & GetFontName() const { return mFontName; }
|
||||
int GetCharHeight() const { return mCharHeight; }
|
||||
|
||||
/// Get extents of a string, assuming the height of the text is 1 and with the normal aspect ratio of the font
|
||||
Float2 MeasureText(const string_view &inText) const;
|
||||
|
||||
/// Draw a string at a specific location
|
||||
/// If the string is drawn with the identity matrix, it's top left will start at (0, 0, 0)
|
||||
/// The text width is in the X direction and the text height is in the Y direction and it will have a height of 1
|
||||
void DrawText3D(Mat44Arg inTransform, const string_view &inText, ColorArg inColor = Color::sWhite) const;
|
||||
|
||||
private:
|
||||
/// Create a primitive for a string
|
||||
bool CreateString(Mat44Arg inTransform, const string_view &inText, ColorArg inColor, RenderPrimitive &ioPrimitive) const;
|
||||
|
||||
struct FontVertex
|
||||
{
|
||||
Float3 mPosition;
|
||||
Float2 mTexCoord;
|
||||
Color mColor;
|
||||
};
|
||||
|
||||
/// Properties of the font
|
||||
String mFontName; ///< Name of the font
|
||||
int mCharHeight; ///< Height of a character
|
||||
int mHorizontalTexels; ///< Number of texels horizontally, determines the scale of mStartU, mWidth and mSpacing
|
||||
int mVerticalTexels; ///< Number of texels vertically, determines the scale of mStartV
|
||||
uint16 mStartU[cNumChars] = {}; ///< Start U in texels
|
||||
uint16 mStartV[cNumChars] = {}; ///< Start V in texels
|
||||
uint8 mWidth[cNumChars] = {}; ///< Width of character in texels
|
||||
uint8 mSpacing[cNumChars][cNumChars] = {}; ///< Spacing between characters in texels
|
||||
|
||||
/// Structures used for drawing
|
||||
Renderer * mRenderer; ///< Our renderer
|
||||
Ref<Texture> mTexture; ///< The texture containing all characters
|
||||
unique_ptr<PipelineState> mPipelineState; ///< The state used to render characters
|
||||
};
|
||||
57
lib/All/JoltPhysics/TestFramework/Renderer/Frustum.h
Normal file
57
lib/All/JoltPhysics/TestFramework/Renderer/Frustum.h
Normal file
@@ -0,0 +1,57 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Geometry/Plane.h>
|
||||
#include <Jolt/Geometry/AABox.h>
|
||||
|
||||
/// A camera frustum containing of 6 planes (left, right, top, bottom, near, far) pointing inwards
|
||||
class [[nodiscard]] Frustum
|
||||
{
|
||||
public:
|
||||
/// Empty constructor
|
||||
Frustum() = default;
|
||||
|
||||
/// Construct frustum from position, forward, up, field of view x and y and near plane.
|
||||
/// Note that inUp does not need to be perpendicular to inForward but cannot be collinear.
|
||||
inline Frustum(Vec3Arg inPosition, Vec3Arg inForward, Vec3Arg inUp, float inFOVX, float inFOVY, float inNear)
|
||||
{
|
||||
Vec3 right = inForward.Cross(inUp).Normalized();
|
||||
Vec3 up = right.Cross(inForward).Normalized(); // Calculate the real up vector (inUp does not need to be perpendicular to inForward)
|
||||
|
||||
// Near plane
|
||||
mPlanes[0] = Plane::sFromPointAndNormal(inPosition + inNear * inForward, inForward);
|
||||
|
||||
// Top and bottom planes
|
||||
mPlanes[1] = Plane::sFromPointAndNormal(inPosition, Mat44::sRotation(right, 0.5f * inFOVY) * -up);
|
||||
mPlanes[2] = Plane::sFromPointAndNormal(inPosition, Mat44::sRotation(right, -0.5f * inFOVY) * up);
|
||||
|
||||
// Left and right planes
|
||||
mPlanes[3] = Plane::sFromPointAndNormal(inPosition, Mat44::sRotation(up, 0.5f * inFOVX) * right);
|
||||
mPlanes[4] = Plane::sFromPointAndNormal(inPosition, Mat44::sRotation(up, -0.5f * inFOVX) * -right);
|
||||
}
|
||||
|
||||
/// Test if frustum overlaps with axis aligned box. Note that this is a conservative estimate and can return true if the
|
||||
/// frustum doesn't actually overlap with the box. This is because we only test the plane axis as separating axis
|
||||
/// and skip checking the cross products of the edges of the frustum
|
||||
inline bool Overlaps(const AABox &inBox) const
|
||||
{
|
||||
// Loop over all frustum planes
|
||||
for (const Plane &p : mPlanes)
|
||||
{
|
||||
// Get support point (the maximum extent) in the direction of our normal
|
||||
Vec3 support = inBox.GetSupport(p.GetNormal());
|
||||
|
||||
// If this is behind our plane, the box is not inside the frustum
|
||||
if (p.SignedDistance(support) < 0.0f)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Plane mPlanes[5]; ///< Planes forming the frustum
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
/// Convert Metal error to readable text and alert
|
||||
void FatalErrorIfFailed(NSError *inResult);
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/MTL/FatalErrorIfFailedMTL.h>
|
||||
#include <Utils/Log.h>
|
||||
|
||||
void FatalErrorIfFailed(NSError *inResult)
|
||||
{
|
||||
if (inResult != nullptr)
|
||||
FatalError("Metal error returned: %s", [[inResult localizedDescription] cStringUsingEncoding: NSUTF8StringEncoding]);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/PipelineState.h>
|
||||
#include <Renderer/MTL/VertexShaderMTL.h>
|
||||
#include <Renderer/MTL/PixelShaderMTL.h>
|
||||
|
||||
class RendererMTL;
|
||||
|
||||
/// Metal pipeline state object
|
||||
class PipelineStateMTL : public PipelineState
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
PipelineStateMTL(RendererMTL *inRenderer, const VertexShaderMTL *inVertexShader, const EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShaderMTL *inPixelShader, EDrawPass inDrawPass, EFillMode inFillMode, ETopology inTopology, EDepthTest inDepthTest, EBlendMode inBlendMode, ECullMode inCullMode);
|
||||
virtual ~PipelineStateMTL() override;
|
||||
|
||||
/// Make this pipeline state active (any primitives rendered after this will use this state)
|
||||
virtual void Activate() override;
|
||||
|
||||
private:
|
||||
RendererMTL * mRenderer;
|
||||
RefConst<VertexShaderMTL> mVertexShader;
|
||||
RefConst<PixelShaderMTL> mPixelShader;
|
||||
id<MTLRenderPipelineState> mPipelineState;
|
||||
id<MTLDepthStencilState> mDepthState;
|
||||
MTLCullMode mCullMode;
|
||||
MTLTriangleFillMode mFillMode;
|
||||
};
|
||||
@@ -0,0 +1,163 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/MTL/PipelineStateMTL.h>
|
||||
#include <Renderer/MTL/RendererMTL.h>
|
||||
#include <Renderer/MTL/FatalErrorIfFailedMTL.h>
|
||||
|
||||
PipelineStateMTL::PipelineStateMTL(RendererMTL *inRenderer, const VertexShaderMTL *inVertexShader, const EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShaderMTL *inPixelShader, EDrawPass inDrawPass, EFillMode inFillMode, ETopology inTopology, EDepthTest inDepthTest, EBlendMode inBlendMode, ECullMode inCullMode) :
|
||||
mRenderer(inRenderer),
|
||||
mVertexShader(inVertexShader),
|
||||
mPixelShader(inPixelShader)
|
||||
{
|
||||
// Create a vertex descriptor
|
||||
MTLVertexDescriptor *vertex_descriptor = [[MTLVertexDescriptor alloc] init];
|
||||
uint vertex_offset = 0;
|
||||
uint instance_offset = 0, instance_alignment = 4;
|
||||
uint index = 0;
|
||||
for (uint i = 0; i < inInputDescriptionCount; ++i)
|
||||
switch (inInputDescription[i])
|
||||
{
|
||||
case EInputDescription::Position:
|
||||
case EInputDescription::Normal:
|
||||
vertex_descriptor.attributes[index].format = MTLVertexFormatFloat3;
|
||||
vertex_descriptor.attributes[index].offset = vertex_offset;
|
||||
vertex_descriptor.attributes[index].bufferIndex = 0;
|
||||
vertex_offset += 3 * sizeof(float);
|
||||
++index;
|
||||
break;
|
||||
|
||||
case EInputDescription::Color:
|
||||
vertex_descriptor.attributes[index].format = MTLVertexFormatUChar4;
|
||||
vertex_descriptor.attributes[index].offset = vertex_offset;
|
||||
vertex_descriptor.attributes[index].bufferIndex = 0;
|
||||
vertex_offset += 4 * sizeof(uint8);
|
||||
++index;
|
||||
break;
|
||||
|
||||
case EInputDescription::TexCoord:
|
||||
vertex_descriptor.attributes[index].format = MTLVertexFormatFloat2;
|
||||
vertex_descriptor.attributes[index].offset = vertex_offset;
|
||||
vertex_descriptor.attributes[index].bufferIndex = 0;
|
||||
vertex_offset += 2 * sizeof(float);
|
||||
++index;
|
||||
break;
|
||||
|
||||
case EInputDescription::InstanceColor:
|
||||
vertex_descriptor.attributes[index].format = MTLVertexFormatUChar4;
|
||||
vertex_descriptor.attributes[index].offset = instance_offset;
|
||||
vertex_descriptor.attributes[index].bufferIndex = 1;
|
||||
instance_offset += 4 * sizeof(uint8);
|
||||
++index;
|
||||
break;
|
||||
|
||||
case EInputDescription::InstanceTransform:
|
||||
case EInputDescription::InstanceInvTransform:
|
||||
instance_alignment = max(instance_alignment, 16u);
|
||||
instance_offset = AlignUp(instance_offset, 16u);
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
vertex_descriptor.attributes[index].format = MTLVertexFormatFloat4;
|
||||
vertex_descriptor.attributes[index].offset = instance_offset;
|
||||
vertex_descriptor.attributes[index].bufferIndex = 1;
|
||||
instance_offset += 4 * sizeof(float);
|
||||
++index;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Configure layouts
|
||||
vertex_descriptor.layouts[0].stride = vertex_offset;
|
||||
vertex_descriptor.layouts[0].stepRate = 1;
|
||||
vertex_descriptor.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
|
||||
|
||||
if (instance_offset > 0)
|
||||
{
|
||||
vertex_descriptor.layouts[1].stride = AlignUp(instance_offset, instance_alignment);
|
||||
vertex_descriptor.layouts[1].stepRate = 1;
|
||||
vertex_descriptor.layouts[1].stepFunction = MTLVertexStepFunctionPerInstance;
|
||||
}
|
||||
|
||||
// Create the pipeline descriptor
|
||||
MTLRenderPipelineDescriptor *descriptor = [[MTLRenderPipelineDescriptor alloc] init];
|
||||
descriptor.vertexFunction = inVertexShader->GetFunction();
|
||||
descriptor.fragmentFunction = inPixelShader->GetFunction();
|
||||
descriptor.vertexDescriptor = vertex_descriptor;
|
||||
switch (inDrawPass)
|
||||
{
|
||||
case EDrawPass::Shadow:
|
||||
descriptor.depthAttachmentPixelFormat = static_cast<TextureMTL *>(mRenderer->GetShadowMap())->GetTexture().pixelFormat;
|
||||
break;
|
||||
|
||||
case EDrawPass::Normal:
|
||||
descriptor.colorAttachments[0].pixelFormat = mRenderer->GetView().colorPixelFormat;
|
||||
switch (inBlendMode)
|
||||
{
|
||||
case EBlendMode::Write:
|
||||
descriptor.colorAttachments[0].blendingEnabled = NO;
|
||||
break;
|
||||
|
||||
case EBlendMode::AlphaBlend:
|
||||
descriptor.colorAttachments[0].blendingEnabled = YES;
|
||||
descriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
|
||||
descriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||
descriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
|
||||
descriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorZero;
|
||||
descriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorZero;
|
||||
descriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
|
||||
break;
|
||||
}
|
||||
descriptor.depthAttachmentPixelFormat = mRenderer->GetView().depthStencilPixelFormat;
|
||||
}
|
||||
|
||||
NSError *error = nullptr;
|
||||
mPipelineState = [mRenderer->GetDevice() newRenderPipelineStateWithDescriptor: descriptor error: &error];
|
||||
FatalErrorIfFailed(error);
|
||||
[descriptor release];
|
||||
[vertex_descriptor release];
|
||||
|
||||
// Create depth descriptor
|
||||
MTLDepthStencilDescriptor *depth_descriptor = [[MTLDepthStencilDescriptor new] init];
|
||||
if (inDepthTest == EDepthTest::On)
|
||||
{
|
||||
depth_descriptor.depthCompareFunction = MTLCompareFunctionGreaterEqual;
|
||||
depth_descriptor.depthWriteEnabled = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
depth_descriptor.depthCompareFunction = MTLCompareFunctionAlways;
|
||||
depth_descriptor.depthWriteEnabled = NO;
|
||||
}
|
||||
mDepthState = [mRenderer->GetDevice() newDepthStencilStateWithDescriptor: depth_descriptor];
|
||||
[depth_descriptor release];
|
||||
|
||||
// Determine cull mode
|
||||
if (inCullMode == ECullMode::FrontFace)
|
||||
mCullMode = MTLCullModeFront;
|
||||
else
|
||||
mCullMode = MTLCullModeBack;
|
||||
|
||||
// Determine fill mode
|
||||
if (inFillMode == EFillMode::Solid)
|
||||
mFillMode = MTLTriangleFillModeFill;
|
||||
else
|
||||
mFillMode = MTLTriangleFillModeLines;
|
||||
}
|
||||
|
||||
PipelineStateMTL::~PipelineStateMTL()
|
||||
{
|
||||
[mPipelineState release];
|
||||
[mDepthState release];
|
||||
}
|
||||
|
||||
void PipelineStateMTL::Activate()
|
||||
{
|
||||
id<MTLRenderCommandEncoder> encoder = mRenderer->GetRenderEncoder();
|
||||
[encoder setRenderPipelineState: mPipelineState];
|
||||
[encoder setDepthStencilState: mDepthState];
|
||||
[encoder setCullMode: mCullMode];
|
||||
[encoder setTriangleFillMode: mFillMode];
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/PixelShader.h>
|
||||
|
||||
#include <MetalKit/MetalKit.h>
|
||||
|
||||
/// Pixel shader handle for Metal
|
||||
class PixelShaderMTL : public PixelShader
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
PixelShaderMTL(id<MTLFunction> inFunction) : mFunction(inFunction) { }
|
||||
virtual ~PixelShaderMTL() override { [mFunction release]; }
|
||||
|
||||
/// Access to the function
|
||||
id<MTLFunction> GetFunction() const { return mFunction; }
|
||||
|
||||
private:
|
||||
id<MTLFunction> mFunction;
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/MTL/RendererMTL.h>
|
||||
#include <Renderer/RenderInstances.h>
|
||||
|
||||
class RenderPrimitive;
|
||||
|
||||
/// Metal implementation of a render instances object
|
||||
class RenderInstancesMTL : public RenderInstances
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
RenderInstancesMTL(RendererMTL *inRenderer) : mRenderer(inRenderer) { }
|
||||
virtual ~RenderInstancesMTL() override { Clear(); }
|
||||
|
||||
/// Erase all instance data
|
||||
virtual void Clear() override;
|
||||
|
||||
/// Instance buffer management functions
|
||||
virtual void CreateBuffer(int inNumInstances, int inInstanceSize) override;
|
||||
virtual void * Lock() override;
|
||||
virtual void Unlock() override;
|
||||
|
||||
/// Draw the instances when context has been set by Renderer::BindShader
|
||||
virtual void Draw(RenderPrimitive *inPrimitive, int inStartInstance, int inNumInstances) const override;
|
||||
|
||||
private:
|
||||
RendererMTL * mRenderer;
|
||||
id<MTLBuffer> mBuffer;
|
||||
NSUInteger mBufferSize;
|
||||
NSUInteger mInstanceSize;
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/MTL/RenderInstancesMTL.h>
|
||||
#include <Renderer/MTL/RenderPrimitiveMTL.h>
|
||||
|
||||
void RenderInstancesMTL::Clear()
|
||||
{
|
||||
[mBuffer release];
|
||||
mBuffer = nil;
|
||||
}
|
||||
|
||||
void RenderInstancesMTL::CreateBuffer(int inNumInstances, int inInstanceSize)
|
||||
{
|
||||
mInstanceSize = NSUInteger(inInstanceSize);
|
||||
NSUInteger size = mInstanceSize * inNumInstances;
|
||||
if (mBuffer == nullptr || mBufferSize < size)
|
||||
{
|
||||
Clear();
|
||||
|
||||
mBuffer = [mRenderer->GetView().device newBufferWithLength: size options: MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeShared | MTLResourceHazardTrackingModeTracked];
|
||||
mBufferSize = size;
|
||||
}
|
||||
}
|
||||
|
||||
void *RenderInstancesMTL::Lock()
|
||||
{
|
||||
return mBuffer.contents;
|
||||
}
|
||||
|
||||
void RenderInstancesMTL::Unlock()
|
||||
{
|
||||
}
|
||||
|
||||
void RenderInstancesMTL::Draw(RenderPrimitive *inPrimitive, int inStartInstance, int inNumInstances) const
|
||||
{
|
||||
if (inNumInstances <= 0)
|
||||
return;
|
||||
|
||||
id<MTLRenderCommandEncoder> encoder = mRenderer->GetRenderEncoder();
|
||||
RenderPrimitiveMTL *prim = static_cast<RenderPrimitiveMTL *>(inPrimitive);
|
||||
|
||||
[encoder setVertexBuffer: prim->mVertexBuffer offset: 0 atIndex: 0];
|
||||
[encoder setVertexBuffer: mBuffer offset: mInstanceSize * inStartInstance atIndex: 1];
|
||||
if (prim->mIndexBuffer == nil)
|
||||
[encoder drawPrimitives: prim->mPrimitiveType vertexStart: 0 vertexCount: prim->mNumVtxToDraw instanceCount: inNumInstances];
|
||||
else
|
||||
[encoder drawIndexedPrimitives: prim->mPrimitiveType indexCount: prim->mNumIdxToDraw indexType: MTLIndexTypeUInt32 indexBuffer: prim->mIndexBuffer indexBufferOffset: 0 instanceCount: inNumInstances];
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/RenderPrimitive.h>
|
||||
#include <Renderer/MTL/RendererMTL.h>
|
||||
|
||||
/// Metal implementation of a render primitive
|
||||
class RenderPrimitiveMTL : public RenderPrimitive
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
RenderPrimitiveMTL(RendererMTL *inRenderer, MTLPrimitiveType inType) : mRenderer(inRenderer), mPrimitiveType(inType) { }
|
||||
virtual ~RenderPrimitiveMTL() override { Clear(); }
|
||||
|
||||
/// Vertex buffer management functions
|
||||
virtual void CreateVertexBuffer(int inNumVtx, int inVtxSize, const void *inData = nullptr) override;
|
||||
virtual void ReleaseVertexBuffer() override;
|
||||
virtual void * LockVertexBuffer() override;
|
||||
virtual void UnlockVertexBuffer() override;
|
||||
|
||||
/// Index buffer management functions
|
||||
virtual void CreateIndexBuffer(int inNumIdx, const uint32 *inData = nullptr) override;
|
||||
virtual void ReleaseIndexBuffer() override;
|
||||
virtual uint32 * LockIndexBuffer() override;
|
||||
virtual void UnlockIndexBuffer() override;
|
||||
|
||||
/// Draw the primitive
|
||||
virtual void Draw() const override;
|
||||
|
||||
private:
|
||||
friend class RenderInstancesMTL;
|
||||
|
||||
RendererMTL * mRenderer;
|
||||
MTLPrimitiveType mPrimitiveType;
|
||||
id<MTLBuffer> mVertexBuffer;
|
||||
id<MTLBuffer> mIndexBuffer;
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/MTL/RenderPrimitiveMTL.h>
|
||||
|
||||
void RenderPrimitiveMTL::ReleaseVertexBuffer()
|
||||
{
|
||||
[mVertexBuffer release];
|
||||
mVertexBuffer = nil;
|
||||
|
||||
RenderPrimitive::ReleaseVertexBuffer();
|
||||
}
|
||||
|
||||
void RenderPrimitiveMTL::ReleaseIndexBuffer()
|
||||
{
|
||||
[mIndexBuffer release];
|
||||
mIndexBuffer = nil;
|
||||
|
||||
RenderPrimitive::ReleaseIndexBuffer();
|
||||
}
|
||||
|
||||
void RenderPrimitiveMTL::CreateVertexBuffer(int inNumVtx, int inVtxSize, const void *inData)
|
||||
{
|
||||
RenderPrimitive::CreateVertexBuffer(inNumVtx, inVtxSize, inData);
|
||||
|
||||
NSUInteger size = NSUInteger(inNumVtx) * inVtxSize;
|
||||
if (inData != nullptr)
|
||||
mVertexBuffer = [mRenderer->GetDevice() newBufferWithBytes: inData length: size options: MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeShared | MTLResourceHazardTrackingModeTracked];
|
||||
else
|
||||
mVertexBuffer = [mRenderer->GetDevice() newBufferWithLength: size options: MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeShared | MTLResourceHazardTrackingModeTracked];
|
||||
}
|
||||
|
||||
void *RenderPrimitiveMTL::LockVertexBuffer()
|
||||
{
|
||||
return mVertexBuffer.contents;
|
||||
}
|
||||
|
||||
void RenderPrimitiveMTL::UnlockVertexBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
void RenderPrimitiveMTL::CreateIndexBuffer(int inNumIdx, const uint32 *inData)
|
||||
{
|
||||
RenderPrimitive::CreateIndexBuffer(inNumIdx, inData);
|
||||
|
||||
NSUInteger size = NSUInteger(inNumIdx) * sizeof(uint32);
|
||||
if (inData != nullptr)
|
||||
mIndexBuffer = [mRenderer->GetDevice() newBufferWithBytes: inData length: size options: MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeManaged | MTLResourceHazardTrackingModeTracked];
|
||||
else
|
||||
mIndexBuffer = [mRenderer->GetDevice() newBufferWithLength: size options: MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeShared | MTLResourceHazardTrackingModeTracked];
|
||||
}
|
||||
|
||||
uint32 *RenderPrimitiveMTL::LockIndexBuffer()
|
||||
{
|
||||
return (uint32 *)mIndexBuffer.contents;
|
||||
}
|
||||
|
||||
void RenderPrimitiveMTL::UnlockIndexBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
void RenderPrimitiveMTL::Draw() const
|
||||
{
|
||||
id<MTLRenderCommandEncoder> encoder = mRenderer->GetRenderEncoder();
|
||||
|
||||
[encoder setVertexBuffer: mVertexBuffer offset: 0 atIndex: 0];
|
||||
if (mIndexBuffer == nil)
|
||||
[encoder drawPrimitives: mPrimitiveType vertexStart: 0 vertexCount: mNumVtxToDraw];
|
||||
else
|
||||
[encoder drawIndexedPrimitives: mPrimitiveType indexCount: mNumIdxToDraw indexType: MTLIndexTypeUInt32 indexBuffer: mIndexBuffer indexBufferOffset: 0];
|
||||
}
|
||||
46
lib/All/JoltPhysics/TestFramework/Renderer/MTL/RendererMTL.h
Normal file
46
lib/All/JoltPhysics/TestFramework/Renderer/MTL/RendererMTL.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Renderer/MTL/TextureMTL.h>
|
||||
|
||||
#include <MetalKit/MetalKit.h>
|
||||
|
||||
/// Metal renderer
|
||||
class RendererMTL : public Renderer
|
||||
{
|
||||
public:
|
||||
virtual ~RendererMTL() override;
|
||||
|
||||
// See: Renderer
|
||||
virtual void Initialize(ApplicationWindow *inWindow) override;
|
||||
virtual bool BeginFrame(const CameraState &inCamera, float inWorldScale) override;
|
||||
virtual void EndShadowPass() override;
|
||||
virtual void EndFrame() override;
|
||||
virtual void SetProjectionMode() override;
|
||||
virtual void SetOrthoMode() override;
|
||||
virtual Ref<Texture> CreateTexture(const Surface *inSurface) override;
|
||||
virtual Ref<VertexShader> CreateVertexShader(const char *inName) override;
|
||||
virtual Ref<PixelShader> CreatePixelShader(const char *inName) override;
|
||||
virtual unique_ptr<PipelineState> CreatePipelineState(const VertexShader *inVertexShader, const PipelineState::EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShader *inPixelShader, PipelineState::EDrawPass inDrawPass, PipelineState::EFillMode inFillMode, PipelineState::ETopology inTopology, PipelineState::EDepthTest inDepthTest, PipelineState::EBlendMode inBlendMode, PipelineState::ECullMode inCullMode) override;
|
||||
virtual RenderPrimitive * CreateRenderPrimitive(PipelineState::ETopology inType) override;
|
||||
virtual RenderInstances * CreateRenderInstances() override;
|
||||
virtual Texture * GetShadowMap() const override { return mShadowMap; }
|
||||
virtual void OnWindowResize() override { }
|
||||
|
||||
MTKView * GetView() const { return mView; }
|
||||
id<MTLDevice> GetDevice() const { return mView.device; }
|
||||
id<MTLRenderCommandEncoder> GetRenderEncoder() const { return mRenderEncoder; }
|
||||
|
||||
private:
|
||||
MTKView * mView;
|
||||
MTLRenderPassDescriptor * mShadowRenderPass;
|
||||
Ref<TextureMTL> mShadowMap;
|
||||
id<MTLLibrary> mShaderLibrary;
|
||||
id<MTLCommandQueue> mCommandQueue;
|
||||
id<MTLCommandBuffer> mCommandBuffer;
|
||||
id<MTLRenderCommandEncoder> mRenderEncoder;
|
||||
};
|
||||
184
lib/All/JoltPhysics/TestFramework/Renderer/MTL/RendererMTL.mm
Normal file
184
lib/All/JoltPhysics/TestFramework/Renderer/MTL/RendererMTL.mm
Normal file
@@ -0,0 +1,184 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/MTL/RendererMTL.h>
|
||||
#include <Renderer/MTL/RenderPrimitiveMTL.h>
|
||||
#include <Renderer/MTL/RenderInstancesMTL.h>
|
||||
#include <Renderer/MTL/PipelineStateMTL.h>
|
||||
#include <Renderer/MTL/VertexShaderMTL.h>
|
||||
#include <Renderer/MTL/PixelShaderMTL.h>
|
||||
#include <Renderer/MTL/TextureMTL.h>
|
||||
#include <Renderer/MTL/FatalErrorIfFailedMTL.h>
|
||||
#include <Window/ApplicationWindowMacOS.h>
|
||||
#include <Utils/Log.h>
|
||||
#include <Utils/AssetStream.h>
|
||||
#include <Jolt/Core/Profiler.h>
|
||||
|
||||
RendererMTL::~RendererMTL()
|
||||
{
|
||||
[mCommandQueue release];
|
||||
[mShadowRenderPass release];
|
||||
[mShaderLibrary release];
|
||||
}
|
||||
|
||||
void RendererMTL::Initialize(ApplicationWindow *inWindow)
|
||||
{
|
||||
Renderer::Initialize(inWindow);
|
||||
|
||||
mView = static_cast<ApplicationWindowMacOS *>(inWindow)->GetMetalView();
|
||||
|
||||
id<MTLDevice> device = GetDevice();
|
||||
|
||||
// Load the shader library containing all shaders for the test framework
|
||||
NSError *error = nullptr;
|
||||
NSURL *url = [NSURL URLWithString: [NSString stringWithCString: (AssetStream::sGetAssetsBasePath() + "Shaders/MTL/Shaders.metallib").c_str() encoding: NSUTF8StringEncoding]];
|
||||
mShaderLibrary = [device newLibraryWithURL: url error: &error];
|
||||
FatalErrorIfFailed(error);
|
||||
|
||||
// Create depth only texture (no color buffer, as seen from light)
|
||||
mShadowMap = new TextureMTL(this, cShadowMapSize, cShadowMapSize);
|
||||
|
||||
// Create render pass descriptor for shadow pass
|
||||
mShadowRenderPass = [[MTLRenderPassDescriptor alloc] init];
|
||||
mShadowRenderPass.depthAttachment.texture = mShadowMap->GetTexture();
|
||||
mShadowRenderPass.depthAttachment.loadAction = MTLLoadActionClear;
|
||||
mShadowRenderPass.depthAttachment.storeAction = MTLStoreActionStore;
|
||||
mShadowRenderPass.depthAttachment.clearDepth = 0.0f;
|
||||
|
||||
// Create the command queue
|
||||
mCommandQueue = [device newCommandQueue];
|
||||
}
|
||||
|
||||
bool RendererMTL::BeginFrame(const CameraState &inCamera, float inWorldScale)
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
Renderer::BeginFrame(inCamera, inWorldScale);
|
||||
|
||||
// Update frame index
|
||||
mFrameIndex = (mFrameIndex + 1) % cFrameCount;
|
||||
|
||||
// Create a new command buffer
|
||||
mCommandBuffer = [mCommandQueue commandBuffer];
|
||||
|
||||
// Create shadow render encoder
|
||||
mRenderEncoder = [mCommandBuffer renderCommandEncoderWithDescriptor: mShadowRenderPass];
|
||||
|
||||
// Set viewport to size of shadow map
|
||||
[mRenderEncoder setViewport: (MTLViewport){ 0.0, 0.0, double(cShadowMapSize), double(cShadowMapSize), 0.0, 1.0 }];
|
||||
|
||||
// Set pixel shader constants
|
||||
[mRenderEncoder setFragmentBytes: &mPSBuffer length: sizeof(mPSBuffer) atIndex: 0];
|
||||
|
||||
// Counter clockwise is default winding order
|
||||
[mRenderEncoder setFrontFacingWinding: MTLWindingCounterClockwise];
|
||||
|
||||
// Start with projection mode
|
||||
SetProjectionMode();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RendererMTL::EndShadowPass()
|
||||
{
|
||||
// Finish the shadow encoder
|
||||
[mRenderEncoder endEncoding];
|
||||
mRenderEncoder = nil;
|
||||
|
||||
// Get the descriptor for the main window
|
||||
MTLRenderPassDescriptor *render_pass_descriptor = mView.currentRenderPassDescriptor;
|
||||
if (render_pass_descriptor == nullptr)
|
||||
return;
|
||||
|
||||
// Create render encoder
|
||||
mRenderEncoder = [mCommandBuffer renderCommandEncoderWithDescriptor: render_pass_descriptor];
|
||||
|
||||
// Set viewport
|
||||
[mRenderEncoder setViewport: (MTLViewport){ 0.0, 0.0, double(mWindow->GetWindowWidth()), double(mWindow->GetWindowHeight()), 0.0, 1.0 }];
|
||||
|
||||
// Set pixel shader constants
|
||||
[mRenderEncoder setFragmentBytes: &mPSBuffer length: sizeof(mPSBuffer) atIndex: 0];
|
||||
|
||||
// Counter clockwise is default winding order
|
||||
[mRenderEncoder setFrontFacingWinding: MTLWindingCounterClockwise];
|
||||
|
||||
// Start with projection mode
|
||||
SetProjectionMode();
|
||||
}
|
||||
|
||||
void RendererMTL::EndFrame()
|
||||
{
|
||||
JPH_PROFILE_FUNCTION();
|
||||
|
||||
// Finish the encoder
|
||||
[mRenderEncoder endEncoding];
|
||||
mRenderEncoder = nil;
|
||||
|
||||
// Schedule a present
|
||||
[mCommandBuffer presentDrawable: mView.currentDrawable];
|
||||
|
||||
// Commit the command buffer
|
||||
[mCommandBuffer commit];
|
||||
|
||||
Renderer::EndFrame();
|
||||
}
|
||||
|
||||
void RendererMTL::SetProjectionMode()
|
||||
{
|
||||
JPH_ASSERT(mInFrame);
|
||||
|
||||
[mRenderEncoder setVertexBytes: &mVSBuffer length: sizeof(mVSBuffer) atIndex: 2];
|
||||
}
|
||||
|
||||
void RendererMTL::SetOrthoMode()
|
||||
{
|
||||
JPH_ASSERT(mInFrame);
|
||||
|
||||
[mRenderEncoder setVertexBytes: &mVSBufferOrtho length: sizeof(mVSBufferOrtho) atIndex: 2];
|
||||
}
|
||||
|
||||
Ref<Texture> RendererMTL::CreateTexture(const Surface *inSurface)
|
||||
{
|
||||
return new TextureMTL(this, inSurface);
|
||||
}
|
||||
|
||||
Ref<VertexShader> RendererMTL::CreateVertexShader(const char *inName)
|
||||
{
|
||||
id<MTLFunction> function = [mShaderLibrary newFunctionWithName: [NSString stringWithCString: inName encoding: NSUTF8StringEncoding]];
|
||||
if (function == nil)
|
||||
FatalError("Vertex shader %s not found", inName);
|
||||
return new VertexShaderMTL(function);
|
||||
}
|
||||
|
||||
Ref<PixelShader> RendererMTL::CreatePixelShader(const char *inName)
|
||||
{
|
||||
id<MTLFunction> function = [mShaderLibrary newFunctionWithName: [NSString stringWithCString: inName encoding: NSUTF8StringEncoding]];
|
||||
if (function == nil)
|
||||
FatalError("Pixel shader %s not found", inName);
|
||||
return new PixelShaderMTL(function);
|
||||
}
|
||||
|
||||
unique_ptr<PipelineState> RendererMTL::CreatePipelineState(const VertexShader *inVertexShader, const PipelineState::EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShader *inPixelShader, PipelineState::EDrawPass inDrawPass, PipelineState::EFillMode inFillMode, PipelineState::ETopology inTopology, PipelineState::EDepthTest inDepthTest, PipelineState::EBlendMode inBlendMode, PipelineState::ECullMode inCullMode)
|
||||
{
|
||||
return make_unique<PipelineStateMTL>(this, static_cast<const VertexShaderMTL *>(inVertexShader), inInputDescription, inInputDescriptionCount, static_cast<const PixelShaderMTL *>(inPixelShader), inDrawPass, inFillMode, inTopology, inDepthTest, inBlendMode, inCullMode);
|
||||
}
|
||||
|
||||
RenderPrimitive *RendererMTL::CreateRenderPrimitive(PipelineState::ETopology inType)
|
||||
{
|
||||
return new RenderPrimitiveMTL(this, inType == PipelineState::ETopology::Line? MTLPrimitiveTypeLine : MTLPrimitiveTypeTriangle);
|
||||
}
|
||||
|
||||
RenderInstances *RendererMTL::CreateRenderInstances()
|
||||
{
|
||||
return new RenderInstancesMTL(this);
|
||||
}
|
||||
|
||||
#ifndef JPH_ENABLE_VULKAN
|
||||
Renderer *Renderer::sCreate()
|
||||
{
|
||||
return new RendererMTL;
|
||||
}
|
||||
#endif
|
||||
32
lib/All/JoltPhysics/TestFramework/Renderer/MTL/TextureMTL.h
Normal file
32
lib/All/JoltPhysics/TestFramework/Renderer/MTL/TextureMTL.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/Texture.h>
|
||||
|
||||
#include <MetalKit/MetalKit.h>
|
||||
|
||||
class RendererMTL;
|
||||
|
||||
/// Metal texture
|
||||
class TextureMTL : public Texture
|
||||
{
|
||||
public:
|
||||
/// Constructor, called by Renderer::CreateTextureMTL
|
||||
TextureMTL(RendererMTL *inRenderer, const Surface *inSurface); // Create a normal Texture
|
||||
TextureMTL(RendererMTL *inRenderer, int inWidth, int inHeight); // Create a render target (depth only)
|
||||
virtual ~TextureMTL() override;
|
||||
|
||||
/// Bind texture to the pixel shader
|
||||
virtual void Bind() const override;
|
||||
|
||||
/// Access to the metal texture
|
||||
id<MTLTexture> GetTexture() const { return mTexture; }
|
||||
|
||||
private:
|
||||
RendererMTL * mRenderer;
|
||||
id<MTLTexture> mTexture;
|
||||
};
|
||||
|
||||
98
lib/All/JoltPhysics/TestFramework/Renderer/MTL/TextureMTL.mm
Normal file
98
lib/All/JoltPhysics/TestFramework/Renderer/MTL/TextureMTL.mm
Normal file
@@ -0,0 +1,98 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/MTL/TextureMTL.h>
|
||||
#include <Renderer/MTL/RendererMTL.h>
|
||||
#include <Renderer/MTL/FatalErrorIfFailedMTL.h>
|
||||
#include <Image/BlitSurface.h>
|
||||
|
||||
TextureMTL::TextureMTL(RendererMTL *inRenderer, const Surface *inSurface) :
|
||||
Texture(inSurface->GetWidth(), inSurface->GetHeight()),
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
ESurfaceFormat format = inSurface->GetFormat();
|
||||
MTLPixelFormat mt_format = MTLPixelFormatBGRA8Unorm;
|
||||
switch (format)
|
||||
{
|
||||
case ESurfaceFormat::A4L4:
|
||||
case ESurfaceFormat::A8L8:
|
||||
case ESurfaceFormat::A4R4G4B4:
|
||||
case ESurfaceFormat::R8G8B8:
|
||||
case ESurfaceFormat::B8G8R8:
|
||||
case ESurfaceFormat::X8R8G8B8:
|
||||
case ESurfaceFormat::X8B8G8R8:
|
||||
case ESurfaceFormat::A8R8G8B8:
|
||||
case ESurfaceFormat::A8B8G8R8: mt_format = MTLPixelFormatBGRA8Unorm; format = ESurfaceFormat::A8R8G8B8; break;
|
||||
case ESurfaceFormat::L8: mt_format = MTLPixelFormatR8Unorm; break;
|
||||
case ESurfaceFormat::A8: mt_format = MTLPixelFormatA8Unorm; break;
|
||||
case ESurfaceFormat::R5G6B5:
|
||||
case ESurfaceFormat::X1R5G5B5:
|
||||
case ESurfaceFormat::X4R4G4B4: mt_format = MTLPixelFormatB5G6R5Unorm; format = ESurfaceFormat::R5G6B5; break;
|
||||
case ESurfaceFormat::A1R5G5B5: mt_format = MTLPixelFormatA1BGR5Unorm; break;
|
||||
case ESurfaceFormat::Invalid:
|
||||
default: JPH_ASSERT(false); break;
|
||||
}
|
||||
|
||||
// Blit the surface to another temporary surface if the format changed
|
||||
const Surface *surface = inSurface;
|
||||
Ref<Surface> tmp;
|
||||
if (format != inSurface->GetFormat())
|
||||
{
|
||||
tmp = new SoftwareSurface(mWidth, mHeight, format);
|
||||
BlitSurface(inSurface, tmp);
|
||||
surface = tmp;
|
||||
}
|
||||
|
||||
// Create descriptor
|
||||
MTLTextureDescriptor *descriptor = [[MTLTextureDescriptor alloc] init];
|
||||
descriptor.textureType = MTLTextureType2D;
|
||||
descriptor.usage = MTLTextureUsageShaderRead;
|
||||
descriptor.pixelFormat = mt_format;
|
||||
descriptor.width = mWidth;
|
||||
descriptor.height = mHeight;
|
||||
descriptor.storageMode = MTLStorageModeManaged;
|
||||
|
||||
MTLRegion region =
|
||||
{
|
||||
{ 0, 0, 0 },
|
||||
{ NSUInteger(mWidth), NSUInteger(mHeight), 1}
|
||||
};
|
||||
|
||||
// Create texture
|
||||
mTexture = [inRenderer->GetDevice() newTextureWithDescriptor: descriptor];
|
||||
surface->Lock(ESurfaceLockMode::Read);
|
||||
[mTexture replaceRegion: region mipmapLevel:0 withBytes: surface->GetData() bytesPerRow: surface->GetStride()];
|
||||
surface->UnLock();
|
||||
|
||||
[descriptor release];
|
||||
}
|
||||
|
||||
TextureMTL::TextureMTL(RendererMTL *inRenderer, int inWidth, int inHeight) :
|
||||
Texture(inWidth, inHeight),
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
MTLTextureDescriptor *descriptor = [[MTLTextureDescriptor alloc] init];
|
||||
descriptor.textureType = MTLTextureType2D;
|
||||
descriptor.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
|
||||
descriptor.pixelFormat = MTLPixelFormatDepth32Float;
|
||||
descriptor.width = mWidth;
|
||||
descriptor.height = mHeight;
|
||||
descriptor.storageMode = MTLStorageModePrivate;
|
||||
|
||||
mTexture = [inRenderer->GetDevice() newTextureWithDescriptor: descriptor];
|
||||
|
||||
[descriptor release];
|
||||
}
|
||||
|
||||
TextureMTL::~TextureMTL()
|
||||
{
|
||||
[mTexture release];
|
||||
}
|
||||
|
||||
void TextureMTL::Bind() const
|
||||
{
|
||||
[mRenderer->GetRenderEncoder() setFragmentTexture: mTexture atIndex: 0];
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/VertexShader.h>
|
||||
|
||||
#include <MetalKit/MetalKit.h>
|
||||
|
||||
/// Vertex shader handle for Metal
|
||||
class VertexShaderMTL : public VertexShader
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
VertexShaderMTL(id<MTLFunction> inFunction) : mFunction(inFunction) { }
|
||||
virtual ~VertexShaderMTL() override { [mFunction release]; }
|
||||
|
||||
/// Access to the function
|
||||
id<MTLFunction> GetFunction() const { return mFunction; }
|
||||
|
||||
private:
|
||||
id<MTLFunction> mFunction;
|
||||
};
|
||||
70
lib/All/JoltPhysics/TestFramework/Renderer/PipelineState.h
Normal file
70
lib/All/JoltPhysics/TestFramework/Renderer/PipelineState.h
Normal file
@@ -0,0 +1,70 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
/// Defines how primitives should be rendered
|
||||
class PipelineState
|
||||
{
|
||||
public:
|
||||
/// Describes the input layout of the vertex shader
|
||||
enum class EInputDescription
|
||||
{
|
||||
Position, ///< 3 float position
|
||||
Color, ///< 4 uint8 color
|
||||
Normal, ///< 3 float normal
|
||||
TexCoord, ///< 2 float texture coordinate
|
||||
InstanceColor, ///< 4 uint8 per instance color
|
||||
InstanceTransform, ///< 4x4 float per instance transform
|
||||
InstanceInvTransform, ///< 4x4 float per instance inverse transform
|
||||
};
|
||||
|
||||
/// In which draw pass to use this pipeline state
|
||||
enum class EDrawPass
|
||||
{
|
||||
Shadow,
|
||||
Normal
|
||||
};
|
||||
|
||||
/// The type of topology to emit
|
||||
enum class ETopology
|
||||
{
|
||||
Triangle,
|
||||
Line
|
||||
};
|
||||
|
||||
/// Fill mode of the triangles
|
||||
enum class EFillMode
|
||||
{
|
||||
Solid,
|
||||
Wireframe
|
||||
};
|
||||
|
||||
/// If depth write / depth test is on
|
||||
enum class EDepthTest
|
||||
{
|
||||
Off,
|
||||
On
|
||||
};
|
||||
|
||||
/// How to blend the pixel from the shader in the back buffer
|
||||
enum class EBlendMode
|
||||
{
|
||||
Write,
|
||||
AlphaBlend,
|
||||
};
|
||||
|
||||
/// How to cull triangles
|
||||
enum class ECullMode
|
||||
{
|
||||
Backface,
|
||||
FrontFace,
|
||||
};
|
||||
|
||||
/// Destructor
|
||||
virtual ~PipelineState() = default;
|
||||
|
||||
/// Make this pipeline state active (any primitives rendered after this will use this state)
|
||||
virtual void Activate() = 0;
|
||||
};
|
||||
15
lib/All/JoltPhysics/TestFramework/Renderer/PixelShader.h
Normal file
15
lib/All/JoltPhysics/TestFramework/Renderer/PixelShader.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
|
||||
/// Pixel shader handle
|
||||
class PixelShader : public RefTarget<PixelShader>
|
||||
{
|
||||
public:
|
||||
/// Destructor
|
||||
virtual ~PixelShader() = default;
|
||||
};
|
||||
28
lib/All/JoltPhysics/TestFramework/Renderer/RenderInstances.h
Normal file
28
lib/All/JoltPhysics/TestFramework/Renderer/RenderInstances.h
Normal file
@@ -0,0 +1,28 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
|
||||
class RenderPrimitive;
|
||||
|
||||
/// Buffer that holds a list of instances (usually model transform etc.) for instance based rendering
|
||||
class RenderInstances : public RefTarget<RenderInstances>
|
||||
{
|
||||
public:
|
||||
/// Destructor
|
||||
virtual ~RenderInstances() = default;
|
||||
|
||||
/// Erase all instance data
|
||||
virtual void Clear() = 0;
|
||||
|
||||
/// Instance buffer management functions
|
||||
virtual void CreateBuffer(int inNumInstances, int inInstanceSize) = 0;
|
||||
virtual void * Lock() = 0;
|
||||
virtual void Unlock() = 0;
|
||||
|
||||
/// Draw the instances when context has been set by Renderer::BindShader
|
||||
virtual void Draw(RenderPrimitive *inPrimitive, int inStartInstance, int inNumInstances) const = 0;
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/RenderPrimitive.h>
|
||||
|
||||
void RenderPrimitive::ReleaseVertexBuffer()
|
||||
{
|
||||
mNumVtx = 0;
|
||||
mNumVtxToDraw = 0;
|
||||
mVtxSize = 0;
|
||||
}
|
||||
|
||||
void RenderPrimitive::ReleaseIndexBuffer()
|
||||
{
|
||||
mNumIdx = 0;
|
||||
mNumIdxToDraw = 0;
|
||||
}
|
||||
|
||||
void RenderPrimitive::Clear()
|
||||
{
|
||||
ReleaseVertexBuffer();
|
||||
ReleaseIndexBuffer();
|
||||
}
|
||||
|
||||
void RenderPrimitive::CreateVertexBuffer(int inNumVtx, int inVtxSize, const void *inData)
|
||||
{
|
||||
ReleaseVertexBuffer();
|
||||
|
||||
mNumVtx = inNumVtx;
|
||||
mNumVtxToDraw = inNumVtx;
|
||||
mVtxSize = inVtxSize;
|
||||
}
|
||||
|
||||
void RenderPrimitive::CreateIndexBuffer(int inNumIdx, const uint32 *inData)
|
||||
{
|
||||
ReleaseIndexBuffer();
|
||||
|
||||
mNumIdx = inNumIdx;
|
||||
mNumIdxToDraw = inNumIdx;
|
||||
}
|
||||
54
lib/All/JoltPhysics/TestFramework/Renderer/RenderPrimitive.h
Normal file
54
lib/All/JoltPhysics/TestFramework/Renderer/RenderPrimitive.h
Normal file
@@ -0,0 +1,54 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
|
||||
/// Simple wrapper around vertex and index buffers
|
||||
class RenderPrimitive : public RefTarget<RenderPrimitive>, public RefTargetVirtual
|
||||
{
|
||||
public:
|
||||
/// Destructor
|
||||
virtual ~RenderPrimitive() override = default;
|
||||
|
||||
/// Erase all primitive data
|
||||
void Clear();
|
||||
|
||||
/// Check if this primitive contains any data
|
||||
bool IsEmpty() const { return mNumVtx == 0 && mNumIdx == 0; }
|
||||
|
||||
/// Vertex buffer management functions
|
||||
virtual void CreateVertexBuffer(int inNumVtx, int inVtxSize, const void *inData = nullptr) = 0;
|
||||
virtual void ReleaseVertexBuffer();
|
||||
virtual void * LockVertexBuffer() = 0;
|
||||
virtual void UnlockVertexBuffer() = 0;
|
||||
int GetNumVtx() const { return mNumVtx; }
|
||||
int GetNumVtxToDraw() const { return mNumVtxToDraw; }
|
||||
void SetNumVtxToDraw(int inUsed) { mNumVtxToDraw = inUsed; }
|
||||
|
||||
/// Index buffer management functions
|
||||
virtual void CreateIndexBuffer(int inNumIdx, const uint32 *inData = nullptr) = 0;
|
||||
virtual void ReleaseIndexBuffer();
|
||||
virtual uint32 * LockIndexBuffer() = 0;
|
||||
virtual void UnlockIndexBuffer() = 0;
|
||||
int GetNumIdx() const { return mNumIdx; }
|
||||
int GetNumIdxToDraw() const { return mNumIdxToDraw; }
|
||||
void SetNumIdxToDraw(int inUsed) { mNumIdxToDraw = inUsed; }
|
||||
|
||||
/// Draw the primitive
|
||||
virtual void Draw() const = 0;
|
||||
|
||||
/// Implement RefTargetVirtual, so we can conveniently use this class as DebugRenderer::Batch
|
||||
virtual void AddRef() override { RefTarget<RenderPrimitive>::AddRef(); }
|
||||
virtual void Release() override { RefTarget<RenderPrimitive>::Release(); }
|
||||
|
||||
protected:
|
||||
int mNumVtx = 0;
|
||||
int mNumVtxToDraw = 0;
|
||||
int mVtxSize = 0;
|
||||
|
||||
int mNumIdx = 0;
|
||||
int mNumIdxToDraw = 0;
|
||||
};
|
||||
89
lib/All/JoltPhysics/TestFramework/Renderer/Renderer.cpp
Normal file
89
lib/All/JoltPhysics/TestFramework/Renderer/Renderer.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/Renderer.h>
|
||||
|
||||
Renderer::~Renderer()
|
||||
{
|
||||
if (mWindow != nullptr)
|
||||
mWindow->SetWindowResizeListener({});
|
||||
}
|
||||
|
||||
void Renderer::Initialize(ApplicationWindow *inWindow)
|
||||
{
|
||||
// Store window
|
||||
mWindow = inWindow;
|
||||
mWindow->SetWindowResizeListener([this]() { OnWindowResize(); });
|
||||
}
|
||||
|
||||
static Mat44 sPerspectiveInfiniteReverseZ(float inFovY, float inAspect, float inNear, float inYSign)
|
||||
{
|
||||
float height = 1.0f / Tan(0.5f * inFovY);
|
||||
float width = height / inAspect;
|
||||
|
||||
return Mat44(Vec4(width, 0.0f, 0.0f, 0.0f), Vec4(0.0f, inYSign * height, 0.0f, 0.0f), Vec4(0.0f, 0.0f, 0.0f, -1.0f), Vec4(0.0f, 0.0f, inNear, 0.0f));
|
||||
}
|
||||
|
||||
bool Renderer::BeginFrame(const CameraState &inCamera, float inWorldScale)
|
||||
{
|
||||
// Mark that we're in the frame
|
||||
JPH_ASSERT(!mInFrame);
|
||||
mInFrame = true;
|
||||
|
||||
// Store state
|
||||
mCameraState = inCamera;
|
||||
|
||||
// Light properties
|
||||
Vec3 light_pos = inWorldScale * Vec3(250, 250, 250);
|
||||
Vec3 light_tgt = Vec3::sZero();
|
||||
Vec3 light_up = Vec3(0, 1, 0);
|
||||
Vec3 light_fwd = (light_tgt - light_pos).Normalized();
|
||||
float light_fov = DegreesToRadians(20.0f);
|
||||
float light_near = 1.0f;
|
||||
|
||||
// Camera properties
|
||||
Vec3 cam_pos = Vec3(inCamera.mPos - mBaseOffset);
|
||||
float camera_fovy = inCamera.mFOVY;
|
||||
float camera_aspect = static_cast<float>(mWindow->GetWindowWidth()) / mWindow->GetWindowHeight();
|
||||
float camera_fovx = 2.0f * ATan(camera_aspect * Tan(0.5f * camera_fovy));
|
||||
float camera_near = 0.01f * inWorldScale;
|
||||
|
||||
// Calculate camera frustum
|
||||
mCameraFrustum = Frustum(cam_pos, inCamera.mForward, inCamera.mUp, camera_fovx, camera_fovy, camera_near);
|
||||
|
||||
// Calculate light frustum
|
||||
mLightFrustum = Frustum(light_pos, light_fwd, light_up, light_fov, light_fov, light_near);
|
||||
|
||||
// Camera projection and view
|
||||
mVSBuffer.mProjection = sPerspectiveInfiniteReverseZ(camera_fovy, camera_aspect, camera_near, mPerspectiveYSign);
|
||||
Vec3 tgt = cam_pos + inCamera.mForward;
|
||||
mVSBuffer.mView = Mat44::sLookAt(cam_pos, tgt, inCamera.mUp);
|
||||
|
||||
// Light projection and view
|
||||
mVSBuffer.mLightProjection = sPerspectiveInfiniteReverseZ(light_fov, 1.0f, light_near, mPerspectiveYSign);
|
||||
mVSBuffer.mLightView = Mat44::sLookAt(light_pos, light_tgt, light_up);
|
||||
|
||||
// Camera ortho projection and view
|
||||
mVSBufferOrtho.mProjection = Mat44(Vec4(2.0f / mWindow->GetWindowWidth(), 0.0f, 0.0f, 0.0f), Vec4(0.0f, -mPerspectiveYSign * 2.0f / mWindow->GetWindowHeight(), 0.0f, 0.0f), Vec4(0.0f, 0.0f, -1.0f, 0.0f), Vec4(-1.0f, mPerspectiveYSign * 1.0f, 0.0f, 1.0f));
|
||||
mVSBufferOrtho.mView = Mat44::sIdentity();
|
||||
|
||||
// Light projection and view are unused in ortho mode
|
||||
mVSBufferOrtho.mLightView = Mat44::sIdentity();
|
||||
mVSBufferOrtho.mLightProjection = Mat44::sIdentity();
|
||||
|
||||
// Set constants for pixel shader
|
||||
mPSBuffer.mCameraPos = Vec4(cam_pos, 0);
|
||||
mPSBuffer.mLightPos = Vec4(light_pos, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Renderer::EndFrame()
|
||||
{
|
||||
// Mark that we're no longer in the frame
|
||||
JPH_ASSERT(mInFrame);
|
||||
mInFrame = false;
|
||||
}
|
||||
126
lib/All/JoltPhysics/TestFramework/Renderer/Renderer.h
Normal file
126
lib/All/JoltPhysics/TestFramework/Renderer/Renderer.h
Normal file
@@ -0,0 +1,126 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Image/Surface.h>
|
||||
#include <Window/ApplicationWindow.h>
|
||||
#include <Renderer/Frustum.h>
|
||||
#include <Renderer/PipelineState.h>
|
||||
#include <Renderer/VertexShader.h>
|
||||
#include <Renderer/PixelShader.h>
|
||||
#include <Renderer/RenderPrimitive.h>
|
||||
#include <Renderer/RenderInstances.h>
|
||||
#include <memory>
|
||||
|
||||
// Forward declares
|
||||
class Texture;
|
||||
|
||||
/// Camera setup
|
||||
struct CameraState
|
||||
{
|
||||
CameraState() : mPos(RVec3::sZero()), mForward(0, 0, -1), mUp(0, 1, 0), mFOVY(DegreesToRadians(70.0f)) { }
|
||||
|
||||
RVec3 mPos; ///< Camera position
|
||||
Vec3 mForward; ///< Camera forward vector
|
||||
Vec3 mUp; ///< Camera up vector
|
||||
float mFOVY; ///< Field of view in radians in up direction
|
||||
};
|
||||
|
||||
/// Responsible for rendering primitives to the screen
|
||||
class Renderer
|
||||
{
|
||||
public:
|
||||
/// Destructor
|
||||
virtual ~Renderer();
|
||||
|
||||
/// Initialize renderer
|
||||
virtual void Initialize(ApplicationWindow *inWindow);
|
||||
|
||||
/// Start / end drawing a frame
|
||||
virtual bool BeginFrame(const CameraState &inCamera, float inWorldScale);
|
||||
virtual void EndShadowPass() = 0;
|
||||
virtual void EndFrame();
|
||||
|
||||
/// Switch between orthographic and 3D projection mode
|
||||
virtual void SetProjectionMode() = 0;
|
||||
virtual void SetOrthoMode() = 0;
|
||||
|
||||
/// Create texture from an image surface
|
||||
virtual Ref<Texture> CreateTexture(const Surface *inSurface) = 0;
|
||||
|
||||
/// Compile a vertex shader
|
||||
virtual Ref<VertexShader> CreateVertexShader(const char *inName) = 0;
|
||||
|
||||
/// Compile a pixel shader
|
||||
virtual Ref<PixelShader> CreatePixelShader(const char *inName) = 0;
|
||||
|
||||
/// Create pipeline state object that defines the complete state of how primitives should be rendered
|
||||
virtual unique_ptr<PipelineState> CreatePipelineState(const VertexShader *inVertexShader, const PipelineState::EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShader *inPixelShader, PipelineState::EDrawPass inDrawPass, PipelineState::EFillMode inFillMode, PipelineState::ETopology inTopology, PipelineState::EDepthTest inDepthTest, PipelineState::EBlendMode inBlendMode, PipelineState::ECullMode inCullMode) = 0;
|
||||
|
||||
/// Create a render primitive
|
||||
virtual RenderPrimitive * CreateRenderPrimitive(PipelineState::ETopology inType) = 0;
|
||||
|
||||
/// Create render instances object to allow drawing batches of objects
|
||||
virtual RenderInstances * CreateRenderInstances() = 0;
|
||||
|
||||
/// Get the shadow map texture
|
||||
virtual Texture * GetShadowMap() const = 0;
|
||||
|
||||
/// Get the camera state / frustum (only valid between BeginFrame() / EndFrame())
|
||||
const CameraState & GetCameraState() const { JPH_ASSERT(mInFrame); return mCameraState; }
|
||||
const Frustum & GetCameraFrustum() const { JPH_ASSERT(mInFrame); return mCameraFrustum; }
|
||||
|
||||
/// Offset relative to which the world is rendered, helps avoiding rendering artifacts at big distances
|
||||
RVec3 GetBaseOffset() const { return mBaseOffset; }
|
||||
void SetBaseOffset(RVec3 inOffset) { mBaseOffset = inOffset; }
|
||||
|
||||
/// Get the light frustum (only valid between BeginFrame() / EndFrame())
|
||||
const Frustum & GetLightFrustum() const { JPH_ASSERT(mInFrame); return mLightFrustum; }
|
||||
|
||||
/// How many frames our pipeline is
|
||||
inline static const uint cFrameCount = 2;
|
||||
|
||||
/// Size of the shadow map will be cShadowMapSize x cShadowMapSize pixels
|
||||
inline static const uint cShadowMapSize = 4096;
|
||||
|
||||
/// Which frame is currently rendering (to keep track of which buffers are free to overwrite)
|
||||
uint GetCurrentFrameIndex() const { JPH_ASSERT(mInFrame); return mFrameIndex; }
|
||||
|
||||
/// Get the window we're rendering to
|
||||
ApplicationWindow * GetWindow() const { return mWindow; }
|
||||
|
||||
/// Callback when the window resizes and the back buffer needs to be adjusted
|
||||
virtual void OnWindowResize() = 0;
|
||||
|
||||
/// Create a platform specific Renderer instance
|
||||
static Renderer * sCreate();
|
||||
|
||||
protected:
|
||||
struct VertexShaderConstantBuffer
|
||||
{
|
||||
Mat44 mView;
|
||||
Mat44 mProjection;
|
||||
Mat44 mLightView;
|
||||
Mat44 mLightProjection;
|
||||
};
|
||||
|
||||
struct PixelShaderConstantBuffer
|
||||
{
|
||||
Vec4 mCameraPos;
|
||||
Vec4 mLightPos;
|
||||
};
|
||||
|
||||
ApplicationWindow * mWindow = nullptr; ///< The window we're rendering to
|
||||
float mPerspectiveYSign = 1.0f; ///< Sign for the Y coordinate in the projection matrix (1 for DX, -1 for Vulkan)
|
||||
bool mInFrame = false; ///< If we're within a BeginFrame() / EndFrame() pair
|
||||
CameraState mCameraState;
|
||||
RVec3 mBaseOffset { RVec3::sZero() }; ///< Offset to subtract from the camera position to deal with large worlds
|
||||
Frustum mCameraFrustum;
|
||||
Frustum mLightFrustum;
|
||||
uint mFrameIndex = 0; ///< Current frame index (0 or 1)
|
||||
VertexShaderConstantBuffer mVSBuffer;
|
||||
VertexShaderConstantBuffer mVSBufferOrtho;
|
||||
PixelShaderConstantBuffer mPSBuffer;
|
||||
};
|
||||
29
lib/All/JoltPhysics/TestFramework/Renderer/Texture.h
Normal file
29
lib/All/JoltPhysics/TestFramework/Renderer/Texture.h
Normal file
@@ -0,0 +1,29 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
|
||||
/// Forward declares
|
||||
class Surface;
|
||||
|
||||
class Texture : public RefTarget<Texture>
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
Texture(int inWidth, int inHeight) : mWidth(inWidth), mHeight(inHeight) { }
|
||||
virtual ~Texture() = default;
|
||||
|
||||
/// Get dimensions of texture
|
||||
inline int GetWidth() const { return mWidth; }
|
||||
inline int GetHeight() const { return mHeight; }
|
||||
|
||||
/// Bind texture to the pixel shader
|
||||
virtual void Bind() const = 0;
|
||||
|
||||
protected:
|
||||
int mWidth;
|
||||
int mHeight;
|
||||
};
|
||||
21
lib/All/JoltPhysics/TestFramework/Renderer/VK/BufferVK.h
Normal file
21
lib/All/JoltPhysics/TestFramework/Renderer/VK/BufferVK.h
Normal file
@@ -0,0 +1,21 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
/// Simple wrapper class to manage a Vulkan buffer
|
||||
class BufferVK
|
||||
{
|
||||
public:
|
||||
VkBuffer mBuffer = VK_NULL_HANDLE;
|
||||
VkDeviceMemory mMemory = VK_NULL_HANDLE;
|
||||
VkDeviceSize mOffset = 0;
|
||||
VkDeviceSize mSize = 0;
|
||||
|
||||
VkBufferUsageFlags mUsage;
|
||||
VkMemoryPropertyFlags mProperties;
|
||||
VkDeviceSize mAllocatedSize;
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/VK/ConstantBufferVK.h>
|
||||
#include <Renderer/VK/RendererVK.h>
|
||||
#include <Renderer/VK/FatalErrorIfFailedVK.h>
|
||||
|
||||
ConstantBufferVK::ConstantBufferVK(RendererVK *inRenderer, VkDeviceSize inBufferSize) :
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
mRenderer->CreateBuffer(inBufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, mBuffer);
|
||||
}
|
||||
|
||||
ConstantBufferVK::~ConstantBufferVK()
|
||||
{
|
||||
mRenderer->FreeBuffer(mBuffer);
|
||||
}
|
||||
|
||||
void *ConstantBufferVK::MapInternal()
|
||||
{
|
||||
void *data = nullptr;
|
||||
FatalErrorIfFailed(vkMapMemory(mRenderer->GetDevice(), mBuffer.mMemory, mBuffer.mOffset, mBuffer.mSize, 0, &data));
|
||||
return data;
|
||||
}
|
||||
|
||||
void ConstantBufferVK::Unmap()
|
||||
{
|
||||
vkUnmapMemory(mRenderer->GetDevice(), mBuffer.mMemory);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/VK/BufferVK.h>
|
||||
|
||||
class RendererVK;
|
||||
|
||||
/// A binary blob that can be used to pass constants to a shader
|
||||
class ConstantBufferVK
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
ConstantBufferVK(RendererVK *inRenderer, VkDeviceSize inBufferSize);
|
||||
~ConstantBufferVK();
|
||||
|
||||
/// Map / unmap buffer (get pointer to data). This will discard all data in the buffer.
|
||||
template <typename T> T * Map() { return reinterpret_cast<T *>(MapInternal()); }
|
||||
void Unmap();
|
||||
|
||||
VkBuffer GetBuffer() const { return mBuffer.mBuffer; }
|
||||
|
||||
private:
|
||||
void * MapInternal();
|
||||
|
||||
RendererVK * mRenderer;
|
||||
BufferVK mBuffer;
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/VK/FatalErrorIfFailedVK.h>
|
||||
#include <Utils/Log.h>
|
||||
|
||||
void FatalErrorIfFailed(VkResult inVkResult)
|
||||
{
|
||||
if (inVkResult != VK_SUCCESS)
|
||||
FatalError("Vulkan error returned: %d", inVkResult);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
/// Convert Vulkan error to readable text and alert
|
||||
void FatalErrorIfFailed(VkResult inVkResult);
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/VK/PipelineStateVK.h>
|
||||
#include <Renderer/VK/RendererVK.h>
|
||||
#include <Renderer/VK/FatalErrorIfFailedVK.h>
|
||||
|
||||
PipelineStateVK::PipelineStateVK(RendererVK *inRenderer, const VertexShaderVK *inVertexShader, const EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShaderVK *inPixelShader, EDrawPass inDrawPass, EFillMode inFillMode, ETopology inTopology, EDepthTest inDepthTest, EBlendMode inBlendMode, ECullMode inCullMode) :
|
||||
mRenderer(inRenderer),
|
||||
mVertexShader(inVertexShader),
|
||||
mPixelShader(inPixelShader)
|
||||
{
|
||||
VkPipelineShaderStageCreateInfo shader_stages[] = { inVertexShader->mStageInfo, inPixelShader->mStageInfo };
|
||||
|
||||
// TODO: This doesn't follow the SPIR-V alignment rules
|
||||
Array<VkVertexInputAttributeDescription> attribute_descriptions;
|
||||
VkVertexInputAttributeDescription temp_vtx = { }, temp_instance = { };
|
||||
temp_instance.binding = 1;
|
||||
uint instance_alignment = 1;
|
||||
for (uint i = 0; i < inInputDescriptionCount; ++i)
|
||||
switch (inInputDescription[i])
|
||||
{
|
||||
case EInputDescription::Position:
|
||||
case EInputDescription::Normal:
|
||||
temp_vtx.format = VK_FORMAT_R32G32B32_SFLOAT;
|
||||
attribute_descriptions.push_back(temp_vtx);
|
||||
temp_vtx.offset += 3 * sizeof(float);
|
||||
break;
|
||||
|
||||
case EInputDescription::Color:
|
||||
temp_vtx.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
attribute_descriptions.push_back(temp_vtx);
|
||||
temp_vtx.offset += 4 * sizeof(uint8);
|
||||
break;
|
||||
|
||||
case EInputDescription::TexCoord:
|
||||
temp_vtx.format = VK_FORMAT_R32G32_SFLOAT;
|
||||
attribute_descriptions.push_back(temp_vtx);
|
||||
temp_vtx.offset += 2 * sizeof(float);
|
||||
break;
|
||||
|
||||
case EInputDescription::InstanceColor:
|
||||
instance_alignment = max(instance_alignment, 4u);
|
||||
temp_instance.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
attribute_descriptions.push_back(temp_instance);
|
||||
temp_instance.offset += 4 * sizeof(uint8);
|
||||
break;
|
||||
|
||||
case EInputDescription::InstanceTransform:
|
||||
case EInputDescription::InstanceInvTransform:
|
||||
instance_alignment = max(instance_alignment, 16u);
|
||||
temp_instance.format = VK_FORMAT_R32G32B32A32_SFLOAT;
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
attribute_descriptions.push_back(temp_instance);
|
||||
temp_instance.offset += 4 * sizeof(float);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < uint32(attribute_descriptions.size()); ++i)
|
||||
attribute_descriptions[i].location = i;
|
||||
|
||||
VkVertexInputBindingDescription binding_description[2];
|
||||
binding_description[0].binding = 0;
|
||||
binding_description[0].stride = temp_vtx.offset;
|
||||
binding_description[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
binding_description[1].binding = 1;
|
||||
binding_description[1].stride = AlignUp(temp_instance.offset, instance_alignment);
|
||||
binding_description[1].inputRate = VK_VERTEX_INPUT_RATE_INSTANCE;
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertex_input_info = {};
|
||||
vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||
vertex_input_info.vertexBindingDescriptionCount = temp_instance.offset > 0? 2 : 1;
|
||||
vertex_input_info.pVertexBindingDescriptions = binding_description;
|
||||
vertex_input_info.vertexAttributeDescriptionCount = uint32(attribute_descriptions.size());
|
||||
vertex_input_info.pVertexAttributeDescriptions = attribute_descriptions.data();
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo input_assembly = {};
|
||||
input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||
input_assembly.topology = inTopology == ETopology::Triangle? VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST : VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
|
||||
input_assembly.primitiveRestartEnable = VK_FALSE;
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewport_state = {};
|
||||
viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||
viewport_state.viewportCount = 1;
|
||||
viewport_state.scissorCount = 1;
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rasterizer = {};
|
||||
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||
rasterizer.depthClampEnable = VK_FALSE;
|
||||
rasterizer.rasterizerDiscardEnable = VK_FALSE;
|
||||
rasterizer.polygonMode = inFillMode == EFillMode::Solid? VK_POLYGON_MODE_FILL : VK_POLYGON_MODE_LINE;
|
||||
rasterizer.lineWidth = 1.0f;
|
||||
rasterizer.cullMode = inCullMode == ECullMode::Backface? VK_CULL_MODE_BACK_BIT : VK_CULL_MODE_FRONT_BIT;
|
||||
rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||
rasterizer.depthBiasEnable = VK_FALSE;
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo multisampling = {};
|
||||
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||
multisampling.sampleShadingEnable = VK_FALSE;
|
||||
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
VkPipelineDepthStencilStateCreateInfo depth_stencil = {};
|
||||
depth_stencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
||||
depth_stencil.depthTestEnable = inDepthTest == EDepthTest::On? VK_TRUE : VK_FALSE;
|
||||
depth_stencil.depthWriteEnable = inDepthTest == EDepthTest::On? VK_TRUE : VK_FALSE;
|
||||
depth_stencil.depthCompareOp = VK_COMPARE_OP_GREATER_OR_EQUAL; // Reverse-Z, greater is closer
|
||||
|
||||
VkPipelineColorBlendAttachmentState color_blend_attachment = {};
|
||||
color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||
switch (inBlendMode)
|
||||
{
|
||||
case EBlendMode::Write:
|
||||
color_blend_attachment.blendEnable = VK_FALSE;
|
||||
break;
|
||||
|
||||
case EBlendMode::AlphaBlend:
|
||||
color_blend_attachment.blendEnable = VK_TRUE;
|
||||
color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
color_blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
break;
|
||||
}
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo color_blending = {};
|
||||
color_blending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||
color_blending.logicOpEnable = VK_FALSE;
|
||||
color_blending.logicOp = VK_LOGIC_OP_COPY;
|
||||
color_blending.attachmentCount = 1;
|
||||
color_blending.pAttachments = &color_blend_attachment;
|
||||
|
||||
VkDynamicState dynamic_states[] = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR
|
||||
};
|
||||
VkPipelineDynamicStateCreateInfo dynamic_state = {};
|
||||
dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
||||
dynamic_state.dynamicStateCount = std::size(dynamic_states);
|
||||
dynamic_state.pDynamicStates = dynamic_states;
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipeline_info = {};
|
||||
pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
pipeline_info.stageCount = std::size(shader_stages);
|
||||
pipeline_info.pStages = shader_stages;
|
||||
pipeline_info.pVertexInputState = &vertex_input_info;
|
||||
pipeline_info.pInputAssemblyState = &input_assembly;
|
||||
pipeline_info.pViewportState = &viewport_state;
|
||||
pipeline_info.pRasterizationState = &rasterizer;
|
||||
pipeline_info.pMultisampleState = &multisampling;
|
||||
pipeline_info.pDepthStencilState = &depth_stencil;
|
||||
pipeline_info.pColorBlendState = &color_blending;
|
||||
pipeline_info.pDynamicState = &dynamic_state;
|
||||
pipeline_info.layout = mRenderer->GetPipelineLayout();
|
||||
pipeline_info.renderPass = inDrawPass == EDrawPass::Normal? mRenderer->GetRenderPass() : mRenderer->GetRenderPassShadow();
|
||||
FatalErrorIfFailed(vkCreateGraphicsPipelines(mRenderer->GetDevice(), VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &mGraphicsPipeline));
|
||||
}
|
||||
|
||||
PipelineStateVK::~PipelineStateVK()
|
||||
{
|
||||
vkDeviceWaitIdle(mRenderer->GetDevice());
|
||||
|
||||
vkDestroyPipeline(mRenderer->GetDevice(), mGraphicsPipeline, nullptr);
|
||||
}
|
||||
|
||||
void PipelineStateVK::Activate()
|
||||
{
|
||||
vkCmdBindPipeline(mRenderer->GetCommandBuffer(), VK_PIPELINE_BIND_POINT_GRAPHICS, mGraphicsPipeline);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/PipelineState.h>
|
||||
#include <Renderer/VK/VertexShaderVK.h>
|
||||
#include <Renderer/VK/PixelShaderVK.h>
|
||||
|
||||
class RendererVK;
|
||||
|
||||
/// Vulkan pipeline state object
|
||||
class PipelineStateVK : public PipelineState
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
PipelineStateVK(RendererVK *inRenderer, const VertexShaderVK *inVertexShader, const EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShaderVK *inPixelShader, EDrawPass inDrawPass, EFillMode inFillMode, ETopology inTopology, EDepthTest inDepthTest, EBlendMode inBlendMode, ECullMode inCullMode);
|
||||
virtual ~PipelineStateVK() override;
|
||||
|
||||
/// Make this pipeline state active (any primitives rendered after this will use this state)
|
||||
virtual void Activate() override;
|
||||
|
||||
private:
|
||||
RendererVK * mRenderer;
|
||||
RefConst<VertexShaderVK> mVertexShader;
|
||||
RefConst<PixelShaderVK> mPixelShader;
|
||||
|
||||
VkPipeline mGraphicsPipeline;
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/PixelShader.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
/// Pixel shader handle for Vulkan
|
||||
class PixelShaderVK : public PixelShader
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
PixelShaderVK(VkDevice inDevice, VkShaderModule inShaderModule) :
|
||||
mDevice(inDevice),
|
||||
mStageInfo()
|
||||
{
|
||||
mStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
mStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
mStageInfo.module = inShaderModule;
|
||||
mStageInfo.pName = "main";
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
virtual ~PixelShaderVK() override
|
||||
{
|
||||
vkDestroyShaderModule(mDevice, mStageInfo.module, nullptr);
|
||||
}
|
||||
|
||||
VkDevice mDevice;
|
||||
VkPipelineShaderStageCreateInfo mStageInfo;
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/VK/RenderInstancesVK.h>
|
||||
#include <Renderer/VK/RenderPrimitiveVK.h>
|
||||
#include <Renderer/VK/FatalErrorIfFailedVK.h>
|
||||
|
||||
void RenderInstancesVK::Clear()
|
||||
{
|
||||
mRenderer->FreeBuffer(mInstancesBuffer);
|
||||
}
|
||||
|
||||
void RenderInstancesVK::CreateBuffer(int inNumInstances, int inInstanceSize)
|
||||
{
|
||||
Clear();
|
||||
|
||||
mRenderer->CreateBuffer(VkDeviceSize(inNumInstances) * inInstanceSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, mInstancesBuffer);
|
||||
}
|
||||
|
||||
void *RenderInstancesVK::Lock()
|
||||
{
|
||||
void *data;
|
||||
FatalErrorIfFailed(vkMapMemory(mRenderer->GetDevice(), mInstancesBuffer.mMemory, mInstancesBuffer.mOffset, mInstancesBuffer.mSize, 0, &data));
|
||||
return data;
|
||||
}
|
||||
|
||||
void RenderInstancesVK::Unlock()
|
||||
{
|
||||
vkUnmapMemory(mRenderer->GetDevice(), mInstancesBuffer.mMemory);
|
||||
}
|
||||
|
||||
void RenderInstancesVK::Draw(RenderPrimitive *inPrimitive, int inStartInstance, int inNumInstances) const
|
||||
{
|
||||
if (inNumInstances <= 0)
|
||||
return;
|
||||
|
||||
VkCommandBuffer command_buffer = mRenderer->GetCommandBuffer();
|
||||
RenderPrimitiveVK *primitive = static_cast<RenderPrimitiveVK *>(inPrimitive);
|
||||
|
||||
VkBuffer buffers[] = { primitive->mVertexBuffer.mBuffer, mInstancesBuffer.mBuffer };
|
||||
VkDeviceSize offsets[] = { 0, 0 };
|
||||
vkCmdBindVertexBuffers(command_buffer, 0, 2, buffers, offsets);
|
||||
|
||||
if (primitive->mIndexBuffer.mBuffer == VK_NULL_HANDLE)
|
||||
{
|
||||
vkCmdDraw(command_buffer, primitive->mNumVtxToDraw, inNumInstances, 0, inStartInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
vkCmdBindIndexBuffer(command_buffer, primitive->mIndexBuffer.mBuffer, 0, VK_INDEX_TYPE_UINT32);
|
||||
|
||||
vkCmdDrawIndexed(command_buffer, primitive->mNumIdxToDraw, inNumInstances, 0, 0, inStartInstance);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/VK/RendererVK.h>
|
||||
#include <Renderer/RenderInstances.h>
|
||||
|
||||
class RenderPrimitive;
|
||||
|
||||
/// Vulkan implementation of a render instances object
|
||||
class RenderInstancesVK : public RenderInstances
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
RenderInstancesVK(RendererVK *inRenderer) : mRenderer(inRenderer) { }
|
||||
virtual ~RenderInstancesVK() override { Clear(); }
|
||||
|
||||
/// Erase all instance data
|
||||
virtual void Clear() override;
|
||||
|
||||
/// Instance buffer management functions
|
||||
virtual void CreateBuffer(int inNumInstances, int inInstanceSize) override;
|
||||
virtual void * Lock() override;
|
||||
virtual void Unlock() override;
|
||||
|
||||
/// Draw the instances when context has been set by Renderer::BindShader
|
||||
virtual void Draw(RenderPrimitive *inPrimitive, int inStartInstance, int inNumInstances) const override;
|
||||
|
||||
private:
|
||||
RendererVK * mRenderer;
|
||||
|
||||
BufferVK mInstancesBuffer;
|
||||
};
|
||||
@@ -0,0 +1,100 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/VK/RenderPrimitiveVK.h>
|
||||
#include <Renderer/VK/FatalErrorIfFailedVK.h>
|
||||
|
||||
void RenderPrimitiveVK::ReleaseVertexBuffer()
|
||||
{
|
||||
mRenderer->FreeBuffer(mVertexBuffer);
|
||||
mVertexBufferDeviceLocal = false;
|
||||
|
||||
RenderPrimitive::ReleaseVertexBuffer();
|
||||
}
|
||||
|
||||
void RenderPrimitiveVK::ReleaseIndexBuffer()
|
||||
{
|
||||
mRenderer->FreeBuffer(mIndexBuffer);
|
||||
mIndexBufferDeviceLocal = false;
|
||||
|
||||
RenderPrimitive::ReleaseIndexBuffer();
|
||||
}
|
||||
|
||||
void RenderPrimitiveVK::CreateVertexBuffer(int inNumVtx, int inVtxSize, const void *inData)
|
||||
{
|
||||
RenderPrimitive::CreateVertexBuffer(inNumVtx, inVtxSize, inData);
|
||||
|
||||
VkDeviceSize size = VkDeviceSize(inNumVtx) * inVtxSize;
|
||||
if (inData != nullptr)
|
||||
{
|
||||
mRenderer->CreateDeviceLocalBuffer(inData, size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, mVertexBuffer);
|
||||
mVertexBufferDeviceLocal = true;
|
||||
}
|
||||
else
|
||||
mRenderer->CreateBuffer(size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, mVertexBuffer);
|
||||
}
|
||||
|
||||
void *RenderPrimitiveVK::LockVertexBuffer()
|
||||
{
|
||||
JPH_ASSERT(!mVertexBufferDeviceLocal);
|
||||
|
||||
void *data;
|
||||
FatalErrorIfFailed(vkMapMemory(mRenderer->GetDevice(), mVertexBuffer.mMemory, mVertexBuffer.mOffset, VkDeviceSize(mNumVtx) * mVtxSize, 0, &data));
|
||||
return data;
|
||||
}
|
||||
|
||||
void RenderPrimitiveVK::UnlockVertexBuffer()
|
||||
{
|
||||
vkUnmapMemory(mRenderer->GetDevice(), mVertexBuffer.mMemory);
|
||||
}
|
||||
|
||||
void RenderPrimitiveVK::CreateIndexBuffer(int inNumIdx, const uint32 *inData)
|
||||
{
|
||||
RenderPrimitive::CreateIndexBuffer(inNumIdx, inData);
|
||||
|
||||
VkDeviceSize size = VkDeviceSize(inNumIdx) * sizeof(uint32);
|
||||
if (inData != nullptr)
|
||||
{
|
||||
mRenderer->CreateDeviceLocalBuffer(inData, size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, mIndexBuffer);
|
||||
mIndexBufferDeviceLocal = true;
|
||||
}
|
||||
else
|
||||
mRenderer->CreateBuffer(size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, mIndexBuffer);
|
||||
}
|
||||
|
||||
uint32 *RenderPrimitiveVK::LockIndexBuffer()
|
||||
{
|
||||
JPH_ASSERT(!mIndexBufferDeviceLocal);
|
||||
|
||||
void *data;
|
||||
vkMapMemory(mRenderer->GetDevice(), mIndexBuffer.mMemory, mIndexBuffer.mOffset, VkDeviceSize(mNumIdx) * sizeof(uint32), 0, &data);
|
||||
return reinterpret_cast<uint32 *>(data);
|
||||
}
|
||||
|
||||
void RenderPrimitiveVK::UnlockIndexBuffer()
|
||||
{
|
||||
vkUnmapMemory(mRenderer->GetDevice(), mIndexBuffer.mMemory);
|
||||
}
|
||||
|
||||
void RenderPrimitiveVK::Draw() const
|
||||
{
|
||||
VkCommandBuffer command_buffer = mRenderer->GetCommandBuffer();
|
||||
|
||||
VkBuffer vertex_buffers[] = { mVertexBuffer.mBuffer };
|
||||
VkDeviceSize offsets[] = { 0 };
|
||||
vkCmdBindVertexBuffers(command_buffer, 0, 1, vertex_buffers, offsets);
|
||||
|
||||
if (mIndexBuffer.mBuffer == VK_NULL_HANDLE)
|
||||
{
|
||||
vkCmdDraw(command_buffer, mNumVtxToDraw, 1, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
vkCmdBindIndexBuffer(command_buffer, mIndexBuffer.mBuffer, 0, VK_INDEX_TYPE_UINT32);
|
||||
|
||||
vkCmdDrawIndexed(command_buffer, mNumIdxToDraw, 1, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/RenderPrimitive.h>
|
||||
#include <Renderer/VK/RendererVK.h>
|
||||
#include <Renderer/VK/BufferVK.h>
|
||||
|
||||
/// Vulkan implementation of a render primitive
|
||||
class RenderPrimitiveVK : public RenderPrimitive
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
RenderPrimitiveVK(RendererVK *inRenderer) : mRenderer(inRenderer) { }
|
||||
virtual ~RenderPrimitiveVK() override { Clear(); }
|
||||
|
||||
/// Vertex buffer management functions
|
||||
virtual void CreateVertexBuffer(int inNumVtx, int inVtxSize, const void *inData = nullptr) override;
|
||||
virtual void ReleaseVertexBuffer() override;
|
||||
virtual void * LockVertexBuffer() override;
|
||||
virtual void UnlockVertexBuffer() override;
|
||||
|
||||
/// Index buffer management functions
|
||||
virtual void CreateIndexBuffer(int inNumIdx, const uint32 *inData = nullptr) override;
|
||||
virtual void ReleaseIndexBuffer() override;
|
||||
virtual uint32 * LockIndexBuffer() override;
|
||||
virtual void UnlockIndexBuffer() override;
|
||||
|
||||
/// Draw the primitive
|
||||
virtual void Draw() const override;
|
||||
|
||||
private:
|
||||
friend class RenderInstancesVK;
|
||||
|
||||
RendererVK * mRenderer;
|
||||
|
||||
BufferVK mVertexBuffer;
|
||||
bool mVertexBufferDeviceLocal = false;
|
||||
|
||||
BufferVK mIndexBuffer;
|
||||
bool mIndexBufferDeviceLocal = false;
|
||||
};
|
||||
1324
lib/All/JoltPhysics/TestFramework/Renderer/VK/RendererVK.cpp
Normal file
1324
lib/All/JoltPhysics/TestFramework/Renderer/VK/RendererVK.cpp
Normal file
File diff suppressed because it is too large
Load Diff
156
lib/All/JoltPhysics/TestFramework/Renderer/VK/RendererVK.h
Normal file
156
lib/All/JoltPhysics/TestFramework/Renderer/VK/RendererVK.h
Normal file
@@ -0,0 +1,156 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Renderer/VK/ConstantBufferVK.h>
|
||||
#include <Renderer/VK/TextureVK.h>
|
||||
#include <Jolt/Core/UnorderedMap.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
/// Vulkan renderer
|
||||
class RendererVK : public Renderer
|
||||
{
|
||||
public:
|
||||
/// Destructor
|
||||
virtual ~RendererVK() override;
|
||||
|
||||
// See: Renderer
|
||||
virtual void Initialize(ApplicationWindow *inWindow) override;
|
||||
virtual bool BeginFrame(const CameraState &inCamera, float inWorldScale) override;
|
||||
virtual void EndShadowPass() override;
|
||||
virtual void EndFrame() override;
|
||||
virtual void SetProjectionMode() override;
|
||||
virtual void SetOrthoMode() override;
|
||||
virtual Ref<Texture> CreateTexture(const Surface *inSurface) override;
|
||||
virtual Ref<VertexShader> CreateVertexShader(const char *inName) override;
|
||||
virtual Ref<PixelShader> CreatePixelShader(const char *inName) override;
|
||||
virtual unique_ptr<PipelineState> CreatePipelineState(const VertexShader *inVertexShader, const PipelineState::EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShader *inPixelShader, PipelineState::EDrawPass inDrawPass, PipelineState::EFillMode inFillMode, PipelineState::ETopology inTopology, PipelineState::EDepthTest inDepthTest, PipelineState::EBlendMode inBlendMode, PipelineState::ECullMode inCullMode) override;
|
||||
virtual RenderPrimitive * CreateRenderPrimitive(PipelineState::ETopology inType) override;
|
||||
virtual RenderInstances * CreateRenderInstances() override;
|
||||
virtual Texture * GetShadowMap() const override { return mShadowMap.GetPtr(); }
|
||||
virtual void OnWindowResize() override;
|
||||
|
||||
VkDevice GetDevice() const { return mDevice; }
|
||||
VkDescriptorPool GetDescriptorPool() const { return mDescriptorPool; }
|
||||
VkDescriptorSetLayout GetDescriptorSetLayoutTexture() const { return mDescriptorSetLayoutTexture; }
|
||||
VkSampler GetTextureSamplerRepeat() const { return mTextureSamplerRepeat; }
|
||||
VkSampler GetTextureSamplerShadow() const { return mTextureSamplerShadow; }
|
||||
VkRenderPass GetRenderPassShadow() const { return mRenderPassShadow; }
|
||||
VkRenderPass GetRenderPass() const { return mRenderPass; }
|
||||
VkPipelineLayout GetPipelineLayout() const { return mPipelineLayout; }
|
||||
VkCommandBuffer GetCommandBuffer() { JPH_ASSERT(mInFrame); return mCommandBuffers[mFrameIndex]; }
|
||||
VkCommandBuffer StartTempCommandBuffer();
|
||||
void EndTempCommandBuffer(VkCommandBuffer inCommandBuffer);
|
||||
void AllocateMemory(VkDeviceSize inSize, uint32 inMemoryTypeBits, VkMemoryPropertyFlags inProperties, VkDeviceMemory &outMemory);
|
||||
void FreeMemory(VkDeviceMemory inMemory, VkDeviceSize inSize);
|
||||
void CreateBuffer(VkDeviceSize inSize, VkBufferUsageFlags inUsage, VkMemoryPropertyFlags inProperties, BufferVK &outBuffer);
|
||||
void CopyBuffer(VkBuffer inSrc, VkBuffer inDst, VkDeviceSize inSize);
|
||||
void CreateDeviceLocalBuffer(const void *inData, VkDeviceSize inSize, VkBufferUsageFlags inUsage, BufferVK &outBuffer);
|
||||
void FreeBuffer(BufferVK &ioBuffer);
|
||||
unique_ptr<ConstantBufferVK> CreateConstantBuffer(VkDeviceSize inBufferSize);
|
||||
void CreateImage(uint32 inWidth, uint32 inHeight, VkFormat inFormat, VkImageTiling inTiling, VkImageUsageFlags inUsage, VkMemoryPropertyFlags inProperties, VkImage &outImage, VkDeviceMemory &outMemory);
|
||||
void DestroyImage(VkImage inImage, VkDeviceMemory inMemory);
|
||||
VkImageView CreateImageView(VkImage inImage, VkFormat inFormat, VkImageAspectFlags inAspectFlags);
|
||||
VkFormat FindDepthFormat();
|
||||
|
||||
private:
|
||||
uint32 FindMemoryType(uint32 inTypeFilter, VkMemoryPropertyFlags inProperties);
|
||||
void FreeBufferInternal(BufferVK &ioBuffer);
|
||||
VkSurfaceFormatKHR SelectFormat(VkPhysicalDevice inDevice);
|
||||
void CreateSwapChain(VkPhysicalDevice inDevice);
|
||||
void DestroySwapChain();
|
||||
void UpdateViewPortAndScissorRect(uint32 inWidth, uint32 inHeight);
|
||||
VkSemaphore AllocateSemaphore();
|
||||
void FreeSemaphore(VkSemaphore inSemaphore);
|
||||
|
||||
VkInstance mInstance = VK_NULL_HANDLE;
|
||||
#ifdef JPH_DEBUG
|
||||
VkDebugUtilsMessengerEXT mDebugMessenger = VK_NULL_HANDLE;
|
||||
#endif
|
||||
VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
|
||||
VkPhysicalDeviceMemoryProperties mMemoryProperties;
|
||||
VkDevice mDevice = VK_NULL_HANDLE;
|
||||
uint32 mGraphicsQueueIndex = 0;
|
||||
uint32 mPresentQueueIndex = 0;
|
||||
VkQueue mGraphicsQueue = VK_NULL_HANDLE;
|
||||
VkQueue mPresentQueue = VK_NULL_HANDLE;
|
||||
VkSurfaceKHR mSurface = VK_NULL_HANDLE;
|
||||
VkSwapchainKHR mSwapChain = VK_NULL_HANDLE;
|
||||
bool mSubOptimalSwapChain = false;
|
||||
Array<VkImage> mSwapChainImages;
|
||||
VkFormat mSwapChainImageFormat;
|
||||
VkExtent2D mSwapChainExtent;
|
||||
Array<VkImageView> mSwapChainImageViews;
|
||||
VkImage mDepthImage = VK_NULL_HANDLE;
|
||||
VkDeviceMemory mDepthImageMemory = VK_NULL_HANDLE;
|
||||
VkImageView mDepthImageView = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout mDescriptorSetLayoutUBO = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout mDescriptorSetLayoutTexture = VK_NULL_HANDLE;
|
||||
VkDescriptorPool mDescriptorPool = VK_NULL_HANDLE;
|
||||
VkDescriptorSet mDescriptorSets[cFrameCount];
|
||||
VkDescriptorSet mDescriptorSetsOrtho[cFrameCount];
|
||||
VkSampler mTextureSamplerShadow = VK_NULL_HANDLE;
|
||||
VkSampler mTextureSamplerRepeat = VK_NULL_HANDLE;
|
||||
VkRenderPass mRenderPassShadow = VK_NULL_HANDLE;
|
||||
VkRenderPass mRenderPass = VK_NULL_HANDLE;
|
||||
VkPipelineLayout mPipelineLayout = VK_NULL_HANDLE;
|
||||
VkFramebuffer mShadowFrameBuffer = VK_NULL_HANDLE;
|
||||
Array<VkFramebuffer> mSwapChainFramebuffers;
|
||||
uint32 mImageIndex = 0;
|
||||
VkCommandPool mCommandPool = VK_NULL_HANDLE;
|
||||
VkCommandBuffer mCommandBuffers[cFrameCount];
|
||||
Array<VkSemaphore> mAvailableSemaphores;
|
||||
Array<VkSemaphore> mImageAvailableSemaphores;
|
||||
Array<VkSemaphore> mRenderFinishedSemaphores;
|
||||
VkFence mInFlightFences[cFrameCount];
|
||||
Ref<TextureVK> mShadowMap;
|
||||
unique_ptr<ConstantBufferVK> mVertexShaderConstantBufferProjection[cFrameCount];
|
||||
unique_ptr<ConstantBufferVK> mVertexShaderConstantBufferOrtho[cFrameCount];
|
||||
unique_ptr<ConstantBufferVK> mPixelShaderConstantBuffer[cFrameCount];
|
||||
|
||||
struct Key
|
||||
{
|
||||
bool operator == (const Key &inRHS) const
|
||||
{
|
||||
return mSize == inRHS.mSize && mUsage == inRHS.mUsage && mProperties == inRHS.mProperties;
|
||||
}
|
||||
|
||||
VkDeviceSize mSize;
|
||||
VkBufferUsageFlags mUsage;
|
||||
VkMemoryPropertyFlags mProperties;
|
||||
};
|
||||
|
||||
JPH_MAKE_HASH_STRUCT(Key, KeyHasher, t.mSize, t.mUsage, t.mProperties)
|
||||
|
||||
// We try to recycle buffers from frame to frame
|
||||
using BufferCache = UnorderedMap<Key, Array<BufferVK>, KeyHasher>;
|
||||
|
||||
BufferCache mFreedBuffers[cFrameCount];
|
||||
BufferCache mBufferCache;
|
||||
|
||||
// Smaller allocations (from cMinAllocSize to cMaxAllocSize) will be done in blocks of cBlockSize bytes.
|
||||
// We do this because there is a limit to the number of allocations that we can make in Vulkan.
|
||||
static constexpr VkDeviceSize cMinAllocSize = 512;
|
||||
static constexpr VkDeviceSize cMaxAllocSize = 65536;
|
||||
static constexpr VkDeviceSize cBlockSize = 524288;
|
||||
|
||||
JPH_MAKE_HASH_STRUCT(Key, MemKeyHasher, t.mUsage, t.mProperties, t.mSize)
|
||||
|
||||
struct Memory
|
||||
{
|
||||
VkDeviceMemory mMemory;
|
||||
VkDeviceSize mOffset;
|
||||
};
|
||||
|
||||
using MemoryCache = UnorderedMap<Key, Array<Memory>, KeyHasher>;
|
||||
|
||||
MemoryCache mMemoryCache;
|
||||
uint32 mNumAllocations = 0;
|
||||
uint32 mMaxNumAllocations = 0;
|
||||
VkDeviceSize mTotalAllocated = 0;
|
||||
VkDeviceSize mMaxTotalAllocated = 0;
|
||||
};
|
||||
180
lib/All/JoltPhysics/TestFramework/Renderer/VK/TextureVK.cpp
Normal file
180
lib/All/JoltPhysics/TestFramework/Renderer/VK/TextureVK.cpp
Normal file
@@ -0,0 +1,180 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/VK/TextureVK.h>
|
||||
#include <Renderer/VK/RendererVK.h>
|
||||
#include <Renderer/VK/FatalErrorIfFailedVK.h>
|
||||
#include <Image/BlitSurface.h>
|
||||
|
||||
TextureVK::TextureVK(RendererVK *inRenderer, const Surface *inSurface) :
|
||||
Texture(inSurface->GetWidth(), inSurface->GetHeight()),
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
ESurfaceFormat format = inSurface->GetFormat();
|
||||
VkFormat vk_format = VK_FORMAT_B8G8R8A8_UNORM;
|
||||
switch (format)
|
||||
{
|
||||
case ESurfaceFormat::A4L4: vk_format = VK_FORMAT_R8G8_UNORM; format = ESurfaceFormat::A8L8; break;
|
||||
case ESurfaceFormat::L8: vk_format = VK_FORMAT_R8_UNORM; break;
|
||||
case ESurfaceFormat::A8: vk_format = VK_FORMAT_A8_UNORM_KHR; break;
|
||||
case ESurfaceFormat::A8L8: vk_format = VK_FORMAT_R8G8_UNORM; break;
|
||||
case ESurfaceFormat::R5G6B5: vk_format = VK_FORMAT_B5G6R5_UNORM_PACK16; break;
|
||||
case ESurfaceFormat::X1R5G5B5: vk_format = VK_FORMAT_B5G5R5A1_UNORM_PACK16; format = ESurfaceFormat::A1R5G5B5; break;
|
||||
case ESurfaceFormat::X4R4G4B4: vk_format = VK_FORMAT_B4G4R4A4_UNORM_PACK16; format = ESurfaceFormat::A4R4G4B4; break;
|
||||
case ESurfaceFormat::A1R5G5B5: vk_format = VK_FORMAT_B5G5R5A1_UNORM_PACK16; break;
|
||||
case ESurfaceFormat::A4R4G4B4: vk_format = VK_FORMAT_B4G4R4A4_UNORM_PACK16; break;
|
||||
case ESurfaceFormat::R8G8B8: vk_format = VK_FORMAT_B8G8R8_UNORM; break;
|
||||
case ESurfaceFormat::B8G8R8: vk_format = VK_FORMAT_B8G8R8_UNORM; break;
|
||||
case ESurfaceFormat::X8R8G8B8: vk_format = VK_FORMAT_B8G8R8A8_UNORM; format = ESurfaceFormat::A8R8G8B8; break;
|
||||
case ESurfaceFormat::X8B8G8R8: vk_format = VK_FORMAT_B8G8R8A8_UNORM; format = ESurfaceFormat::X8R8G8B8; break;
|
||||
case ESurfaceFormat::A8R8G8B8: vk_format = VK_FORMAT_B8G8R8A8_UNORM; break;
|
||||
case ESurfaceFormat::A8B8G8R8: vk_format = VK_FORMAT_B8G8R8A8_UNORM; format = ESurfaceFormat::A8R8G8B8; break;
|
||||
case ESurfaceFormat::Invalid:
|
||||
default: JPH_ASSERT(false); break;
|
||||
}
|
||||
|
||||
// Blit the surface to another temporary surface if the format changed
|
||||
const Surface *surface = inSurface;
|
||||
Ref<Surface> tmp;
|
||||
if (format != inSurface->GetFormat())
|
||||
{
|
||||
tmp = new SoftwareSurface(mWidth, mHeight, format);
|
||||
BlitSurface(inSurface, tmp);
|
||||
surface = tmp;
|
||||
}
|
||||
|
||||
int bpp = surface->GetBytesPerPixel();
|
||||
VkDeviceSize image_size = VkDeviceSize(mWidth) * mHeight * bpp;
|
||||
VkDevice device = mRenderer->GetDevice();
|
||||
|
||||
BufferVK staging_buffer;
|
||||
mRenderer->CreateBuffer(image_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, staging_buffer);
|
||||
|
||||
// Copy data to upload texture
|
||||
surface->Lock(ESurfaceLockMode::Read);
|
||||
void *data;
|
||||
vkMapMemory(device, staging_buffer.mMemory, staging_buffer.mOffset, image_size, 0, &data);
|
||||
for (int y = 0; y < mHeight; ++y)
|
||||
memcpy(reinterpret_cast<uint8 *>(data) + y * mWidth * bpp, surface->GetData() + y * surface->GetStride(), mWidth * bpp);
|
||||
vkUnmapMemory(device, staging_buffer.mMemory);
|
||||
surface->UnLock();
|
||||
|
||||
// Create destination image
|
||||
mRenderer->CreateImage(mWidth, mHeight, vk_format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mImage, mImageMemory);
|
||||
|
||||
VkCommandBuffer command_buffer = mRenderer->StartTempCommandBuffer();
|
||||
|
||||
// Make the image suitable for transferring to
|
||||
TransitionImageLayout(command_buffer, mImage, vk_format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
|
||||
// Copy the data to the destination image
|
||||
VkBufferImageCopy region = {};
|
||||
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
region.imageSubresource.layerCount = 1;
|
||||
region.imageExtent = { uint32(mWidth), uint32(mHeight), 1 };
|
||||
vkCmdCopyBufferToImage(command_buffer, staging_buffer.mBuffer, mImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
||||
|
||||
// Make the image suitable for sampling
|
||||
TransitionImageLayout(command_buffer, mImage, vk_format, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
mRenderer->EndTempCommandBuffer(command_buffer);
|
||||
|
||||
// Destroy temporary buffer
|
||||
mRenderer->FreeBuffer(staging_buffer);
|
||||
|
||||
CreateImageViewAndDescriptorSet(vk_format, VK_IMAGE_ASPECT_COLOR_BIT, mRenderer->GetTextureSamplerRepeat());
|
||||
}
|
||||
|
||||
TextureVK::TextureVK(RendererVK *inRenderer, int inWidth, int inHeight) :
|
||||
Texture(inWidth, inHeight),
|
||||
mRenderer(inRenderer)
|
||||
{
|
||||
VkFormat vk_format = mRenderer->FindDepthFormat();
|
||||
|
||||
// Create render target
|
||||
mRenderer->CreateImage(mWidth, mHeight, vk_format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mImage, mImageMemory);
|
||||
|
||||
CreateImageViewAndDescriptorSet(vk_format, VK_IMAGE_ASPECT_DEPTH_BIT, mRenderer->GetTextureSamplerShadow());
|
||||
}
|
||||
|
||||
TextureVK::~TextureVK()
|
||||
{
|
||||
if (mImage != VK_NULL_HANDLE)
|
||||
{
|
||||
VkDevice device = mRenderer->GetDevice();
|
||||
|
||||
vkDeviceWaitIdle(device);
|
||||
|
||||
vkDestroyImageView(device, mImageView, nullptr);
|
||||
|
||||
mRenderer->DestroyImage(mImage, mImageMemory);
|
||||
}
|
||||
}
|
||||
|
||||
void TextureVK::Bind() const
|
||||
{
|
||||
if (mDescriptorSet != VK_NULL_HANDLE)
|
||||
vkCmdBindDescriptorSets(mRenderer->GetCommandBuffer(), VK_PIPELINE_BIND_POINT_GRAPHICS, mRenderer->GetPipelineLayout(), 1, 1, &mDescriptorSet, 0, nullptr);
|
||||
}
|
||||
|
||||
void TextureVK::CreateImageViewAndDescriptorSet(VkFormat inFormat, VkImageAspectFlags inAspectFlags, VkSampler inSampler)
|
||||
{
|
||||
VkDevice device = mRenderer->GetDevice();
|
||||
|
||||
// Create image view
|
||||
mImageView = mRenderer->CreateImageView(mImage, inFormat, inAspectFlags);
|
||||
|
||||
// Allocate descriptor set for binding the texture
|
||||
VkDescriptorSetLayout layout = mRenderer->GetDescriptorSetLayoutTexture();
|
||||
VkDescriptorSetAllocateInfo descriptor_set_alloc_info = {};
|
||||
descriptor_set_alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
descriptor_set_alloc_info.descriptorPool = mRenderer->GetDescriptorPool();
|
||||
descriptor_set_alloc_info.descriptorSetCount = 1;
|
||||
descriptor_set_alloc_info.pSetLayouts = &layout;
|
||||
FatalErrorIfFailed(vkAllocateDescriptorSets(device, &descriptor_set_alloc_info, &mDescriptorSet));
|
||||
|
||||
VkDescriptorImageInfo image_info = {};
|
||||
image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
image_info.imageView = mImageView;
|
||||
image_info.sampler = inSampler;
|
||||
|
||||
VkWriteDescriptorSet descriptor_write = {};
|
||||
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
descriptor_write.dstSet = mDescriptorSet;
|
||||
descriptor_write.dstBinding = 0;
|
||||
descriptor_write.dstArrayElement = 0;
|
||||
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
descriptor_write.descriptorCount = 1;
|
||||
descriptor_write.pImageInfo = &image_info;
|
||||
vkUpdateDescriptorSets(device, 1, &descriptor_write, 0, nullptr);
|
||||
}
|
||||
|
||||
void TextureVK::TransitionImageLayout(VkCommandBuffer inCommandBuffer, VkImage inImage, VkFormat inFormat, VkImageLayout inOldLayout, VkImageLayout inNewLayout)
|
||||
{
|
||||
VkImageMemoryBarrier barrier = {};
|
||||
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
barrier.oldLayout = inOldLayout;
|
||||
barrier.newLayout = inNewLayout;
|
||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.image = inImage;
|
||||
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
barrier.subresourceRange.levelCount = 1;
|
||||
barrier.subresourceRange.layerCount = 1;
|
||||
|
||||
if (inOldLayout == VK_IMAGE_LAYOUT_UNDEFINED && inNewLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
|
||||
{
|
||||
barrier.srcAccessMask = 0;
|
||||
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
vkCmdPipelineBarrier(inCommandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
}
|
||||
else if (inOldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && inNewLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
|
||||
{
|
||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
vkCmdPipelineBarrier(inCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
}
|
||||
}
|
||||
35
lib/All/JoltPhysics/TestFramework/Renderer/VK/TextureVK.h
Normal file
35
lib/All/JoltPhysics/TestFramework/Renderer/VK/TextureVK.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/Texture.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
class RendererVK;
|
||||
|
||||
class TextureVK : public Texture
|
||||
{
|
||||
public:
|
||||
/// Constructor, called by Renderer::CreateTextureVK
|
||||
TextureVK(RendererVK *inRenderer, const Surface *inSurface); // Create a normal TextureVK
|
||||
TextureVK(RendererVK *inRenderer, int inWidth, int inHeight); // Create a render target (depth only)
|
||||
virtual ~TextureVK() override;
|
||||
|
||||
/// Bind texture to the pixel shader
|
||||
virtual void Bind() const override;
|
||||
|
||||
VkImageView GetImageView() const { return mImageView; }
|
||||
|
||||
private:
|
||||
void CreateImageViewAndDescriptorSet(VkFormat inFormat, VkImageAspectFlags inAspectFlags, VkSampler inSampler);
|
||||
void TransitionImageLayout(VkCommandBuffer inCommandBuffer, VkImage inImage, VkFormat inFormat, VkImageLayout inOldLayout, VkImageLayout inNewLayout);
|
||||
|
||||
RendererVK * mRenderer;
|
||||
VkImage mImage = VK_NULL_HANDLE;
|
||||
VkDeviceMemory mImageMemory = VK_NULL_HANDLE;
|
||||
VkImageView mImageView = VK_NULL_HANDLE;
|
||||
VkDescriptorSet mDescriptorSet = VK_NULL_HANDLE;
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Renderer/VertexShader.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
/// Vertex shader handle for Vulkan
|
||||
class VertexShaderVK : public VertexShader
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
VertexShaderVK(VkDevice inDevice, VkShaderModule inShaderModule) :
|
||||
mDevice(inDevice),
|
||||
mStageInfo()
|
||||
{
|
||||
mStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
mStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
mStageInfo.module = inShaderModule;
|
||||
mStageInfo.pName = "main";
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
virtual ~VertexShaderVK() override
|
||||
{
|
||||
vkDestroyShaderModule(mDevice, mStageInfo.module, nullptr);
|
||||
}
|
||||
|
||||
VkDevice mDevice;
|
||||
VkPipelineShaderStageCreateInfo mStageInfo;
|
||||
};
|
||||
15
lib/All/JoltPhysics/TestFramework/Renderer/VertexShader.h
Normal file
15
lib/All/JoltPhysics/TestFramework/Renderer/VertexShader.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/Reference.h>
|
||||
|
||||
/// Vertex shader handle
|
||||
class VertexShader : public RefTarget<VertexShader>
|
||||
{
|
||||
public:
|
||||
/// Destructor
|
||||
virtual ~VertexShader() = default;
|
||||
};
|
||||
333
lib/All/JoltPhysics/TestFramework/TestFramework.cmake
Normal file
333
lib/All/JoltPhysics/TestFramework/TestFramework.cmake
Normal file
@@ -0,0 +1,333 @@
|
||||
# Find Vulkan
|
||||
find_package(Vulkan)
|
||||
if (NOT CROSS_COMPILE_ARM AND (Vulkan_FOUND OR WIN32 OR ("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")))
|
||||
# We have Vulkan/DirectX so we can compile TestFramework
|
||||
set(TEST_FRAMEWORK_AVAILABLE TRUE)
|
||||
|
||||
# Root
|
||||
set(TEST_FRAMEWORK_ROOT ${PHYSICS_REPO_ROOT}/TestFramework)
|
||||
|
||||
# Source files
|
||||
set(TEST_FRAMEWORK_SRC_FILES
|
||||
${TEST_FRAMEWORK_ROOT}/Application/Application.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Application/Application.h
|
||||
${TEST_FRAMEWORK_ROOT}/Application/DebugUI.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Application/DebugUI.h
|
||||
${TEST_FRAMEWORK_ROOT}/Application/EntryPoint.h
|
||||
${TEST_FRAMEWORK_ROOT}/External/Perlin.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/External/Perlin.h
|
||||
${TEST_FRAMEWORK_ROOT}/External/stb_truetype.h
|
||||
${TEST_FRAMEWORK_ROOT}/Image/BlitSurface.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Image/BlitSurface.h
|
||||
${TEST_FRAMEWORK_ROOT}/Image/LoadBMP.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Image/LoadBMP.h
|
||||
${TEST_FRAMEWORK_ROOT}/Image/LoadTGA.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Image/LoadTGA.h
|
||||
${TEST_FRAMEWORK_ROOT}/Image/Surface.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Image/Surface.h
|
||||
${TEST_FRAMEWORK_ROOT}/Image/ZoomImage.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Image/ZoomImage.h
|
||||
${TEST_FRAMEWORK_ROOT}/Input/Keyboard.h
|
||||
${TEST_FRAMEWORK_ROOT}/Input/Mouse.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/DebugRendererImp.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/DebugRendererImp.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/Font.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/Font.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/Frustum.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/PipelineState.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/PixelShader.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/Renderer.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/Renderer.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/RenderInstances.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/RenderPrimitive.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/RenderPrimitive.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/Texture.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/VertexShader.h
|
||||
${TEST_FRAMEWORK_ROOT}/TestFramework.cmake
|
||||
${TEST_FRAMEWORK_ROOT}/TestFramework.h
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIAnimation.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIAnimation.h
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIAnimationSlide.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIAnimationSlide.h
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIButton.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIButton.h
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UICheckBox.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UICheckBox.h
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIComboBox.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIComboBox.h
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIElement.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIElement.h
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIEventListener.h
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIHorizontalStack.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIHorizontalStack.h
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIImage.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIImage.h
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIManager.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIManager.h
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UISlider.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UISlider.h
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIStaticText.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIStaticText.h
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UITextButton.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UITextButton.h
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UITexturedQuad.h
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIVerticalStack.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/UI/UIVerticalStack.h
|
||||
${TEST_FRAMEWORK_ROOT}/Utils/CustomMemoryHook.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Utils/CustomMemoryHook.h
|
||||
${TEST_FRAMEWORK_ROOT}/Utils/AssetStream.h
|
||||
${TEST_FRAMEWORK_ROOT}/Utils/Log.h
|
||||
${TEST_FRAMEWORK_ROOT}/Utils/ReadData.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Utils/ReadData.h
|
||||
${TEST_FRAMEWORK_ROOT}/Window/ApplicationWindow.h
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
# Windows source files
|
||||
set(TEST_FRAMEWORK_SRC_FILES
|
||||
${TEST_FRAMEWORK_SRC_FILES}
|
||||
${TEST_FRAMEWORK_ROOT}/Input/Win/KeyboardWin.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Input/Win/KeyboardWin.h
|
||||
${TEST_FRAMEWORK_ROOT}/Input/Win/MouseWin.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Input/Win/MouseWin.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/DX12/ConstantBufferDX12.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/DX12/ConstantBufferDX12.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/DX12/CommandQueueDX12.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/DX12/DescriptorHeapDX12.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/DX12/FatalErrorIfFailedDX12.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/DX12/FatalErrorIfFailedDX12.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/DX12/PipelineStateDX12.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/DX12/PipelineStateDX12.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/DX12/PixelShaderDX12.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/DX12/RendererDX12.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/DX12/RendererDX12.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/DX12/RenderInstancesDX12.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/DX12/RenderInstancesDX12.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/DX12/RenderPrimitiveDX12.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/DX12/RenderPrimitiveDX12.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/DX12/TextureDX12.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/DX12/TextureDX12.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/DX12/VertexShaderDX12.h
|
||||
${TEST_FRAMEWORK_ROOT}/Utils/AssetStream.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Utils/Log.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Window/ApplicationWindowWin.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Window/ApplicationWindowWin.h
|
||||
)
|
||||
|
||||
# HLSL vertex shaders
|
||||
set(TEST_FRAMEWORK_SRC_FILES_SHADERS
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/DX/VertexConstants.h
|
||||
)
|
||||
set(TEST_FRAMEWORK_HLSL_VERTEX_SHADERS
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/DX/FontVertexShader.hlsl
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/DX/LineVertexShader.hlsl
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/DX/TriangleDepthVertexShader.hlsl
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/DX/TriangleVertexShader.hlsl
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/DX/UIVertexShader.hlsl
|
||||
)
|
||||
set(TEST_FRAMEWORK_SRC_FILES_SHADERS ${TEST_FRAMEWORK_SRC_FILES_SHADERS} ${TEST_FRAMEWORK_HLSL_VERTEX_SHADERS})
|
||||
set_source_files_properties(${TEST_FRAMEWORK_HLSL_VERTEX_SHADERS} PROPERTIES VS_SHADER_FLAGS "/WX /T vs_5_0")
|
||||
|
||||
# HLSL pixel shaders
|
||||
set(TEST_FRAMEWORK_HLSL_PIXEL_SHADERS
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/DX/FontPixelShader.hlsl
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/DX/LinePixelShader.hlsl
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/DX/TriangleDepthPixelShader.hlsl
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/DX/TrianglePixelShader.hlsl
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/DX/UIPixelShader.hlsl
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/DX/UIPixelShaderUntextured.hlsl
|
||||
)
|
||||
set(TEST_FRAMEWORK_SRC_FILES_SHADERS ${TEST_FRAMEWORK_SRC_FILES_SHADERS} ${TEST_FRAMEWORK_HLSL_PIXEL_SHADERS})
|
||||
set_source_files_properties(${TEST_FRAMEWORK_HLSL_PIXEL_SHADERS} PROPERTIES VS_SHADER_FLAGS "/WX /T ps_5_0")
|
||||
endif()
|
||||
|
||||
if (LINUX)
|
||||
# Linux source files
|
||||
set(TEST_FRAMEWORK_SRC_FILES
|
||||
${TEST_FRAMEWORK_SRC_FILES}
|
||||
${TEST_FRAMEWORK_ROOT}/Input/Linux/KeyboardLinux.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Input/Linux/KeyboardLinux.h
|
||||
${TEST_FRAMEWORK_ROOT}/Input/Linux/MouseLinux.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Input/Linux/MouseLinux.h
|
||||
${TEST_FRAMEWORK_ROOT}/Utils/AssetStream.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Utils/Log.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Window/ApplicationWindowLinux.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Window/ApplicationWindowLinux.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if ("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
|
||||
# macOS source files
|
||||
set(TEST_FRAMEWORK_SRC_FILES
|
||||
${TEST_FRAMEWORK_SRC_FILES}
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/MTL/FatalErrorIfFailedMTL.mm
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/MTL/FatalErrorIfFailedMTL.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/MTL/PipelineStateMTL.mm
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/MTL/PipelineStateMTL.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/MTL/PixelShaderMTL.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/MTL/RendererMTL.mm
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/MTL/RendererMTL.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/MTL/RenderInstancesMTL.mm
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/MTL/RenderInstancesMTL.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/MTL/RenderPrimitiveMTL.mm
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/MTL/RenderPrimitiveMTL.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/MTL/TextureMTL.mm
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/MTL/TextureMTL.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/MTL/VertexShaderMTL.h
|
||||
${TEST_FRAMEWORK_ROOT}/Input/MacOS/KeyboardMacOS.mm
|
||||
${TEST_FRAMEWORK_ROOT}/Input/MacOS/KeyboardMacOS.h
|
||||
${TEST_FRAMEWORK_ROOT}/Input/MacOS/MouseMacOS.mm
|
||||
${TEST_FRAMEWORK_ROOT}/Input/MacOS/MouseMacOS.h
|
||||
${TEST_FRAMEWORK_ROOT}/Utils/AssetStream.mm
|
||||
${TEST_FRAMEWORK_ROOT}/Utils/Log.mm
|
||||
${TEST_FRAMEWORK_ROOT}/Window/ApplicationWindowMacOS.mm
|
||||
${TEST_FRAMEWORK_ROOT}/Window/ApplicationWindowMacOS.h
|
||||
)
|
||||
|
||||
# Metal shaders
|
||||
set(TEST_FRAMEWORK_SRC_FILES_SHADERS
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/MTL/VertexConstants.h
|
||||
)
|
||||
set(TEST_FRAMEWORK_METAL_SHADERS
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/MTL/FontShader.metal
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/MTL/LineShader.metal
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/MTL/TriangleShader.metal
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/MTL/UIShader.metal
|
||||
)
|
||||
|
||||
# Compile Metal shaders
|
||||
foreach(SHADER ${TEST_FRAMEWORK_METAL_SHADERS})
|
||||
cmake_path(GET SHADER FILENAME AIR_SHADER)
|
||||
set(AIR_SHADER "${CMAKE_CURRENT_BINARY_DIR}/${AIR_SHADER}.air")
|
||||
add_custom_command(OUTPUT ${AIR_SHADER}
|
||||
COMMAND xcrun -sdk macosx metal -c ${SHADER} -o ${AIR_SHADER}
|
||||
DEPENDS ${SHADER}
|
||||
COMMENT "Compiling ${SHADER}")
|
||||
list(APPEND TEST_FRAMEWORK_AIR_SHADERS ${AIR_SHADER})
|
||||
endforeach()
|
||||
|
||||
# Link Metal shaders
|
||||
set(TEST_FRAMEWORK_METAL_LIB ${PHYSICS_REPO_ROOT}/Assets/Shaders/MTL/Shaders.metallib)
|
||||
add_custom_command(OUTPUT ${TEST_FRAMEWORK_METAL_LIB}
|
||||
COMMAND xcrun -sdk macosx metallib -o ${TEST_FRAMEWORK_METAL_LIB} ${TEST_FRAMEWORK_AIR_SHADERS}
|
||||
DEPENDS ${TEST_FRAMEWORK_AIR_SHADERS}
|
||||
COMMENT "Linking shaders")
|
||||
endif()
|
||||
|
||||
# Include the Vulkan library
|
||||
if (Vulkan_FOUND)
|
||||
# Vulkan source files
|
||||
set(TEST_FRAMEWORK_SRC_FILES
|
||||
${TEST_FRAMEWORK_SRC_FILES}
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/VK/BufferVK.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/VK/ConstantBufferVK.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/VK/ConstantBufferVK.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/VK/FatalErrorIfFailedVK.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/VK/FatalErrorIfFailedVK.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/VK/PipelineStateVK.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/VK/PipelineStateVK.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/VK/PixelShaderVK.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/VK/RendererVK.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/VK/RendererVK.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/VK/RenderInstancesVK.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/VK/RenderInstancesVK.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/VK/RenderPrimitiveVK.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/VK/RenderPrimitiveVK.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/VK/TextureVK.cpp
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/VK/TextureVK.h
|
||||
${TEST_FRAMEWORK_ROOT}/Renderer/VK/VertexShaderVK.h
|
||||
)
|
||||
|
||||
# GLSL headers
|
||||
set(TEST_FRAMEWORK_SRC_FILES_SHADERS
|
||||
${TEST_FRAMEWORK_SRC_FILES_SHADERS}
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/VK/VertexConstants.h
|
||||
)
|
||||
set(TEST_FRAMEWORK_GLSL_SHADERS
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/VK/FontVertexShader.vert
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/VK/LineVertexShader.vert
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/VK/TriangleDepthVertexShader.vert
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/VK/TriangleVertexShader.vert
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/VK/UIVertexShader.vert
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/VK/FontPixelShader.frag
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/VK/LinePixelShader.frag
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/VK/TriangleDepthPixelShader.frag
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/VK/TrianglePixelShader.frag
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/VK/UIPixelShader.frag
|
||||
${PHYSICS_REPO_ROOT}/Assets/Shaders/VK/UIPixelShaderUntextured.frag
|
||||
)
|
||||
|
||||
# Compile GLSL shaders
|
||||
foreach(SHADER ${TEST_FRAMEWORK_GLSL_SHADERS})
|
||||
set(SPV_SHADER ${SHADER}.spv)
|
||||
add_custom_command(OUTPUT ${SPV_SHADER}
|
||||
COMMAND ${Vulkan_GLSLC_EXECUTABLE} ${SHADER} -o ${SPV_SHADER}
|
||||
DEPENDS ${SHADER}
|
||||
COMMENT "Compiling ${SHADER}")
|
||||
list(APPEND TEST_FRAMEWORK_SPV_SHADERS ${SPV_SHADER})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# Assets used by the test framework
|
||||
set(TEST_FRAMEWORK_ASSETS
|
||||
${PHYSICS_REPO_ROOT}/Assets/Fonts/Roboto-Regular.ttf
|
||||
${PHYSICS_REPO_ROOT}/Assets/UI.tga
|
||||
${TEST_FRAMEWORK_SRC_FILES_SHADERS}
|
||||
${TEST_FRAMEWORK_HLSL_VERTEX_SHADERS}
|
||||
${TEST_FRAMEWORK_HLSL_PIXEL_SHADERS}
|
||||
${TEST_FRAMEWORK_SPV_SHADERS}
|
||||
${TEST_FRAMEWORK_METAL_LIB}
|
||||
)
|
||||
|
||||
# Group source files
|
||||
source_group(TREE ${TEST_FRAMEWORK_ROOT} FILES ${TEST_FRAMEWORK_SRC_FILES})
|
||||
|
||||
# Group shader files
|
||||
source_group(TREE ${PHYSICS_REPO_ROOT} FILES ${TEST_FRAMEWORK_SRC_FILES_SHADERS} ${TEST_FRAMEWORK_GLSL_SHADERS} ${TEST_FRAMEWORK_METAL_SHADERS})
|
||||
|
||||
# Group intermediate files
|
||||
source_group(Intermediate FILES ${TEST_FRAMEWORK_SPV_SHADERS} ${TEST_FRAMEWORK_METAL_LIB})
|
||||
|
||||
# Create TestFramework lib
|
||||
add_library(TestFramework STATIC ${TEST_FRAMEWORK_SRC_FILES} ${TEST_FRAMEWORK_SRC_FILES_SHADERS} ${TEST_FRAMEWORK_GLSL_SHADERS} ${TEST_FRAMEWORK_SPV_SHADERS} ${TEST_FRAMEWORK_METAL_SHADERS} ${TEST_FRAMEWORK_METAL_LIB})
|
||||
target_include_directories(TestFramework PUBLIC ${TEST_FRAMEWORK_ROOT})
|
||||
target_precompile_headers(TestFramework PUBLIC ${TEST_FRAMEWORK_ROOT}/TestFramework.h)
|
||||
|
||||
if (Vulkan_FOUND)
|
||||
# Vulkan configuration
|
||||
target_include_directories(TestFramework PUBLIC ${Vulkan_INCLUDE_DIRS})
|
||||
target_link_libraries(TestFramework LINK_PUBLIC Jolt ${Vulkan_LIBRARIES})
|
||||
if (JPH_ENABLE_VULKAN)
|
||||
target_compile_definitions(TestFramework PRIVATE JPH_ENABLE_VULKAN)
|
||||
endif()
|
||||
endif()
|
||||
if (WIN32)
|
||||
# Windows configuration
|
||||
target_link_libraries(TestFramework LINK_PUBLIC Jolt dxguid.lib dinput8.lib dxgi.lib d3d12.lib d3dcompiler.lib shcore.lib)
|
||||
endif()
|
||||
if (LINUX)
|
||||
# Linux configuration
|
||||
target_link_libraries(TestFramework LINK_PUBLIC Jolt X11)
|
||||
endif()
|
||||
if ("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
|
||||
# macOS configuration
|
||||
target_link_libraries(TestFramework LINK_PUBLIC Jolt "-framework Cocoa -framework Metal -framework MetalKit -framework GameController")
|
||||
|
||||
# Make sure that all test framework assets move to the Resources folder in the package
|
||||
foreach(ASSET_FILE ${TEST_FRAMEWORK_ASSETS})
|
||||
string(REPLACE ${PHYSICS_REPO_ROOT}/Assets "Resources" ASSET_DST ${ASSET_FILE})
|
||||
get_filename_component(ASSET_DST ${ASSET_DST} DIRECTORY)
|
||||
set_source_files_properties(${ASSET_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION ${ASSET_DST})
|
||||
endforeach()
|
||||
|
||||
# Ignore PCH files for .mm files
|
||||
foreach(SRC_FILE ${TEST_FRAMEWORK_SRC_FILES})
|
||||
if (SRC_FILE MATCHES "\.mm")
|
||||
set_source_files_properties(${SRC_FILE} PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
else()
|
||||
# No graphics framework found
|
||||
set(TEST_FRAMEWORK_AVAILABLE FALSE)
|
||||
endif()
|
||||
62
lib/All/JoltPhysics/TestFramework/TestFramework.h
Normal file
62
lib/All/JoltPhysics/TestFramework/TestFramework.h
Normal file
@@ -0,0 +1,62 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Jolt.h>
|
||||
|
||||
// Disable common warnings
|
||||
JPH_SUPPRESS_WARNINGS
|
||||
JPH_CLANG_SUPPRESS_WARNING("-Wheader-hygiene")
|
||||
#ifdef JPH_DOUBLE_PRECISION
|
||||
JPH_CLANG_SUPPRESS_WARNING("-Wdouble-promotion")
|
||||
#endif // JPH_DOUBLE_PRECISION
|
||||
JPH_CLANG_SUPPRESS_WARNING("-Wswitch-enum")
|
||||
JPH_CLANG_SUPPRESS_WARNING("-Wswitch")
|
||||
JPH_MSVC_SUPPRESS_WARNING(4061) // enumerator 'X' in switch of enum 'X' is not explicitly handled by a case label
|
||||
JPH_MSVC_SUPPRESS_WARNING(4062) // enumerator 'X' in switch of enum 'X' is not handled
|
||||
|
||||
#ifdef JPH_PLATFORM_WINDOWS
|
||||
|
||||
// Targeting Windows 10 and above
|
||||
#define WINVER 0x0A00
|
||||
#define _WIN32_WINNT 0x0A00
|
||||
|
||||
JPH_SUPPRESS_WARNING_PUSH
|
||||
JPH_MSVC_SUPPRESS_WARNING(5039) // winbase.h(13179): warning C5039: 'TpSetCallbackCleanupGroup': pointer or reference to potentially throwing function passed to 'extern "C"' function under -EHc. Undefined behavior may occur if this function throws an exception.
|
||||
JPH_MSVC_SUPPRESS_WARNING(5204) // implements.h(65): warning C5204: 'Microsoft::WRL::CloakedIid<IMarshal>': class has virtual functions, but its trivial destructor is not virtual; instances of objects derived from this class may not be destructed correctly
|
||||
JPH_MSVC_SUPPRESS_WARNING(4265) // implements.h(1449): warning C4265: 'Microsoft::WRL::FtmBase': class has virtual functions, but its non-trivial destructor is not virtual; instances of this class may not be destructed correctly
|
||||
JPH_MSVC_SUPPRESS_WARNING(5220) // implements.h(1648): warning C5220: 'Microsoft::WRL::Details::RuntimeClassImpl<Microsoft::WRL::RuntimeClassFlags<2>,true,false,true,IWeakReference>::refcount_': a non-static data member with a volatile qualified type no longer implies
|
||||
JPH_MSVC_SUPPRESS_WARNING(4986) // implements.h(2343): warning C4986: 'Microsoft::WRL::Details::RuntimeClassImpl<RuntimeClassFlagsT,true,true,false,I0,TInterfaces...>::GetWeakReference': exception specification does not match previous declaration
|
||||
JPH_MSVC2026_PLUS_SUPPRESS_WARNING(4865) // wingdi.h(2806,1): '<unnamed-enum-DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER>': the underlying type will change from 'int' to '__int64' when '/Zc:enumTypes' is specified on the command line
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define Ellipse DrawEllipse // Windows.h defines a name that we would like to use
|
||||
#include <windows.h>
|
||||
#undef Ellipse
|
||||
#undef min // We'd like to use std::min and max instead of the ones defined in windows.h
|
||||
#undef max
|
||||
#undef DrawText // We don't want this to map to DrawTextW
|
||||
#include <d3d12.h>
|
||||
#include <dxgi1_6.h>
|
||||
#include <wrl.h> // for ComPtr
|
||||
JPH_SUPPRESS_WARNING_POP
|
||||
|
||||
using Microsoft::WRL::ComPtr;
|
||||
|
||||
#elif defined(JPH_PLATFORM_LINUX)
|
||||
|
||||
#define Font X11Font
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/XKBlib.h>
|
||||
#undef Font
|
||||
#undef Success
|
||||
#undef None
|
||||
#undef Convex
|
||||
|
||||
#endif
|
||||
|
||||
using namespace JPH;
|
||||
using namespace JPH::literals;
|
||||
using namespace std;
|
||||
11
lib/All/JoltPhysics/TestFramework/UI/UIAnimation.cpp
Normal file
11
lib/All/JoltPhysics/TestFramework/UI/UIAnimation.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <UI/UIAnimation.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL_BASE(UIAnimation)
|
||||
{
|
||||
}
|
||||
26
lib/All/JoltPhysics/TestFramework/UI/UIAnimation.h
Normal file
26
lib/All/JoltPhysics/TestFramework/UI/UIAnimation.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/RTTI.h>
|
||||
|
||||
class UIElement;
|
||||
|
||||
/// Base class for UI element animations
|
||||
class UIAnimation
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL_BASE(JPH_NO_EXPORT, UIAnimation)
|
||||
|
||||
/// Destructor
|
||||
virtual ~UIAnimation() = default;
|
||||
|
||||
///@name Interface
|
||||
virtual void Init(UIElement *inElement) { }
|
||||
virtual bool Update(UIElement *inElement, float inDeltaTime) { return true; } ///< Returns false when done
|
||||
virtual void Exit(UIElement *inElement) { }
|
||||
};
|
||||
|
||||
using UIAnimationVector = Array<UIAnimation *>;
|
||||
82
lib/All/JoltPhysics/TestFramework/UI/UIAnimationSlide.cpp
Normal file
82
lib/All/JoltPhysics/TestFramework/UI/UIAnimationSlide.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Renderer/Renderer.h>
|
||||
#include <Window/ApplicationWindow.h>
|
||||
#include <UI/UIAnimationSlide.h>
|
||||
#include <UI/UIElement.h>
|
||||
#include <UI/UIManager.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_ABSTRACT(UIAnimationSlide)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(UIAnimationSlide, UIAnimation)
|
||||
}
|
||||
|
||||
UIAnimationSlide::UIAnimationSlide(EMode inMode, int inSlideDistanceH, int inSlideDistanceV, float inTimeBeforeSlide, float inSlideTime) :
|
||||
mSlideMode(inMode),
|
||||
mSlideDistanceH(inSlideDistanceH),
|
||||
mSlideDistanceV(inSlideDistanceV),
|
||||
mTimeBeforeSlide(inTimeBeforeSlide),
|
||||
mSlideTime(inSlideTime)
|
||||
{
|
||||
}
|
||||
|
||||
void UIAnimationSlide::Init(UIElement *inElement)
|
||||
{
|
||||
mTargetRelativeX = inElement->GetRelativeX();
|
||||
mTargetRelativeY = inElement->GetRelativeY();
|
||||
|
||||
ApplicationWindow *window = inElement->GetManager()->GetRenderer()->GetWindow();
|
||||
int dl = inElement->GetX();
|
||||
int dr = window->GetWindowWidth() - (inElement->GetX() + inElement->GetWidth());
|
||||
int dt = inElement->GetY();
|
||||
int db = window->GetWindowHeight() - (inElement->GetY() + inElement->GetHeight());
|
||||
|
||||
if (min(dl, dr) < min(dt, db))
|
||||
{
|
||||
mInitialRelativeX = mTargetRelativeX + (dl < dr? -mSlideDistanceH : mSlideDistanceH);
|
||||
mInitialRelativeY = mTargetRelativeY;
|
||||
}
|
||||
else
|
||||
{
|
||||
mInitialRelativeX = mTargetRelativeX;
|
||||
mInitialRelativeY = mTargetRelativeY + (dt < db? -mSlideDistanceV : mSlideDistanceV);
|
||||
}
|
||||
|
||||
if (mSlideMode == SLIDE_ON_SCREEN)
|
||||
inElement->SetAnimatedVisible(true);
|
||||
|
||||
mTime = 0.0f;
|
||||
}
|
||||
|
||||
bool UIAnimationSlide::Update(UIElement *inElement, float inDeltaTime)
|
||||
{
|
||||
mTime += inDeltaTime;
|
||||
|
||||
float factor = (mTime - mTimeBeforeSlide) / mSlideTime;
|
||||
if (factor >= 1.0f)
|
||||
return false;
|
||||
if (factor < 0.0f)
|
||||
factor = 0.0f;
|
||||
|
||||
if (mSlideMode == SLIDE_OFF_SCREEN)
|
||||
factor = 1.0f - factor;
|
||||
|
||||
float x = mInitialRelativeX * (1.0f - factor) + mTargetRelativeX * factor;
|
||||
float y = mInitialRelativeY * (1.0f - factor) + mTargetRelativeY * factor;
|
||||
|
||||
inElement->SetRelativeX((int)x);
|
||||
inElement->SetRelativeY((int)y);
|
||||
return true;
|
||||
}
|
||||
|
||||
void UIAnimationSlide::Exit(UIElement *inElement)
|
||||
{
|
||||
inElement->SetRelativeX(mTargetRelativeX);
|
||||
inElement->SetRelativeY(mTargetRelativeY);
|
||||
|
||||
inElement->SetAnimatedVisible(mSlideMode == SLIDE_ON_SCREEN);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user